1 // Copyright (C) 2007-2020 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
49 #include "utilities.h"
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_ListOfInteger.hxx>
66 #include <TopAbs_State.hxx>
68 #include <TopExp_Explorer.hxx>
69 #include <TopTools_ListIteratorOfListOfShape.hxx>
70 #include <TopTools_ListOfShape.hxx>
71 #include <TopTools_SequenceOfShape.hxx>
73 #include <TopoDS_Edge.hxx>
74 #include <TopoDS_Face.hxx>
75 #include <TopoDS_Solid.hxx>
81 #include <gp_Trsf.hxx>
95 #include <boost/tuple/tuple.hpp>
96 #include <boost/container/flat_set.hpp>
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
101 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
103 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
106 using namespace SMESH::Controls;
108 //=======================================================================
109 //function : SMESH_MeshEditor
111 //=======================================================================
113 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
114 :myMesh( theMesh ) // theMesh may be NULL
118 //================================================================================
120 * \brief Return mesh DS
122 //================================================================================
124 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
126 return myMesh->GetMeshDS();
130 //================================================================================
132 * \brief Clears myLastCreatedNodes and myLastCreatedElems
134 //================================================================================
136 void SMESH_MeshEditor::ClearLastCreated()
138 SMESHUtils::FreeVector( myLastCreatedElems );
139 SMESHUtils::FreeVector( myLastCreatedNodes );
142 //================================================================================
144 * \brief Initializes members by an existing element
145 * \param [in] elem - the source element
146 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
148 //================================================================================
150 SMESH_MeshEditor::ElemFeatures&
151 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
155 myType = elem->GetType();
156 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
158 myIsPoly = elem->IsPoly();
161 myIsQuad = elem->IsQuadratic();
162 if ( myType == SMDSAbs_Volume && !basicOnly )
164 myPolyhedQuantities = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
168 else if ( myType == SMDSAbs_Ball && !basicOnly )
170 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
176 //=======================================================================
180 //=======================================================================
183 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
184 const ElemFeatures& features)
186 SMDS_MeshElement* e = 0;
187 int nbnode = node.size();
188 SMESHDS_Mesh* mesh = GetMeshDS();
189 const int ID = features.myID;
191 switch ( features.myType ) {
193 if ( !features.myIsPoly ) {
195 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
196 else e = mesh->AddFace (node[0], node[1], node[2] );
198 else if (nbnode == 4) {
199 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
200 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
202 else if (nbnode == 6) {
203 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
204 node[4], node[5], ID);
205 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
208 else if (nbnode == 7) {
209 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
210 node[4], node[5], node[6], ID);
211 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
212 node[4], node[5], node[6] );
214 else if (nbnode == 8) {
215 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
216 node[4], node[5], node[6], node[7], ID);
217 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
218 node[4], node[5], node[6], node[7] );
220 else if (nbnode == 9) {
221 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
222 node[4], node[5], node[6], node[7], node[8], ID);
223 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
224 node[4], node[5], node[6], node[7], node[8] );
227 else if ( !features.myIsQuad )
229 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
230 else e = mesh->AddPolygonalFace (node );
232 else if ( nbnode % 2 == 0 ) // just a protection
234 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
235 else e = mesh->AddQuadPolygonalFace (node );
240 if ( !features.myIsPoly ) {
242 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
243 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
245 else if (nbnode == 5) {
246 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
248 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
251 else if (nbnode == 6) {
252 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
253 node[4], node[5], ID);
254 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
257 else if (nbnode == 8) {
258 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
259 node[4], node[5], node[6], node[7], ID);
260 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
261 node[4], node[5], node[6], node[7] );
263 else if (nbnode == 10) {
264 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
265 node[4], node[5], node[6], node[7],
266 node[8], node[9], ID);
267 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
268 node[4], node[5], node[6], node[7],
271 else if (nbnode == 12) {
272 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
273 node[4], node[5], node[6], node[7],
274 node[8], node[9], node[10], node[11], ID);
275 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
276 node[4], node[5], node[6], node[7],
277 node[8], node[9], node[10], node[11] );
279 else if (nbnode == 13) {
280 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
281 node[4], node[5], node[6], node[7],
282 node[8], node[9], node[10],node[11],
284 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
285 node[4], node[5], node[6], node[7],
286 node[8], node[9], node[10],node[11],
289 else if (nbnode == 15) {
290 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
291 node[4], node[5], node[6], node[7],
292 node[8], node[9], node[10],node[11],
293 node[12],node[13],node[14],ID);
294 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
295 node[4], node[5], node[6], node[7],
296 node[8], node[9], node[10],node[11],
297 node[12],node[13],node[14] );
299 else if (nbnode == 18) {
300 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
301 node[4], node[5], node[6], node[7],
302 node[8], node[9], node[10],node[11],
303 node[12],node[13],node[14],
304 node[15],node[16],node[17],ID );
305 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
306 node[4], node[5], node[6], node[7],
307 node[8], node[9], node[10],node[11],
308 node[12],node[13],node[14],
309 node[15],node[16],node[17] );
311 else if (nbnode == 20) {
312 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
313 node[4], node[5], node[6], node[7],
314 node[8], node[9], node[10],node[11],
315 node[12],node[13],node[14],node[15],
316 node[16],node[17],node[18],node[19],ID);
317 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
318 node[4], node[5], node[6], node[7],
319 node[8], node[9], node[10],node[11],
320 node[12],node[13],node[14],node[15],
321 node[16],node[17],node[18],node[19] );
323 else if (nbnode == 27) {
324 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
325 node[4], node[5], node[6], node[7],
326 node[8], node[9], node[10],node[11],
327 node[12],node[13],node[14],node[15],
328 node[16],node[17],node[18],node[19],
329 node[20],node[21],node[22],node[23],
330 node[24],node[25],node[26], ID);
331 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
332 node[4], node[5], node[6], node[7],
333 node[8], node[9], node[10],node[11],
334 node[12],node[13],node[14],node[15],
335 node[16],node[17],node[18],node[19],
336 node[20],node[21],node[22],node[23],
337 node[24],node[25],node[26] );
340 else if ( !features.myIsQuad )
342 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
343 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
347 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
348 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
354 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
355 else e = mesh->AddEdge (node[0], node[1] );
357 else if ( nbnode == 3 ) {
358 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
359 else e = mesh->AddEdge (node[0], node[1], node[2] );
363 case SMDSAbs_0DElement:
365 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
366 else e = mesh->Add0DElement (node[0] );
371 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
372 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
376 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
377 else e = mesh->AddBall (node[0], features.myBallDiameter );
382 if ( e ) myLastCreatedElems.push_back( e );
386 //=======================================================================
390 //=======================================================================
392 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
393 const ElemFeatures& features)
395 vector<const SMDS_MeshNode*> nodes;
396 nodes.reserve( nodeIDs.size() );
397 vector<int>::const_iterator id = nodeIDs.begin();
398 while ( id != nodeIDs.end() ) {
399 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
400 nodes.push_back( node );
404 return AddElement( nodes, features );
407 //=======================================================================
409 //purpose : Remove a node or an element.
410 // Modify a compute state of sub-meshes which become empty
411 //=======================================================================
413 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
418 SMESHDS_Mesh* aMesh = GetMeshDS();
419 set< SMESH_subMesh *> smmap;
422 list<int>::const_iterator it = theIDs.begin();
423 for ( ; it != theIDs.end(); it++ ) {
424 const SMDS_MeshElement * elem;
426 elem = aMesh->FindNode( *it );
428 elem = aMesh->FindElement( *it );
432 // Notify VERTEX sub-meshes about modification
434 const SMDS_MeshNode* node = cast2Node( elem );
435 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
436 if ( int aShapeID = node->getshapeId() )
437 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
440 // Find sub-meshes to notify about modification
441 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
442 // while ( nodeIt->more() ) {
443 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
444 // const SMDS_PositionPtr& aPosition = node->GetPosition();
445 // if ( aPosition.get() ) {
446 // if ( int aShapeID = aPosition->GetShapeId() ) {
447 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
448 // smmap.insert( sm );
455 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
457 aMesh->RemoveElement( elem );
461 // Notify sub-meshes about modification
462 if ( !smmap.empty() ) {
463 set< SMESH_subMesh *>::iterator smIt;
464 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
465 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
468 // // Check if the whole mesh becomes empty
469 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
470 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
475 //================================================================================
477 * \brief Create 0D elements on all nodes of the given object.
478 * \param elements - Elements on whose nodes to create 0D elements; if empty,
479 * the all mesh is treated
480 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
481 * \param duplicateElements - to add one more 0D element to a node or not
483 //================================================================================
485 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
486 TIDSortedElemSet& all0DElems,
487 const bool duplicateElements )
489 SMDS_ElemIteratorPtr elemIt;
490 if ( elements.empty() )
492 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
496 elemIt = SMESHUtils::elemSetIterator( elements );
499 while ( elemIt->more() )
501 const SMDS_MeshElement* e = elemIt->next();
502 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
503 while ( nodeIt->more() )
505 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
506 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
507 if ( duplicateElements || !it0D->more() )
509 myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
510 all0DElems.insert( myLastCreatedElems.back() );
512 while ( it0D->more() )
513 all0DElems.insert( it0D->next() );
518 //=======================================================================
519 //function : FindShape
520 //purpose : Return an index of the shape theElem is on
521 // or zero if a shape not found
522 //=======================================================================
524 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
528 SMESHDS_Mesh * aMesh = GetMeshDS();
529 if ( aMesh->ShapeToMesh().IsNull() )
532 int aShapeID = theElem->getshapeId();
536 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
537 if ( sm->Contains( theElem ))
540 if ( theElem->GetType() == SMDSAbs_Node ) {
541 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
544 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
547 TopoDS_Shape aShape; // the shape a node of theElem is on
548 if ( theElem->GetType() != SMDSAbs_Node )
550 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
551 while ( nodeIt->more() ) {
552 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
553 if ((aShapeID = node->getshapeId()) > 0) {
554 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
555 if ( sm->Contains( theElem ))
557 if ( aShape.IsNull() )
558 aShape = aMesh->IndexToShape( aShapeID );
564 // None of nodes is on a proper shape,
565 // find the shape among ancestors of aShape on which a node is
566 if ( !aShape.IsNull() ) {
567 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
568 for ( ; ancIt.More(); ancIt.Next() ) {
569 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
570 if ( sm && sm->Contains( theElem ))
571 return aMesh->ShapeToIndex( ancIt.Value() );
576 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
577 while ( const SMESHDS_SubMesh* sm = smIt->next() )
578 if ( sm->Contains( theElem ))
585 //=======================================================================
586 //function : IsMedium
588 //=======================================================================
590 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
591 const SMDSAbs_ElementType typeToCheck)
593 bool isMedium = false;
594 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
595 while (it->more() && !isMedium ) {
596 const SMDS_MeshElement* elem = it->next();
597 isMedium = elem->IsMediumNode(node);
602 //=======================================================================
603 //function : shiftNodesQuadTria
604 //purpose : Shift nodes in the array corresponded to quadratic triangle
605 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
606 //=======================================================================
608 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
610 const SMDS_MeshNode* nd1 = aNodes[0];
611 aNodes[0] = aNodes[1];
612 aNodes[1] = aNodes[2];
614 const SMDS_MeshNode* nd2 = aNodes[3];
615 aNodes[3] = aNodes[4];
616 aNodes[4] = aNodes[5];
620 //=======================================================================
621 //function : getNodesFromTwoTria
623 //=======================================================================
625 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
626 const SMDS_MeshElement * theTria2,
627 vector< const SMDS_MeshNode*>& N1,
628 vector< const SMDS_MeshNode*>& N2)
630 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
631 if ( N1.size() < 6 ) return false;
632 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
633 if ( N2.size() < 6 ) return false;
635 int sames[3] = {-1,-1,-1};
647 if(nbsames!=2) return false;
649 shiftNodesQuadTria(N1);
651 shiftNodesQuadTria(N1);
654 i = sames[0] + sames[1] + sames[2];
656 shiftNodesQuadTria(N2);
658 // now we receive following N1 and N2 (using numeration as in the image below)
659 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
660 // i.e. first nodes from both arrays form a new diagonal
664 //=======================================================================
665 //function : InverseDiag
666 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
667 // but having other common link.
668 // Return False if args are improper
669 //=======================================================================
671 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
672 const SMDS_MeshElement * theTria2 )
676 if ( !theTria1 || !theTria2 ||
677 !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
678 !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
679 theTria1->GetType() != SMDSAbs_Face ||
680 theTria2->GetType() != SMDSAbs_Face )
683 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
684 (theTria2->GetEntityType() == SMDSEntity_Triangle))
686 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
687 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
691 // put nodes in array and find out indices of the same ones
692 const SMDS_MeshNode* aNodes [6];
693 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
695 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
696 while ( it->more() ) {
697 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
699 if ( i > 2 ) // theTria2
700 // find same node of theTria1
701 for ( int j = 0; j < 3; j++ )
702 if ( aNodes[ i ] == aNodes[ j ]) {
711 return false; // theTria1 is not a triangle
712 it = theTria2->nodesIterator();
714 if ( i == 6 && it->more() )
715 return false; // theTria2 is not a triangle
718 // find indices of 1,2 and of A,B in theTria1
719 int iA = -1, iB = 0, i1 = 0, i2 = 0;
720 for ( i = 0; i < 6; i++ ) {
721 if ( sameInd [ i ] == -1 ) {
726 if ( iA >= 0) iB = i;
730 // nodes 1 and 2 should not be the same
731 if ( aNodes[ i1 ] == aNodes[ i2 ] )
735 aNodes[ iA ] = aNodes[ i2 ];
737 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
739 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
740 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
744 } // end if(F1 && F2)
746 // check case of quadratic faces
747 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
748 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
750 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
751 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
755 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
756 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
764 vector< const SMDS_MeshNode* > N1;
765 vector< const SMDS_MeshNode* > N2;
766 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
768 // now we receive following N1 and N2 (using numeration as above image)
769 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
770 // i.e. first nodes from both arrays determ new diagonal
772 vector< const SMDS_MeshNode*> N1new( N1.size() );
773 vector< const SMDS_MeshNode*> N2new( N2.size() );
774 N1new.back() = N1.back(); // central node of biquadratic
775 N2new.back() = N2.back();
776 N1new[0] = N1[0]; N2new[0] = N1[0];
777 N1new[1] = N2[0]; N2new[1] = N1[1];
778 N1new[2] = N2[1]; N2new[2] = N2[0];
779 N1new[3] = N1[4]; N2new[3] = N1[3];
780 N1new[4] = N2[3]; N2new[4] = N2[5];
781 N1new[5] = N1[5]; N2new[5] = N1[4];
782 // change nodes in faces
783 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
784 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
786 // move the central node of biquadratic triangle
787 SMESH_MesherHelper helper( *GetMesh() );
788 for ( int is2nd = 0; is2nd < 2; ++is2nd )
790 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
791 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
792 if ( nodes.size() < 7 )
794 helper.SetSubShape( tria->getshapeId() );
795 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
799 xyz = ( SMESH_NodeXYZ( nodes[3] ) +
800 SMESH_NodeXYZ( nodes[4] ) +
801 SMESH_NodeXYZ( nodes[5] )) / 3.;
806 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
807 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
808 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
810 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
811 xyz = S->Value( uv.X(), uv.Y() );
812 xyz.Transform( loc );
813 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
814 nodes[6]->getshapeId() > 0 )
815 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
817 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
822 //=======================================================================
823 //function : findTriangles
824 //purpose : find triangles sharing theNode1-theNode2 link
825 //=======================================================================
827 static bool findTriangles(const SMDS_MeshNode * theNode1,
828 const SMDS_MeshNode * theNode2,
829 const SMDS_MeshElement*& theTria1,
830 const SMDS_MeshElement*& theTria2)
832 if ( !theNode1 || !theNode2 ) return false;
834 theTria1 = theTria2 = 0;
836 set< const SMDS_MeshElement* > emap;
837 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
839 const SMDS_MeshElement* elem = it->next();
840 if ( elem->NbCornerNodes() == 3 )
843 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
845 const SMDS_MeshElement* elem = it->next();
846 if ( emap.count( elem )) {
854 // theTria1 must be element with minimum ID
855 if ( theTria2->GetID() < theTria1->GetID() )
856 std::swap( theTria2, theTria1 );
864 //=======================================================================
865 //function : InverseDiag
866 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
867 // with ones built on the same 4 nodes but having other common link.
868 // Return false if proper faces not found
869 //=======================================================================
871 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
872 const SMDS_MeshNode * theNode2)
876 const SMDS_MeshElement *tr1, *tr2;
877 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
880 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
881 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
884 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
885 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
887 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
888 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
892 // put nodes in array
893 // and find indices of 1,2 and of A in tr1 and of B in tr2
894 int i, iA1 = 0, i1 = 0;
895 const SMDS_MeshNode* aNodes1 [3];
896 SMDS_ElemIteratorPtr it;
897 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
898 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
899 if ( aNodes1[ i ] == theNode1 )
900 iA1 = i; // node A in tr1
901 else if ( aNodes1[ i ] != theNode2 )
905 const SMDS_MeshNode* aNodes2 [3];
906 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
907 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
908 if ( aNodes2[ i ] == theNode2 )
909 iB2 = i; // node B in tr2
910 else if ( aNodes2[ i ] != theNode1 )
914 // nodes 1 and 2 should not be the same
915 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
919 aNodes1[ iA1 ] = aNodes2[ i2 ];
921 aNodes2[ iB2 ] = aNodes1[ i1 ];
923 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
924 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
929 // check case of quadratic faces
930 return InverseDiag(tr1,tr2);
933 //=======================================================================
934 //function : getQuadrangleNodes
935 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
936 // fusion of triangles tr1 and tr2 having shared link on
937 // theNode1 and theNode2
938 //=======================================================================
940 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
941 const SMDS_MeshNode * theNode1,
942 const SMDS_MeshNode * theNode2,
943 const SMDS_MeshElement * tr1,
944 const SMDS_MeshElement * tr2 )
946 if( tr1->NbNodes() != tr2->NbNodes() )
948 // find the 4-th node to insert into tr1
949 const SMDS_MeshNode* n4 = 0;
950 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
952 while ( !n4 && i<3 ) {
953 const SMDS_MeshNode * n = cast2Node( it->next() );
955 bool isDiag = ( n == theNode1 || n == theNode2 );
959 // Make an array of nodes to be in a quadrangle
960 int iNode = 0, iFirstDiag = -1;
961 it = tr1->nodesIterator();
964 const SMDS_MeshNode * n = cast2Node( it->next() );
966 bool isDiag = ( n == theNode1 || n == theNode2 );
968 if ( iFirstDiag < 0 )
970 else if ( iNode - iFirstDiag == 1 )
971 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
973 else if ( n == n4 ) {
974 return false; // tr1 and tr2 should not have all the same nodes
976 theQuadNodes[ iNode++ ] = n;
978 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
979 theQuadNodes[ iNode ] = n4;
984 //=======================================================================
985 //function : DeleteDiag
986 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
987 // with a quadrangle built on the same 4 nodes.
988 // Return false if proper faces not found
989 //=======================================================================
991 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
992 const SMDS_MeshNode * theNode2)
996 const SMDS_MeshElement *tr1, *tr2;
997 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1000 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
1001 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
1004 SMESHDS_Mesh * aMesh = GetMeshDS();
1006 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1007 (tr2->GetEntityType() == SMDSEntity_Triangle))
1009 const SMDS_MeshNode* aNodes [ 4 ];
1010 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1013 const SMDS_MeshElement* newElem = 0;
1014 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1015 myLastCreatedElems.push_back(newElem);
1016 AddToSameGroups( newElem, tr1, aMesh );
1017 int aShapeId = tr1->getshapeId();
1019 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1021 aMesh->RemoveElement( tr1 );
1022 aMesh->RemoveElement( tr2 );
1027 // check case of quadratic faces
1028 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1030 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1034 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1035 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1043 vector< const SMDS_MeshNode* > N1;
1044 vector< const SMDS_MeshNode* > N2;
1045 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1047 // now we receive following N1 and N2 (using numeration as above image)
1048 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1049 // i.e. first nodes from both arrays determ new diagonal
1051 const SMDS_MeshNode* aNodes[8];
1061 const SMDS_MeshElement* newElem = 0;
1062 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1063 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1064 myLastCreatedElems.push_back(newElem);
1065 AddToSameGroups( newElem, tr1, aMesh );
1066 int aShapeId = tr1->getshapeId();
1069 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1071 aMesh->RemoveElement( tr1 );
1072 aMesh->RemoveElement( tr2 );
1074 // remove middle node (9)
1075 GetMeshDS()->RemoveNode( N1[4] );
1080 //=======================================================================
1081 //function : Reorient
1082 //purpose : Reverse theElement orientation
1083 //=======================================================================
1085 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1091 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1092 if ( !it || !it->more() )
1095 const SMDSAbs_ElementType type = theElem->GetType();
1096 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1099 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1100 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1102 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1104 MESSAGE("Warning: bad volumic element");
1107 SMDS_VolumeTool vTool( aPolyedre );
1108 const int nbFaces = vTool.NbFaces();
1109 vector<int> quantities( nbFaces );
1110 vector<const SMDS_MeshNode *> poly_nodes;
1112 // check if all facets are oriented equally
1113 bool sameOri = true;
1114 vector<int>& facetOri = quantities; // keep orientation in quantities so far
1115 for (int iface = 0; iface < nbFaces; iface++)
1117 facetOri[ iface ] = vTool.IsFaceExternal( iface );
1118 if ( facetOri[ iface ] != facetOri[ 0 ])
1122 // reverse faces of the polyhedron
1123 int neededOri = sameOri ? 1 - facetOri[0] : 1;
1124 poly_nodes.reserve( vTool.NbNodes() );
1125 for ( int iface = 0; iface < nbFaces; iface++ )
1127 int nbFaceNodes = vTool.NbFaceNodes( iface );
1128 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( iface );
1129 bool toReverse = ( facetOri[ iface ] != neededOri );
1131 quantities[ iface ] = nbFaceNodes;
1134 for ( int inode = nbFaceNodes - 1; inode >= 0; inode-- )
1135 poly_nodes.push_back( nodes[ inode ]);
1137 poly_nodes.insert( poly_nodes.end(), nodes, nodes + nbFaceNodes );
1139 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1141 else // other elements
1143 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1144 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1145 if ( interlace.empty() )
1147 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1151 SMDS_MeshCell::applyInterlace( interlace, nodes );
1153 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1158 //================================================================================
1160 * \brief Reorient faces.
1161 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1162 * \param theDirection - desired direction of normal of \a theFace
1163 * \param theFace - one of \a theFaces that should be oriented according to
1164 * \a theDirection and whose orientation defines orientation of other faces
1165 * \return number of reoriented faces.
1167 //================================================================================
1169 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1170 const gp_Dir& theDirection,
1171 const SMDS_MeshElement * theFace)
1174 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1176 if ( theFaces.empty() )
1178 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
1179 while ( fIt->more() )
1180 theFaces.insert( theFaces.end(), fIt->next() );
1183 // orient theFace according to theDirection
1185 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1186 if ( normal * theDirection.XYZ() < 0 )
1187 nbReori += Reorient( theFace );
1189 // Orient other faces
1191 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1192 TIDSortedElemSet avoidSet;
1193 set< SMESH_TLink > checkedLinks;
1194 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1196 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1197 theFaces.erase( theFace );
1198 startFaces.insert( theFace );
1200 int nodeInd1, nodeInd2;
1201 const SMDS_MeshElement* otherFace;
1202 vector< const SMDS_MeshElement* > facesNearLink;
1203 vector< std::pair< int, int > > nodeIndsOfFace;
1205 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1206 while ( !startFaces.empty() )
1208 startFace = startFaces.begin();
1209 theFace = *startFace;
1210 startFaces.erase( startFace );
1211 if ( !visitedFaces.insert( theFace ).second )
1215 avoidSet.insert(theFace);
1217 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1219 const int nbNodes = theFace->NbCornerNodes();
1220 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1222 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1223 linkIt_isNew = checkedLinks.insert( link );
1224 if ( !linkIt_isNew.second )
1226 // link has already been checked and won't be encountered more
1227 // if the group (theFaces) is manifold
1228 //checkedLinks.erase( linkIt_isNew.first );
1232 facesNearLink.clear();
1233 nodeIndsOfFace.clear();
1234 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1236 &nodeInd1, &nodeInd2 )))
1237 if ( otherFace != theFace)
1239 facesNearLink.push_back( otherFace );
1240 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1241 avoidSet.insert( otherFace );
1243 if ( facesNearLink.size() > 1 )
1245 // NON-MANIFOLD mesh shell !
1246 // select a face most co-directed with theFace,
1247 // other faces won't be visited this time
1249 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1250 double proj, maxProj = -1;
1251 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1252 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1253 if (( proj = Abs( NF * NOF )) > maxProj ) {
1255 otherFace = facesNearLink[i];
1256 nodeInd1 = nodeIndsOfFace[i].first;
1257 nodeInd2 = nodeIndsOfFace[i].second;
1260 // not to visit rejected faces
1261 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1262 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1263 visitedFaces.insert( facesNearLink[i] );
1265 else if ( facesNearLink.size() == 1 )
1267 otherFace = facesNearLink[0];
1268 nodeInd1 = nodeIndsOfFace.back().first;
1269 nodeInd2 = nodeIndsOfFace.back().second;
1271 if ( otherFace && otherFace != theFace)
1273 // link must be reverse in otherFace if orientation to otherFace
1274 // is same as that of theFace
1275 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1277 nbReori += Reorient( otherFace );
1279 startFaces.insert( otherFace );
1282 std::swap( link.first, link.second ); // reverse the link
1288 //================================================================================
1290 * \brief Reorient faces basing on orientation of adjacent volumes.
1291 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1292 * \param theVolumes - reference volumes.
1293 * \param theOutsideNormal - to orient faces to have their normal
1294 * pointing either \a outside or \a inside the adjacent volumes.
1295 * \return number of reoriented faces.
1297 //================================================================================
1299 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1300 TIDSortedElemSet & theVolumes,
1301 const bool theOutsideNormal)
1305 SMDS_ElemIteratorPtr faceIt;
1306 if ( theFaces.empty() )
1307 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1309 faceIt = SMESHUtils::elemSetIterator( theFaces );
1311 vector< const SMDS_MeshNode* > faceNodes;
1312 TIDSortedElemSet checkedVolumes;
1313 set< const SMDS_MeshNode* > faceNodesSet;
1314 SMDS_VolumeTool volumeTool;
1316 while ( faceIt->more() ) // loop on given faces
1318 const SMDS_MeshElement* face = faceIt->next();
1319 if ( face->GetType() != SMDSAbs_Face )
1322 const size_t nbCornersNodes = face->NbCornerNodes();
1323 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1325 checkedVolumes.clear();
1326 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1327 while ( vIt->more() )
1329 const SMDS_MeshElement* volume = vIt->next();
1331 if ( !checkedVolumes.insert( volume ).second )
1333 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1336 // is volume adjacent?
1337 bool allNodesCommon = true;
1338 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1339 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1340 if ( !allNodesCommon )
1343 // get nodes of a corresponding volume facet
1344 faceNodesSet.clear();
1345 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1346 volumeTool.Set( volume );
1347 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1348 if ( facetID < 0 ) continue;
1349 volumeTool.SetExternalNormal();
1350 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1352 // compare order of faceNodes and facetNodes
1353 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1355 for ( int i = 0; i < 2; ++i )
1357 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1358 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1359 if ( faceNodes[ iN ] == n )
1365 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1366 if ( isOutside != theOutsideNormal )
1367 nbReori += Reorient( face );
1369 } // loop on given faces
1374 //=======================================================================
1375 //function : getBadRate
1377 //=======================================================================
1379 static double getBadRate (const SMDS_MeshElement* theElem,
1380 SMESH::Controls::NumericalFunctorPtr& theCrit)
1382 SMESH::Controls::TSequenceOfXYZ P;
1383 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1385 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1386 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1389 //=======================================================================
1390 //function : QuadToTri
1391 //purpose : Cut quadrangles into triangles.
1392 // theCrit is used to select a diagonal to cut
1393 //=======================================================================
1395 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1396 SMESH::Controls::NumericalFunctorPtr theCrit)
1400 if ( !theCrit.get() )
1403 SMESHDS_Mesh * aMesh = GetMeshDS();
1404 Handle(Geom_Surface) surface;
1405 SMESH_MesherHelper helper( *GetMesh() );
1407 myLastCreatedElems.reserve( theElems.size() * 2 );
1409 TIDSortedElemSet::iterator itElem;
1410 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1412 const SMDS_MeshElement* elem = *itElem;
1413 if ( !elem || elem->GetType() != SMDSAbs_Face )
1415 if ( elem->NbCornerNodes() != 4 )
1418 // retrieve element nodes
1419 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1421 // compare two sets of possible triangles
1422 double aBadRate1, aBadRate2; // to what extent a set is bad
1423 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1424 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1425 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1427 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1428 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1429 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1431 const int aShapeId = FindShape( elem );
1432 const SMDS_MeshElement* newElem1 = 0;
1433 const SMDS_MeshElement* newElem2 = 0;
1435 if ( !elem->IsQuadratic() ) // split linear quadrangle
1437 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1438 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1439 if ( aBadRate1 <= aBadRate2 ) {
1440 // tr1 + tr2 is better
1441 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1442 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1445 // tr3 + tr4 is better
1446 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1447 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1450 else // split quadratic quadrangle
1452 helper.SetIsQuadratic( true );
1453 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1455 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1456 if ( aNodes.size() == 9 )
1458 helper.SetIsBiQuadratic( true );
1459 if ( aBadRate1 <= aBadRate2 )
1460 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1462 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1464 // create a new element
1465 if ( aBadRate1 <= aBadRate2 ) {
1466 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1467 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1470 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1471 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1475 // care of a new element
1477 myLastCreatedElems.push_back(newElem1);
1478 myLastCreatedElems.push_back(newElem2);
1479 AddToSameGroups( newElem1, elem, aMesh );
1480 AddToSameGroups( newElem2, elem, aMesh );
1482 // put a new triangle on the same shape
1484 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1485 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1487 aMesh->RemoveElement( elem );
1492 //=======================================================================
1494 * \brief Split each of given quadrangles into 4 triangles.
1495 * \param theElems - The faces to be split. If empty all faces are split.
1497 //=======================================================================
1499 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1502 myLastCreatedElems.reserve( theElems.size() * 4 );
1504 SMESH_MesherHelper helper( *GetMesh() );
1505 helper.SetElementsOnShape( true );
1507 SMDS_ElemIteratorPtr faceIt;
1508 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1509 else faceIt = SMESHUtils::elemSetIterator( theElems );
1512 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1514 vector< const SMDS_MeshNode* > nodes;
1515 SMESHDS_SubMesh* subMeshDS = 0;
1517 Handle(Geom_Surface) surface;
1518 TopLoc_Location loc;
1520 while ( faceIt->more() )
1522 const SMDS_MeshElement* quad = faceIt->next();
1523 if ( !quad || quad->NbCornerNodes() != 4 )
1526 // get a surface the quad is on
1528 if ( quad->getshapeId() < 1 )
1531 helper.SetSubShape( 0 );
1534 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1536 helper.SetSubShape( quad->getshapeId() );
1537 if ( !helper.GetSubShape().IsNull() &&
1538 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1540 F = TopoDS::Face( helper.GetSubShape() );
1541 surface = BRep_Tool::Surface( F, loc );
1542 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1546 helper.SetSubShape( 0 );
1551 // create a central node
1553 const SMDS_MeshNode* nCentral;
1554 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1556 if ( nodes.size() == 9 )
1558 nCentral = nodes.back();
1565 for ( ; iN < nodes.size(); ++iN )
1566 xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1568 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1569 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1571 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1572 xyz[0], xyz[1], xyz[2], xyz[3],
1573 xyz[4], xyz[5], xyz[6], xyz[7] );
1577 for ( ; iN < nodes.size(); ++iN )
1578 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1580 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1581 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1583 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1584 uv[0], uv[1], uv[2], uv[3],
1585 uv[4], uv[5], uv[6], uv[7] );
1587 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1591 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1592 uv[8].X(), uv[8].Y() );
1593 myLastCreatedNodes.push_back( nCentral );
1596 // create 4 triangles
1598 helper.SetIsQuadratic ( nodes.size() > 4 );
1599 helper.SetIsBiQuadratic( nodes.size() == 9 );
1600 if ( helper.GetIsQuadratic() )
1601 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1603 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1605 for ( int i = 0; i < 4; ++i )
1607 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1610 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1611 myLastCreatedElems.push_back( tria );
1616 //=======================================================================
1617 //function : BestSplit
1618 //purpose : Find better diagonal for cutting.
1619 //=======================================================================
1621 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1622 SMESH::Controls::NumericalFunctorPtr theCrit)
1629 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1632 if( theQuad->NbNodes()==4 ||
1633 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1635 // retrieve element nodes
1636 const SMDS_MeshNode* aNodes [4];
1637 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1639 //while (itN->more())
1641 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1643 // compare two sets of possible triangles
1644 double aBadRate1, aBadRate2; // to what extent a set is bad
1645 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1646 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1647 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1649 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1650 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1651 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1652 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1653 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1654 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1655 return 1; // diagonal 1-3
1657 return 2; // diagonal 2-4
1664 // Methods of splitting volumes into tetra
1666 const int theHexTo5_1[5*4+1] =
1668 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1670 const int theHexTo5_2[5*4+1] =
1672 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1674 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1676 const int theHexTo6_1[6*4+1] =
1678 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
1680 const int theHexTo6_2[6*4+1] =
1682 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
1684 const int theHexTo6_3[6*4+1] =
1686 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
1688 const int theHexTo6_4[6*4+1] =
1690 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
1692 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1694 const int thePyraTo2_1[2*4+1] =
1696 0, 1, 2, 4, 0, 2, 3, 4, -1
1698 const int thePyraTo2_2[2*4+1] =
1700 1, 2, 3, 4, 1, 3, 0, 4, -1
1702 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1704 const int thePentaTo3_1[3*4+1] =
1706 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1708 const int thePentaTo3_2[3*4+1] =
1710 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1712 const int thePentaTo3_3[3*4+1] =
1714 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1716 const int thePentaTo3_4[3*4+1] =
1718 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1720 const int thePentaTo3_5[3*4+1] =
1722 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1724 const int thePentaTo3_6[3*4+1] =
1726 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1728 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1729 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1731 // Methods of splitting hexahedron into prisms
1733 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1735 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
1737 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1739 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
1741 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1743 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
1746 const int theHexTo2Prisms_BT_1[6*2+1] =
1748 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1750 const int theHexTo2Prisms_BT_2[6*2+1] =
1752 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1754 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1756 const int theHexTo2Prisms_LR_1[6*2+1] =
1758 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1760 const int theHexTo2Prisms_LR_2[6*2+1] =
1762 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1764 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1766 const int theHexTo2Prisms_FB_1[6*2+1] =
1768 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1770 const int theHexTo2Prisms_FB_2[6*2+1] =
1772 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1774 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1777 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1780 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1781 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1782 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1783 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1789 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1790 bool _baryNode; //!< additional node is to be created at cell barycenter
1791 bool _ownConn; //!< to delete _connectivity in destructor
1792 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1794 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1795 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1796 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1797 bool hasFacet( const TTriangleFacet& facet ) const
1799 if ( _nbCorners == 4 )
1801 const int* tetConn = _connectivity;
1802 for ( ; tetConn[0] >= 0; tetConn += 4 )
1803 if (( facet.contains( tetConn[0] ) +
1804 facet.contains( tetConn[1] ) +
1805 facet.contains( tetConn[2] ) +
1806 facet.contains( tetConn[3] )) == 3 )
1809 else // prism, _nbCorners == 6
1811 const int* prismConn = _connectivity;
1812 for ( ; prismConn[0] >= 0; prismConn += 6 )
1814 if (( facet.contains( prismConn[0] ) &&
1815 facet.contains( prismConn[1] ) &&
1816 facet.contains( prismConn[2] ))
1818 ( facet.contains( prismConn[3] ) &&
1819 facet.contains( prismConn[4] ) &&
1820 facet.contains( prismConn[5] )))
1828 //=======================================================================
1830 * \brief return TSplitMethod for the given element to split into tetrahedra
1832 //=======================================================================
1834 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1836 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1838 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1839 // an edge and a face barycenter; tertaherdons are based on triangles and
1840 // a volume barycenter
1841 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1843 // Find out how adjacent volumes are split
1845 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1846 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1847 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1849 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1850 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1851 if ( nbNodes < 4 ) continue;
1853 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1854 const int* nInd = vol.GetFaceNodesIndices( iF );
1857 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1858 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1859 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1860 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1864 int iCom = 0; // common node of triangle faces to split into
1865 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1867 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1868 nInd[ iQ * ( (iCom+1)%nbNodes )],
1869 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1870 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1871 nInd[ iQ * ( (iCom+2)%nbNodes )],
1872 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1873 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1875 triaSplits.push_back( t012 );
1876 triaSplits.push_back( t023 );
1881 if ( !triaSplits.empty() )
1882 hasAdjacentSplits = true;
1885 // Among variants of split method select one compliant with adjacent volumes
1887 TSplitMethod method;
1888 if ( !vol.Element()->IsPoly() && !is24TetMode )
1890 int nbVariants = 2, nbTet = 0;
1891 const int** connVariants = 0;
1892 switch ( vol.Element()->GetEntityType() )
1894 case SMDSEntity_Hexa:
1895 case SMDSEntity_Quad_Hexa:
1896 case SMDSEntity_TriQuad_Hexa:
1897 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1898 connVariants = theHexTo5, nbTet = 5;
1900 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1902 case SMDSEntity_Pyramid:
1903 case SMDSEntity_Quad_Pyramid:
1904 connVariants = thePyraTo2; nbTet = 2;
1906 case SMDSEntity_Penta:
1907 case SMDSEntity_Quad_Penta:
1908 case SMDSEntity_BiQuad_Penta:
1909 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1914 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1916 // check method compliancy with adjacent tetras,
1917 // all found splits must be among facets of tetras described by this method
1918 method = TSplitMethod( nbTet, connVariants[variant] );
1919 if ( hasAdjacentSplits && method._nbSplits > 0 )
1921 bool facetCreated = true;
1922 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1924 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1925 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1926 facetCreated = method.hasFacet( *facet );
1928 if ( !facetCreated )
1929 method = TSplitMethod(0); // incompatible method
1933 if ( method._nbSplits < 1 )
1935 // No standard method is applicable, use a generic solution:
1936 // each facet of a volume is split into triangles and
1937 // each of triangles and a volume barycenter form a tetrahedron.
1939 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1941 int* connectivity = new int[ maxTetConnSize + 1 ];
1942 method._connectivity = connectivity;
1943 method._ownConn = true;
1944 method._baryNode = !isHex27; // to create central node or not
1947 int baryCenInd = vol.NbNodes() - int( isHex27 );
1948 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1950 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1951 const int* nInd = vol.GetFaceNodesIndices( iF );
1952 // find common node of triangle facets of tetra to create
1953 int iCommon = 0; // index in linear numeration
1954 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1955 if ( !triaSplits.empty() )
1958 const TTriangleFacet* facet = &triaSplits.front();
1959 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1960 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1961 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1964 else if ( nbNodes > 3 && !is24TetMode )
1966 // find the best method of splitting into triangles by aspect ratio
1967 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1968 map< double, int > badness2iCommon;
1969 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1970 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1971 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1974 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1976 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1977 nodes[ iQ*((iLast-1)%nbNodes)],
1978 nodes[ iQ*((iLast )%nbNodes)]);
1979 badness += getBadRate( &tria, aspectRatio );
1981 badness2iCommon.insert( make_pair( badness, iCommon ));
1983 // use iCommon with lowest badness
1984 iCommon = badness2iCommon.begin()->second;
1986 if ( iCommon >= nbNodes )
1987 iCommon = 0; // something wrong
1989 // fill connectivity of tetrahedra based on a current face
1990 int nbTet = nbNodes - 2;
1991 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1996 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1997 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2001 method._faceBaryNode[ iF ] = 0;
2002 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2005 for ( int i = 0; i < nbTet; ++i )
2007 int i1 = i, i2 = (i+1) % nbNodes;
2008 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2009 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2010 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2011 connectivity[ connSize++ ] = faceBaryCenInd;
2012 connectivity[ connSize++ ] = baryCenInd;
2017 for ( int i = 0; i < nbTet; ++i )
2019 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2020 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2021 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2022 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2023 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2024 connectivity[ connSize++ ] = baryCenInd;
2027 method._nbSplits += nbTet;
2029 } // loop on volume faces
2031 connectivity[ connSize++ ] = -1;
2033 } // end of generic solution
2037 //=======================================================================
2039 * \brief return TSplitMethod to split haxhedron into prisms
2041 //=======================================================================
2043 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2044 const int methodFlags,
2045 const int facetToSplit)
2047 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2049 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2051 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2053 static TSplitMethod to4methods[4]; // order BT, LR, FB
2054 if ( to4methods[iF]._nbSplits == 0 )
2058 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2059 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2060 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2063 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2064 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2065 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2068 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2069 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2070 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2072 default: return to4methods[3];
2074 to4methods[iF]._nbSplits = 4;
2075 to4methods[iF]._nbCorners = 6;
2077 return to4methods[iF];
2079 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2081 TSplitMethod method;
2083 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2085 const int nbVariants = 2, nbSplits = 2;
2086 const int** connVariants = 0;
2088 case 0: connVariants = theHexTo2Prisms_BT; break;
2089 case 1: connVariants = theHexTo2Prisms_LR; break;
2090 case 2: connVariants = theHexTo2Prisms_FB; break;
2091 default: return method;
2094 // look for prisms adjacent via facetToSplit and an opposite one
2095 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2097 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2098 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2099 if ( nbNodes != 4 ) return method;
2101 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2102 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2103 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2105 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2107 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2112 // there are adjacent prism
2113 for ( int variant = 0; variant < nbVariants; ++variant )
2115 // check method compliancy with adjacent prisms,
2116 // the found prism facets must be among facets of prisms described by current method
2117 method._nbSplits = nbSplits;
2118 method._nbCorners = 6;
2119 method._connectivity = connVariants[ variant ];
2120 if ( method.hasFacet( *t ))
2125 // No adjacent prisms. Select a variant with a best aspect ratio.
2127 double badness[2] = { 0., 0. };
2128 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2129 const SMDS_MeshNode** nodes = vol.GetNodes();
2130 for ( int variant = 0; variant < nbVariants; ++variant )
2131 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2133 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2134 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2136 method._connectivity = connVariants[ variant ];
2137 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2138 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2139 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2141 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2144 badness[ variant ] += getBadRate( &tria, aspectRatio );
2146 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2148 method._nbSplits = nbSplits;
2149 method._nbCorners = 6;
2150 method._connectivity = connVariants[ iBetter ];
2155 //================================================================================
2157 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2159 //================================================================================
2161 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2162 const SMDSAbs_GeometryType geom ) const
2164 // find the tetrahedron including the three nodes of facet
2165 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2166 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2167 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2168 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2169 while ( volIt1->more() )
2171 const SMDS_MeshElement* v = volIt1->next();
2172 if ( v->GetGeomType() != geom )
2174 const int lastCornerInd = v->NbCornerNodes() - 1;
2175 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2176 continue; // medium node not allowed
2177 const int ind2 = v->GetNodeIndex( n2 );
2178 if ( ind2 < 0 || lastCornerInd < ind2 )
2180 const int ind3 = v->GetNodeIndex( n3 );
2181 if ( ind3 < 0 || lastCornerInd < ind3 )
2188 //=======================================================================
2190 * \brief A key of a face of volume
2192 //=======================================================================
2194 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2196 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2198 TIDSortedNodeSet sortedNodes;
2199 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2200 int nbNodes = vol.NbFaceNodes( iF );
2201 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2202 for ( int i = 0; i < nbNodes; i += iQ )
2203 sortedNodes.insert( fNodes[i] );
2204 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2205 first.first = (*(n++))->GetID();
2206 first.second = (*(n++))->GetID();
2207 second.first = (*(n++))->GetID();
2208 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2213 //=======================================================================
2214 //function : SplitVolumes
2215 //purpose : Split volume elements into tetrahedra or prisms.
2216 // If facet ID < 0, element is split into tetrahedra,
2217 // else a hexahedron is split into prisms so that the given facet is
2218 // split into triangles
2219 //=======================================================================
2221 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2222 const int theMethodFlags)
2224 SMDS_VolumeTool volTool;
2225 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2226 fHelper.ToFixNodeParameters( true );
2228 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2229 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2231 SMESH_SequenceOfElemPtr newNodes, newElems;
2233 // map face of volume to it's baricenrtic node
2234 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2236 vector<const SMDS_MeshElement* > splitVols;
2238 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2239 for ( ; elem2facet != theElems.end(); ++elem2facet )
2241 const SMDS_MeshElement* elem = elem2facet->first;
2242 const int facetToSplit = elem2facet->second;
2243 if ( elem->GetType() != SMDSAbs_Volume )
2245 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2246 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2249 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2251 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2252 getTetraSplitMethod( volTool, theMethodFlags ) :
2253 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2254 if ( splitMethod._nbSplits < 1 ) continue;
2256 // find submesh to add new tetras to
2257 if ( !subMesh || !subMesh->Contains( elem ))
2259 int shapeID = FindShape( elem );
2260 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2261 subMesh = GetMeshDS()->MeshElements( shapeID );
2264 if ( elem->IsQuadratic() )
2267 // add quadratic links to the helper
2268 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2270 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2271 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2272 for ( int iN = 0; iN < nbN; iN += iQ )
2273 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2275 helper.SetIsQuadratic( true );
2280 helper.SetIsQuadratic( false );
2282 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2283 volTool.GetNodes() + elem->NbNodes() );
2284 helper.SetElementsOnShape( true );
2285 if ( splitMethod._baryNode )
2287 // make a node at barycenter
2288 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2289 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2290 nodes.push_back( gcNode );
2291 newNodes.push_back( gcNode );
2293 if ( !splitMethod._faceBaryNode.empty() )
2295 // make or find baricentric nodes of faces
2296 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2297 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2299 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2300 volFace2BaryNode.insert
2301 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2304 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2305 newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2307 nodes.push_back( iF_n->second = f_n->second );
2312 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2313 const int* volConn = splitMethod._connectivity;
2314 if ( splitMethod._nbCorners == 4 ) // tetra
2315 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2316 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2317 nodes[ volConn[1] ],
2318 nodes[ volConn[2] ],
2319 nodes[ volConn[3] ]));
2321 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2322 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2323 nodes[ volConn[1] ],
2324 nodes[ volConn[2] ],
2325 nodes[ volConn[3] ],
2326 nodes[ volConn[4] ],
2327 nodes[ volConn[5] ]));
2329 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2331 // Split faces on sides of the split volume
2333 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2334 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2336 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2337 if ( nbNodes < 4 ) continue;
2339 // find an existing face
2340 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2341 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2342 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2343 /*noMedium=*/false))
2346 helper.SetElementsOnShape( false );
2347 vector< const SMDS_MeshElement* > triangles;
2349 // find submesh to add new triangles in
2350 if ( !fSubMesh || !fSubMesh->Contains( face ))
2352 int shapeID = FindShape( face );
2353 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2355 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2356 if ( iF_n != splitMethod._faceBaryNode.end() )
2358 const SMDS_MeshNode *baryNode = iF_n->second;
2359 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2361 const SMDS_MeshNode* n1 = fNodes[iN];
2362 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2363 const SMDS_MeshNode *n3 = baryNode;
2364 if ( !volTool.IsFaceExternal( iF ))
2366 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2368 if ( fSubMesh ) // update position of the bary node on geometry
2371 subMesh->RemoveNode( baryNode );
2372 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2373 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2374 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2376 fHelper.SetSubShape( s );
2377 gp_XY uv( 1e100, 1e100 );
2379 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2380 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2383 // node is too far from the surface
2384 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2385 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2386 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2393 // among possible triangles create ones described by split method
2394 const int* nInd = volTool.GetFaceNodesIndices( iF );
2395 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2396 int iCom = 0; // common node of triangle faces to split into
2397 list< TTriangleFacet > facets;
2398 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2400 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2401 nInd[ iQ * ( (iCom+1)%nbNodes )],
2402 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2403 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2404 nInd[ iQ * ( (iCom+2)%nbNodes )],
2405 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2406 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2408 facets.push_back( t012 );
2409 facets.push_back( t023 );
2410 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2411 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2412 nInd[ iQ * ((iLast-1)%nbNodes )],
2413 nInd[ iQ * ((iLast )%nbNodes )]));
2417 list< TTriangleFacet >::iterator facet = facets.begin();
2418 if ( facet == facets.end() )
2420 for ( ; facet != facets.end(); ++facet )
2422 if ( !volTool.IsFaceExternal( iF ))
2423 swap( facet->_n2, facet->_n3 );
2424 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2425 volNodes[ facet->_n2 ],
2426 volNodes[ facet->_n3 ]));
2429 for ( size_t i = 0; i < triangles.size(); ++i )
2431 if ( !triangles[ i ]) continue;
2433 fSubMesh->AddElement( triangles[ i ]);
2434 newElems.push_back( triangles[ i ]);
2436 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2437 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2439 } // while a face based on facet nodes exists
2440 } // loop on volume faces to split them into triangles
2442 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2444 if ( geomType == SMDSEntity_TriQuad_Hexa )
2446 // remove medium nodes that could become free
2447 for ( int i = 20; i < volTool.NbNodes(); ++i )
2448 if ( volNodes[i]->NbInverseElements() == 0 )
2449 GetMeshDS()->RemoveNode( volNodes[i] );
2451 } // loop on volumes to split
2453 myLastCreatedNodes = newNodes;
2454 myLastCreatedElems = newElems;
2457 //=======================================================================
2458 //function : GetHexaFacetsToSplit
2459 //purpose : For hexahedra that will be split into prisms, finds facets to
2460 // split into triangles. Only hexahedra adjacent to the one closest
2461 // to theFacetNormal.Location() are returned.
2462 //param [in,out] theHexas - the hexahedra
2463 //param [in] theFacetNormal - facet normal
2464 //param [out] theFacets - the hexahedra and found facet IDs
2465 //=======================================================================
2467 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2468 const gp_Ax1& theFacetNormal,
2469 TFacetOfElem & theFacets)
2471 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2473 // Find a hexa closest to the location of theFacetNormal
2475 const SMDS_MeshElement* startHex;
2477 // get SMDS_ElemIteratorPtr on theHexas
2478 typedef const SMDS_MeshElement* TValue;
2479 typedef TIDSortedElemSet::iterator TSetIterator;
2480 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2481 typedef SMDS_MeshElement::GeomFilter TFilter;
2482 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2483 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2484 ( new TElemSetIter( theHexas.begin(),
2486 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2488 SMESH_ElementSearcher* searcher =
2489 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2491 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2496 throw SALOME_Exception( THIS_METHOD "startHex not found");
2499 // Select a facet of startHex by theFacetNormal
2501 SMDS_VolumeTool vTool( startHex );
2502 double norm[3], dot, maxDot = 0;
2504 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2505 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2507 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2515 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2517 // Fill theFacets starting from facetID of startHex
2519 // facets used for searching of volumes adjacent to already treated ones
2520 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2521 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2522 TFacetMap facetsToCheck;
2524 set<const SMDS_MeshNode*> facetNodes;
2525 const SMDS_MeshElement* curHex;
2527 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2531 // move in two directions from startHex via facetID
2532 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2535 int curFacet = facetID;
2536 if ( is2nd ) // do not treat startHex twice
2538 vTool.Set( curHex );
2539 if ( vTool.IsFreeFace( curFacet, &curHex ))
2545 vTool.GetFaceNodes( curFacet, facetNodes );
2546 vTool.Set( curHex );
2547 curFacet = vTool.GetFaceIndex( facetNodes );
2552 // store a facet to split
2553 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2555 theFacets.insert( make_pair( curHex, -1 ));
2558 if ( !allHex && !theHexas.count( curHex ))
2561 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2562 theFacets.insert( make_pair( curHex, curFacet ));
2563 if ( !facetIt2isNew.second )
2566 // remember not-to-split facets in facetsToCheck
2567 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2568 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2570 if ( iF == curFacet && iF == oppFacet )
2572 TVolumeFaceKey facetKey ( vTool, iF );
2573 TElemFacets elemFacet( facetIt2isNew.first, iF );
2574 pair< TFacetMap::iterator, bool > it2isnew =
2575 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2576 if ( !it2isnew.second )
2577 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2579 // pass to a volume adjacent via oppFacet
2580 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2586 // get a new curFacet
2587 vTool.GetFaceNodes( oppFacet, facetNodes );
2588 vTool.Set( curHex );
2589 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2592 } // move in two directions from startHex via facetID
2594 // Find a new startHex by facetsToCheck
2598 TFacetMap::iterator fIt = facetsToCheck.begin();
2599 while ( !startHex && fIt != facetsToCheck.end() )
2601 const TElemFacets& elemFacets = fIt->second;
2602 const SMDS_MeshElement* hex = elemFacets.first->first;
2603 int splitFacet = elemFacets.first->second;
2604 int lateralFacet = elemFacets.second;
2605 facetsToCheck.erase( fIt );
2606 fIt = facetsToCheck.begin();
2609 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2610 curHex->GetGeomType() != SMDSGeom_HEXA )
2612 if ( !allHex && !theHexas.count( curHex ))
2617 // find a facet of startHex to split
2619 set<const SMDS_MeshNode*> lateralNodes;
2620 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2621 vTool.GetFaceNodes( splitFacet, facetNodes );
2622 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2623 vTool.Set( startHex );
2624 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2626 // look for a facet of startHex having common nodes with facetNodes
2627 // but not lateralFacet
2628 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2630 if ( iF == lateralFacet )
2632 int nbCommonNodes = 0;
2633 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2634 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2635 nbCommonNodes += facetNodes.count( nn[ iN ]);
2637 if ( nbCommonNodes >= 2 )
2644 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2646 } // while ( startHex )
2653 //================================================================================
2655 * \brief Selects nodes of several elements according to a given interlace
2656 * \param [in] srcNodes - nodes to select from
2657 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2658 * \param [in] interlace - indices of nodes for all elements
2659 * \param [in] nbElems - nb of elements
2660 * \param [in] nbNodes - nb of nodes in each element
2661 * \param [in] mesh - the mesh
2662 * \param [out] elemQueue - a list to push elements found by the selected nodes
2663 * \param [in] type - type of elements to look for
2665 //================================================================================
2667 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2668 vector< const SMDS_MeshNode* >* tgtNodesVec,
2669 const int* interlace,
2672 SMESHDS_Mesh* mesh = 0,
2673 list< const SMDS_MeshElement* >* elemQueue=0,
2674 SMDSAbs_ElementType type=SMDSAbs_All)
2676 for ( int iE = 0; iE < nbElems; ++iE )
2678 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2679 const int* select = & interlace[iE*nbNodes];
2680 elemNodes.resize( nbNodes );
2681 for ( int iN = 0; iN < nbNodes; ++iN )
2682 elemNodes[iN] = srcNodes[ select[ iN ]];
2684 const SMDS_MeshElement* e;
2686 for ( int iE = 0; iE < nbElems; ++iE )
2687 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2688 elemQueue->push_back( e );
2692 //=======================================================================
2694 * Split bi-quadratic elements into linear ones without creation of additional nodes
2695 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2696 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2697 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2698 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2699 * will be split in order to keep the mesh conformal.
2700 * \param elems - elements to split
2702 //=======================================================================
2704 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2706 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2707 vector<const SMDS_MeshElement* > splitElems;
2708 list< const SMDS_MeshElement* > elemQueue;
2709 list< const SMDS_MeshElement* >::iterator elemIt;
2711 SMESHDS_Mesh * mesh = GetMeshDS();
2712 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2713 int nbElems, nbNodes;
2715 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2716 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2719 elemQueue.push_back( *elemSetIt );
2720 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2722 const SMDS_MeshElement* elem = *elemIt;
2723 switch( elem->GetEntityType() )
2725 case SMDSEntity_TriQuad_Hexa: // HEX27
2727 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2728 nbElems = nbNodes = 8;
2729 elemType = & hexaType;
2731 // get nodes for new elements
2732 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2733 { 1,9,20,8, 17,22,26,21 },
2734 { 2,10,20,9, 18,23,26,22 },
2735 { 3,11,20,10, 19,24,26,23 },
2736 { 16,21,26,24, 4,12,25,15 },
2737 { 17,22,26,21, 5,13,25,12 },
2738 { 18,23,26,22, 6,14,25,13 },
2739 { 19,24,26,23, 7,15,25,14 }};
2740 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2742 // add boundary faces to elemQueue
2743 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2744 { 4,5,6,7, 12,13,14,15, 25 },
2745 { 0,1,5,4, 8,17,12,16, 21 },
2746 { 1,2,6,5, 9,18,13,17, 22 },
2747 { 2,3,7,6, 10,19,14,18, 23 },
2748 { 3,0,4,7, 11,16,15,19, 24 }};
2749 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2751 // add boundary segments to elemQueue
2752 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2753 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2754 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2755 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2758 case SMDSEntity_BiQuad_Triangle: // TRIA7
2760 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2763 elemType = & quadType;
2765 // get nodes for new elements
2766 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2767 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2769 // add boundary segments to elemQueue
2770 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2771 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2774 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2776 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2779 elemType = & quadType;
2781 // get nodes for new elements
2782 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2783 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2785 // add boundary segments to elemQueue
2786 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2787 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2790 case SMDSEntity_Quad_Edge:
2792 if ( elemIt == elemQueue.begin() )
2793 continue; // an elem is in theElems
2794 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2797 elemType = & segType;
2799 // get nodes for new elements
2800 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2801 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2805 } // switch( elem->GetEntityType() )
2807 // Create new elements
2809 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2813 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2814 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2815 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2816 //elemType->SetID( -1 );
2818 for ( int iE = 0; iE < nbElems; ++iE )
2819 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2822 ReplaceElemInGroups( elem, splitElems, mesh );
2825 for ( size_t i = 0; i < splitElems.size(); ++i )
2826 subMesh->AddElement( splitElems[i] );
2831 //=======================================================================
2832 //function : AddToSameGroups
2833 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2834 //=======================================================================
2836 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2837 const SMDS_MeshElement* elemInGroups,
2838 SMESHDS_Mesh * aMesh)
2840 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2841 if (!groups.empty()) {
2842 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2843 for ( ; grIt != groups.end(); grIt++ ) {
2844 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2845 if ( group && group->Contains( elemInGroups ))
2846 group->SMDSGroup().Add( elemToAdd );
2852 //=======================================================================
2853 //function : RemoveElemFromGroups
2854 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2855 //=======================================================================
2856 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2857 SMESHDS_Mesh * aMesh)
2859 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2860 if (!groups.empty())
2862 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2863 for (; GrIt != groups.end(); GrIt++)
2865 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2866 if (!grp || grp->IsEmpty()) continue;
2867 grp->SMDSGroup().Remove(removeelem);
2872 //================================================================================
2874 * \brief Replace elemToRm by elemToAdd in the all groups
2876 //================================================================================
2878 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2879 const SMDS_MeshElement* elemToAdd,
2880 SMESHDS_Mesh * aMesh)
2882 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2883 if (!groups.empty()) {
2884 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2885 for ( ; grIt != groups.end(); grIt++ ) {
2886 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2887 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2888 group->SMDSGroup().Add( elemToAdd );
2893 //================================================================================
2895 * \brief Replace elemToRm by elemToAdd in the all groups
2897 //================================================================================
2899 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2900 const vector<const SMDS_MeshElement*>& elemToAdd,
2901 SMESHDS_Mesh * aMesh)
2903 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2904 if (!groups.empty())
2906 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2907 for ( ; grIt != groups.end(); grIt++ ) {
2908 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2909 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2910 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2911 group->SMDSGroup().Add( elemToAdd[ i ] );
2916 //=======================================================================
2917 //function : QuadToTri
2918 //purpose : Cut quadrangles into triangles.
2919 // theCrit is used to select a diagonal to cut
2920 //=======================================================================
2922 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2923 const bool the13Diag)
2926 myLastCreatedElems.reserve( theElems.size() * 2 );
2928 SMESHDS_Mesh * aMesh = GetMeshDS();
2929 Handle(Geom_Surface) surface;
2930 SMESH_MesherHelper helper( *GetMesh() );
2932 TIDSortedElemSet::iterator itElem;
2933 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2935 const SMDS_MeshElement* elem = *itElem;
2936 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2939 if ( elem->NbNodes() == 4 ) {
2940 // retrieve element nodes
2941 const SMDS_MeshNode* aNodes [4];
2942 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2944 while ( itN->more() )
2945 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2947 int aShapeId = FindShape( elem );
2948 const SMDS_MeshElement* newElem1 = 0;
2949 const SMDS_MeshElement* newElem2 = 0;
2951 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2952 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2955 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2956 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2958 myLastCreatedElems.push_back(newElem1);
2959 myLastCreatedElems.push_back(newElem2);
2960 // put a new triangle on the same shape and add to the same groups
2963 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2964 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2966 AddToSameGroups( newElem1, elem, aMesh );
2967 AddToSameGroups( newElem2, elem, aMesh );
2968 aMesh->RemoveElement( elem );
2971 // Quadratic quadrangle
2973 else if ( elem->NbNodes() >= 8 )
2975 // get surface elem is on
2976 int aShapeId = FindShape( elem );
2977 if ( aShapeId != helper.GetSubShapeID() ) {
2981 shape = aMesh->IndexToShape( aShapeId );
2982 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2983 TopoDS_Face face = TopoDS::Face( shape );
2984 surface = BRep_Tool::Surface( face );
2985 if ( !surface.IsNull() )
2986 helper.SetSubShape( shape );
2990 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
2991 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2992 for ( int i = 0; itN->more(); ++i )
2993 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2995 const SMDS_MeshNode* centrNode = aNodes[8];
2996 if ( centrNode == 0 )
2998 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2999 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3001 myLastCreatedNodes.push_back(centrNode);
3004 // create a new element
3005 const SMDS_MeshElement* newElem1 = 0;
3006 const SMDS_MeshElement* newElem2 = 0;
3008 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3009 aNodes[6], aNodes[7], centrNode );
3010 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3011 centrNode, aNodes[4], aNodes[5] );
3014 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3015 aNodes[7], aNodes[4], centrNode );
3016 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3017 centrNode, aNodes[5], aNodes[6] );
3019 myLastCreatedElems.push_back(newElem1);
3020 myLastCreatedElems.push_back(newElem2);
3021 // put a new triangle on the same shape and add to the same groups
3024 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3025 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3027 AddToSameGroups( newElem1, elem, aMesh );
3028 AddToSameGroups( newElem2, elem, aMesh );
3029 aMesh->RemoveElement( elem );
3036 //=======================================================================
3037 //function : getAngle
3039 //=======================================================================
3041 double getAngle(const SMDS_MeshElement * tr1,
3042 const SMDS_MeshElement * tr2,
3043 const SMDS_MeshNode * n1,
3044 const SMDS_MeshNode * n2)
3046 double angle = 2. * M_PI; // bad angle
3049 SMESH::Controls::TSequenceOfXYZ P1, P2;
3050 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3051 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3054 if(!tr1->IsQuadratic())
3055 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3057 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3058 if ( N1.SquareMagnitude() <= gp::Resolution() )
3060 if(!tr2->IsQuadratic())
3061 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3063 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3064 if ( N2.SquareMagnitude() <= gp::Resolution() )
3067 // find the first diagonal node n1 in the triangles:
3068 // take in account a diagonal link orientation
3069 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3070 for ( int t = 0; t < 2; t++ ) {
3071 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3072 int i = 0, iDiag = -1;
3073 while ( it->more()) {
3074 const SMDS_MeshElement *n = it->next();
3075 if ( n == n1 || n == n2 ) {
3079 if ( i - iDiag == 1 )
3080 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3089 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3092 angle = N1.Angle( N2 );
3097 // =================================================
3098 // class generating a unique ID for a pair of nodes
3099 // and able to return nodes by that ID
3100 // =================================================
3104 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3105 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3108 long GetLinkID (const SMDS_MeshNode * n1,
3109 const SMDS_MeshNode * n2) const
3111 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3114 bool GetNodes (const long theLinkID,
3115 const SMDS_MeshNode* & theNode1,
3116 const SMDS_MeshNode* & theNode2) const
3118 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3119 if ( !theNode1 ) return false;
3120 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3121 if ( !theNode2 ) return false;
3127 const SMESHDS_Mesh* myMesh;
3132 //=======================================================================
3133 //function : TriToQuad
3134 //purpose : Fuse neighbour triangles into quadrangles.
3135 // theCrit is used to select a neighbour to fuse with.
3136 // theMaxAngle is a max angle between element normals at which
3137 // fusion is still performed.
3138 //=======================================================================
3140 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3141 SMESH::Controls::NumericalFunctorPtr theCrit,
3142 const double theMaxAngle)
3145 myLastCreatedElems.reserve( theElems.size() / 2 );
3147 if ( !theCrit.get() )
3150 SMESHDS_Mesh * aMesh = GetMeshDS();
3152 // Prepare data for algo: build
3153 // 1. map of elements with their linkIDs
3154 // 2. map of linkIDs with their elements
3156 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3157 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3158 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3159 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3161 TIDSortedElemSet::iterator itElem;
3162 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3164 const SMDS_MeshElement* elem = *itElem;
3165 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3166 bool IsTria = ( elem->NbCornerNodes()==3 );
3167 if (!IsTria) continue;
3169 // retrieve element nodes
3170 const SMDS_MeshNode* aNodes [4];
3171 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3174 aNodes[ i++ ] = itN->next();
3175 aNodes[ 3 ] = aNodes[ 0 ];
3178 for ( i = 0; i < 3; i++ ) {
3179 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3180 // check if elements sharing a link can be fused
3181 itLE = mapLi_listEl.find( link );
3182 if ( itLE != mapLi_listEl.end() ) {
3183 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3185 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3186 //if ( FindShape( elem ) != FindShape( elem2 ))
3187 // continue; // do not fuse triangles laying on different shapes
3188 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3189 continue; // avoid making badly shaped quads
3190 (*itLE).second.push_back( elem );
3193 mapLi_listEl[ link ].push_back( elem );
3195 mapEl_setLi [ elem ].insert( link );
3198 // Clean the maps from the links shared by a sole element, ie
3199 // links to which only one element is bound in mapLi_listEl
3201 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3202 int nbElems = (*itLE).second.size();
3203 if ( nbElems < 2 ) {
3204 const SMDS_MeshElement* elem = (*itLE).second.front();
3205 SMESH_TLink link = (*itLE).first;
3206 mapEl_setLi[ elem ].erase( link );
3207 if ( mapEl_setLi[ elem ].empty() )
3208 mapEl_setLi.erase( elem );
3212 // Algo: fuse triangles into quadrangles
3214 while ( ! mapEl_setLi.empty() ) {
3215 // Look for the start element:
3216 // the element having the least nb of shared links
3217 const SMDS_MeshElement* startElem = 0;
3219 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3220 int nbLinks = (*itEL).second.size();
3221 if ( nbLinks < minNbLinks ) {
3222 startElem = (*itEL).first;
3223 minNbLinks = nbLinks;
3224 if ( minNbLinks == 1 )
3229 // search elements to fuse starting from startElem or links of elements
3230 // fused earlyer - startLinks
3231 list< SMESH_TLink > startLinks;
3232 while ( startElem || !startLinks.empty() ) {
3233 while ( !startElem && !startLinks.empty() ) {
3234 // Get an element to start, by a link
3235 SMESH_TLink linkId = startLinks.front();
3236 startLinks.pop_front();
3237 itLE = mapLi_listEl.find( linkId );
3238 if ( itLE != mapLi_listEl.end() ) {
3239 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3240 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3241 for ( ; itE != listElem.end() ; itE++ )
3242 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3244 mapLi_listEl.erase( itLE );
3249 // Get candidates to be fused
3250 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3251 const SMESH_TLink *link12 = 0, *link13 = 0;
3253 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3254 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3255 ASSERT( !setLi.empty() );
3256 set< SMESH_TLink >::iterator itLi;
3257 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3259 const SMESH_TLink & link = (*itLi);
3260 itLE = mapLi_listEl.find( link );
3261 if ( itLE == mapLi_listEl.end() )
3264 const SMDS_MeshElement* elem = (*itLE).second.front();
3266 elem = (*itLE).second.back();
3267 mapLi_listEl.erase( itLE );
3268 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3279 // add other links of elem to list of links to re-start from
3280 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3281 set< SMESH_TLink >::iterator it;
3282 for ( it = links.begin(); it != links.end(); it++ ) {
3283 const SMESH_TLink& link2 = (*it);
3284 if ( link2 != link )
3285 startLinks.push_back( link2 );
3289 // Get nodes of possible quadrangles
3290 const SMDS_MeshNode *n12 [4], *n13 [4];
3291 bool Ok12 = false, Ok13 = false;
3292 const SMDS_MeshNode *linkNode1, *linkNode2;
3294 linkNode1 = link12->first;
3295 linkNode2 = link12->second;
3296 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3300 linkNode1 = link13->first;
3301 linkNode2 = link13->second;
3302 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3306 // Choose a pair to fuse
3307 if ( Ok12 && Ok13 ) {
3308 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3309 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3310 double aBadRate12 = getBadRate( &quad12, theCrit );
3311 double aBadRate13 = getBadRate( &quad13, theCrit );
3312 if ( aBadRate13 < aBadRate12 )
3319 // and remove fused elems and remove links from the maps
3320 mapEl_setLi.erase( tr1 );
3323 mapEl_setLi.erase( tr2 );
3324 mapLi_listEl.erase( *link12 );
3325 if ( tr1->NbNodes() == 3 )
3327 const SMDS_MeshElement* newElem = 0;
3328 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3329 myLastCreatedElems.push_back(newElem);
3330 AddToSameGroups( newElem, tr1, aMesh );
3331 int aShapeId = tr1->getshapeId();
3333 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3334 aMesh->RemoveElement( tr1 );
3335 aMesh->RemoveElement( tr2 );
3338 vector< const SMDS_MeshNode* > N1;
3339 vector< const SMDS_MeshNode* > N2;
3340 getNodesFromTwoTria(tr1,tr2,N1,N2);
3341 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3342 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3343 // i.e. first nodes from both arrays form a new diagonal
3344 const SMDS_MeshNode* aNodes[8];
3353 const SMDS_MeshElement* newElem = 0;
3354 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3355 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3356 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3358 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3359 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3360 myLastCreatedElems.push_back(newElem);
3361 AddToSameGroups( newElem, tr1, aMesh );
3362 int aShapeId = tr1->getshapeId();
3364 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3365 aMesh->RemoveElement( tr1 );
3366 aMesh->RemoveElement( tr2 );
3367 // remove middle node (9)
3368 if ( N1[4]->NbInverseElements() == 0 )
3369 aMesh->RemoveNode( N1[4] );
3370 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3371 aMesh->RemoveNode( N1[6] );
3372 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3373 aMesh->RemoveNode( N2[6] );
3378 mapEl_setLi.erase( tr3 );
3379 mapLi_listEl.erase( *link13 );
3380 if ( tr1->NbNodes() == 3 ) {
3381 const SMDS_MeshElement* newElem = 0;
3382 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3383 myLastCreatedElems.push_back(newElem);
3384 AddToSameGroups( newElem, tr1, aMesh );
3385 int aShapeId = tr1->getshapeId();
3387 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3388 aMesh->RemoveElement( tr1 );
3389 aMesh->RemoveElement( tr3 );
3392 vector< const SMDS_MeshNode* > N1;
3393 vector< const SMDS_MeshNode* > N2;
3394 getNodesFromTwoTria(tr1,tr3,N1,N2);
3395 // now we receive following N1 and N2 (using numeration as above image)
3396 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3397 // i.e. first nodes from both arrays form a new diagonal
3398 const SMDS_MeshNode* aNodes[8];
3407 const SMDS_MeshElement* newElem = 0;
3408 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3409 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3410 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3412 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3413 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3414 myLastCreatedElems.push_back(newElem);
3415 AddToSameGroups( newElem, tr1, aMesh );
3416 int aShapeId = tr1->getshapeId();
3418 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3419 aMesh->RemoveElement( tr1 );
3420 aMesh->RemoveElement( tr3 );
3421 // remove middle node (9)
3422 if ( N1[4]->NbInverseElements() == 0 )
3423 aMesh->RemoveNode( N1[4] );
3424 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3425 aMesh->RemoveNode( N1[6] );
3426 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3427 aMesh->RemoveNode( N2[6] );
3431 // Next element to fuse: the rejected one
3433 startElem = Ok12 ? tr3 : tr2;
3435 } // if ( startElem )
3436 } // while ( startElem || !startLinks.empty() )
3437 } // while ( ! mapEl_setLi.empty() )
3442 //================================================================================
3444 * \brief Return nodes linked to the given one
3445 * \param theNode - the node
3446 * \param linkedNodes - the found nodes
3447 * \param type - the type of elements to check
3449 * Medium nodes are ignored
3451 //================================================================================
3453 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3454 TIDSortedElemSet & linkedNodes,
3455 SMDSAbs_ElementType type )
3457 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3458 while ( elemIt->more() )
3460 const SMDS_MeshElement* elem = elemIt->next();
3461 if(elem->GetType() == SMDSAbs_0DElement)
3464 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3465 if ( elem->GetType() == SMDSAbs_Volume )
3467 SMDS_VolumeTool vol( elem );
3468 while ( nodeIt->more() ) {
3469 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3470 if ( theNode != n && vol.IsLinked( theNode, n ))
3471 linkedNodes.insert( n );
3476 for ( int i = 0; nodeIt->more(); ++i ) {
3477 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3478 if ( n == theNode ) {
3479 int iBefore = i - 1;
3481 if ( elem->IsQuadratic() ) {
3482 int nb = elem->NbNodes() / 2;
3483 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3484 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3486 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3487 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3494 //=======================================================================
3495 //function : laplacianSmooth
3496 //purpose : pulls theNode toward the center of surrounding nodes directly
3497 // connected to that node along an element edge
3498 //=======================================================================
3500 void laplacianSmooth(const SMDS_MeshNode* theNode,
3501 const Handle(Geom_Surface)& theSurface,
3502 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3504 // find surrounding nodes
3506 TIDSortedElemSet nodeSet;
3507 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3509 // compute new coodrs
3511 double coord[] = { 0., 0., 0. };
3512 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3513 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3514 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3515 if ( theSurface.IsNull() ) { // smooth in 3D
3516 coord[0] += node->X();
3517 coord[1] += node->Y();
3518 coord[2] += node->Z();
3520 else { // smooth in 2D
3521 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3522 gp_XY* uv = theUVMap[ node ];
3523 coord[0] += uv->X();
3524 coord[1] += uv->Y();
3527 int nbNodes = nodeSet.size();
3530 coord[0] /= nbNodes;
3531 coord[1] /= nbNodes;
3533 if ( !theSurface.IsNull() ) {
3534 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3535 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3536 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3542 coord[2] /= nbNodes;
3546 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3549 //=======================================================================
3550 //function : centroidalSmooth
3551 //purpose : pulls theNode toward the element-area-weighted centroid of the
3552 // surrounding elements
3553 //=======================================================================
3555 void centroidalSmooth(const SMDS_MeshNode* theNode,
3556 const Handle(Geom_Surface)& theSurface,
3557 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3559 gp_XYZ aNewXYZ(0.,0.,0.);
3560 SMESH::Controls::Area anAreaFunc;
3561 double totalArea = 0.;
3566 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3567 while ( elemIt->more() )
3569 const SMDS_MeshElement* elem = elemIt->next();
3572 gp_XYZ elemCenter(0.,0.,0.);
3573 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3574 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3575 int nn = elem->NbNodes();
3576 if(elem->IsQuadratic()) nn = nn/2;
3578 //while ( itN->more() ) {
3580 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3582 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3583 aNodePoints.push_back( aP );
3584 if ( !theSurface.IsNull() ) { // smooth in 2D
3585 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3586 gp_XY* uv = theUVMap[ aNode ];
3587 aP.SetCoord( uv->X(), uv->Y(), 0. );
3591 double elemArea = anAreaFunc.GetValue( aNodePoints );
3592 totalArea += elemArea;
3594 aNewXYZ += elemCenter * elemArea;
3596 aNewXYZ /= totalArea;
3597 if ( !theSurface.IsNull() ) {
3598 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3599 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3604 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3607 //=======================================================================
3608 //function : getClosestUV
3609 //purpose : return UV of closest projection
3610 //=======================================================================
3612 static bool getClosestUV (Extrema_GenExtPS& projector,
3613 const gp_Pnt& point,
3616 projector.Perform( point );
3617 if ( projector.IsDone() ) {
3618 double u, v, minVal = DBL_MAX;
3619 for ( int i = projector.NbExt(); i > 0; i-- )
3620 if ( projector.SquareDistance( i ) < minVal ) {
3621 minVal = projector.SquareDistance( i );
3622 projector.Point( i ).Parameter( u, v );
3624 result.SetCoord( u, v );
3630 //=======================================================================
3632 //purpose : Smooth theElements during theNbIterations or until a worst
3633 // element has aspect ratio <= theTgtAspectRatio.
3634 // Aspect Ratio varies in range [1.0, inf].
3635 // If theElements is empty, the whole mesh is smoothed.
3636 // theFixedNodes contains additionally fixed nodes. Nodes built
3637 // on edges and boundary nodes are always fixed.
3638 //=======================================================================
3640 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3641 set<const SMDS_MeshNode*> & theFixedNodes,
3642 const SmoothMethod theSmoothMethod,
3643 const int theNbIterations,
3644 double theTgtAspectRatio,
3649 if ( theTgtAspectRatio < 1.0 )
3650 theTgtAspectRatio = 1.0;
3652 const double disttol = 1.e-16;
3654 SMESH::Controls::AspectRatio aQualityFunc;
3656 SMESHDS_Mesh* aMesh = GetMeshDS();
3658 if ( theElems.empty() ) {
3659 // add all faces to theElems
3660 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3661 while ( fIt->more() ) {
3662 const SMDS_MeshElement* face = fIt->next();
3663 theElems.insert( theElems.end(), face );
3666 // get all face ids theElems are on
3667 set< int > faceIdSet;
3668 TIDSortedElemSet::iterator itElem;
3670 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3671 int fId = FindShape( *itElem );
3672 // check that corresponding submesh exists and a shape is face
3674 faceIdSet.find( fId ) == faceIdSet.end() &&
3675 aMesh->MeshElements( fId )) {
3676 TopoDS_Shape F = aMesh->IndexToShape( fId );
3677 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3678 faceIdSet.insert( fId );
3681 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3683 // ===============================================
3684 // smooth elements on each TopoDS_Face separately
3685 // ===============================================
3687 SMESH_MesherHelper helper( *GetMesh() );
3689 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3690 for ( ; fId != faceIdSet.rend(); ++fId )
3692 // get face surface and submesh
3693 Handle(Geom_Surface) surface;
3694 SMESHDS_SubMesh* faceSubMesh = 0;
3697 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3698 bool isUPeriodic = false, isVPeriodic = false;
3701 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3702 surface = BRep_Tool::Surface( face );
3703 faceSubMesh = aMesh->MeshElements( *fId );
3704 fToler2 = BRep_Tool::Tolerance( face );
3705 fToler2 *= fToler2 * 10.;
3706 isUPeriodic = surface->IsUPeriodic();
3707 // if ( isUPeriodic )
3708 // surface->UPeriod();
3709 isVPeriodic = surface->IsVPeriodic();
3710 // if ( isVPeriodic )
3711 // surface->VPeriod();
3712 surface->Bounds( u1, u2, v1, v2 );
3713 helper.SetSubShape( face );
3715 // ---------------------------------------------------------
3716 // for elements on a face, find movable and fixed nodes and
3717 // compute UV for them
3718 // ---------------------------------------------------------
3719 bool checkBoundaryNodes = false;
3720 bool isQuadratic = false;
3721 set<const SMDS_MeshNode*> setMovableNodes;
3722 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3723 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3724 list< const SMDS_MeshElement* > elemsOnFace;
3726 Extrema_GenExtPS projector;
3727 GeomAdaptor_Surface surfAdaptor;
3728 if ( !surface.IsNull() ) {
3729 surfAdaptor.Load( surface );
3730 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3732 int nbElemOnFace = 0;
3733 itElem = theElems.begin();
3734 // loop on not yet smoothed elements: look for elems on a face
3735 while ( itElem != theElems.end() )
3737 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3738 break; // all elements found
3740 const SMDS_MeshElement* elem = *itElem;
3741 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3742 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3746 elemsOnFace.push_back( elem );
3747 theElems.erase( itElem++ );
3751 isQuadratic = elem->IsQuadratic();
3753 // get movable nodes of elem
3754 const SMDS_MeshNode* node;
3755 SMDS_TypeOfPosition posType;
3756 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3757 int nn = 0, nbn = elem->NbNodes();
3758 if(elem->IsQuadratic())
3760 while ( nn++ < nbn ) {
3761 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3762 const SMDS_PositionPtr& pos = node->GetPosition();
3763 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3764 if (posType != SMDS_TOP_EDGE &&
3765 posType != SMDS_TOP_VERTEX &&
3766 theFixedNodes.find( node ) == theFixedNodes.end())
3768 // check if all faces around the node are on faceSubMesh
3769 // because a node on edge may be bound to face
3771 if ( faceSubMesh ) {
3772 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3773 while ( eIt->more() && all ) {
3774 const SMDS_MeshElement* e = eIt->next();
3775 all = faceSubMesh->Contains( e );
3779 setMovableNodes.insert( node );
3781 checkBoundaryNodes = true;
3783 if ( posType == SMDS_TOP_3DSPACE )
3784 checkBoundaryNodes = true;
3787 if ( surface.IsNull() )
3790 // get nodes to check UV
3791 list< const SMDS_MeshNode* > uvCheckNodes;
3792 const SMDS_MeshNode* nodeInFace = 0;
3793 itN = elem->nodesIterator();
3794 nn = 0; nbn = elem->NbNodes();
3795 if(elem->IsQuadratic())
3797 while ( nn++ < nbn ) {
3798 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3799 if ( node->GetPosition()->GetDim() == 2 )
3801 if ( uvMap.find( node ) == uvMap.end() )
3802 uvCheckNodes.push_back( node );
3803 // add nodes of elems sharing node
3804 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3805 // while ( eIt->more() ) {
3806 // const SMDS_MeshElement* e = eIt->next();
3807 // if ( e != elem ) {
3808 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3809 // while ( nIt->more() ) {
3810 // const SMDS_MeshNode* n =
3811 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3812 // if ( uvMap.find( n ) == uvMap.end() )
3813 // uvCheckNodes.push_back( n );
3819 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3820 for ( ; n != uvCheckNodes.end(); ++n ) {
3823 const SMDS_PositionPtr& pos = node->GetPosition();
3824 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3828 bool toCheck = true;
3829 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3831 // compute not existing UV
3832 bool project = ( posType == SMDS_TOP_3DSPACE );
3833 // double dist1 = DBL_MAX, dist2 = 0;
3834 // if ( posType != SMDS_TOP_3DSPACE ) {
3835 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3836 // project = dist1 > fToler2;
3838 if ( project ) { // compute new UV
3840 gp_Pnt pNode = SMESH_NodeXYZ( node );
3841 if ( !getClosestUV( projector, pNode, newUV )) {
3842 MESSAGE("Node Projection Failed " << node);
3846 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3848 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3850 // if ( posType != SMDS_TOP_3DSPACE )
3851 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3852 // if ( dist2 < dist1 )
3856 // store UV in the map
3857 listUV.push_back( uv );
3858 uvMap.insert( make_pair( node, &listUV.back() ));
3860 } // loop on not yet smoothed elements
3862 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3863 checkBoundaryNodes = true;
3865 // fix nodes on mesh boundary
3867 if ( checkBoundaryNodes ) {
3868 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3869 map< SMESH_TLink, int >::iterator link_nb;
3870 // put all elements links to linkNbMap
3871 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3872 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3873 const SMDS_MeshElement* elem = (*elemIt);
3874 int nbn = elem->NbCornerNodes();
3875 // loop on elem links: insert them in linkNbMap
3876 for ( int iN = 0; iN < nbn; ++iN ) {
3877 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3878 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3879 SMESH_TLink link( n1, n2 );
3880 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3884 // remove nodes that are in links encountered only once from setMovableNodes
3885 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3886 if ( link_nb->second == 1 ) {
3887 setMovableNodes.erase( link_nb->first.node1() );
3888 setMovableNodes.erase( link_nb->first.node2() );
3893 // -----------------------------------------------------
3894 // for nodes on seam edge, compute one more UV ( uvMap2 );
3895 // find movable nodes linked to nodes on seam and which
3896 // are to be smoothed using the second UV ( uvMap2 )
3897 // -----------------------------------------------------
3899 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3900 if ( !surface.IsNull() ) {
3901 TopExp_Explorer eExp( face, TopAbs_EDGE );
3902 for ( ; eExp.More(); eExp.Next() ) {
3903 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3904 if ( !BRep_Tool::IsClosed( edge, face ))
3906 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3907 if ( !sm ) continue;
3908 // find out which parameter varies for a node on seam
3911 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3912 if ( pcurve.IsNull() ) continue;
3913 uv1 = pcurve->Value( f );
3915 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3916 if ( pcurve.IsNull() ) continue;
3917 uv2 = pcurve->Value( f );
3918 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3920 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3921 std::swap( uv1, uv2 );
3922 // get nodes on seam and its vertices
3923 list< const SMDS_MeshNode* > seamNodes;
3924 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3925 while ( nSeamIt->more() ) {
3926 const SMDS_MeshNode* node = nSeamIt->next();
3927 if ( !isQuadratic || !IsMedium( node ))
3928 seamNodes.push_back( node );
3930 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3931 for ( ; vExp.More(); vExp.Next() ) {
3932 sm = aMesh->MeshElements( vExp.Current() );
3934 nSeamIt = sm->GetNodes();
3935 while ( nSeamIt->more() )
3936 seamNodes.push_back( nSeamIt->next() );
3939 // loop on nodes on seam
3940 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3941 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3942 const SMDS_MeshNode* nSeam = *noSeIt;
3943 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3944 if ( n_uv == uvMap.end() )
3947 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3948 // set the second UV
3949 listUV.push_back( *n_uv->second );
3950 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3951 if ( uvMap2.empty() )
3952 uvMap2 = uvMap; // copy the uvMap contents
3953 uvMap2[ nSeam ] = &listUV.back();
3955 // collect movable nodes linked to ones on seam in nodesNearSeam
3956 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3957 while ( eIt->more() ) {
3958 const SMDS_MeshElement* e = eIt->next();
3959 int nbUseMap1 = 0, nbUseMap2 = 0;
3960 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3961 int nn = 0, nbn = e->NbNodes();
3962 if(e->IsQuadratic()) nbn = nbn/2;
3963 while ( nn++ < nbn )
3965 const SMDS_MeshNode* n =
3966 static_cast<const SMDS_MeshNode*>( nIt->next() );
3968 setMovableNodes.find( n ) == setMovableNodes.end() )
3970 // add only nodes being closer to uv2 than to uv1
3971 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3972 // 0.5 * ( n->Y() + nSeam->Y() ),
3973 // 0.5 * ( n->Z() + nSeam->Z() ));
3975 // getClosestUV( projector, pMid, uv );
3976 double x = uvMap[ n ]->Coord( iPar );
3977 if ( Abs( uv1.Coord( iPar ) - x ) >
3978 Abs( uv2.Coord( iPar ) - x )) {
3979 nodesNearSeam.insert( n );
3985 // for centroidalSmooth all element nodes must
3986 // be on one side of a seam
3987 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3988 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3990 while ( nn++ < nbn ) {
3991 const SMDS_MeshNode* n =
3992 static_cast<const SMDS_MeshNode*>( nIt->next() );
3993 setMovableNodes.erase( n );
3997 } // loop on nodes on seam
3998 } // loop on edge of a face
3999 } // if ( !face.IsNull() )
4001 if ( setMovableNodes.empty() ) {
4002 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4003 continue; // goto next face
4011 double maxRatio = -1., maxDisplacement = -1.;
4012 set<const SMDS_MeshNode*>::iterator nodeToMove;
4013 for ( it = 0; it < theNbIterations; it++ ) {
4014 maxDisplacement = 0.;
4015 nodeToMove = setMovableNodes.begin();
4016 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4017 const SMDS_MeshNode* node = (*nodeToMove);
4018 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4021 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4022 if ( theSmoothMethod == LAPLACIAN )
4023 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4025 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4027 // node displacement
4028 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4029 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4030 if ( aDispl > maxDisplacement )
4031 maxDisplacement = aDispl;
4033 // no node movement => exit
4034 //if ( maxDisplacement < 1.e-16 ) {
4035 if ( maxDisplacement < disttol ) {
4036 MESSAGE("-- no node movement --");
4040 // check elements quality
4042 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4043 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4044 const SMDS_MeshElement* elem = (*elemIt);
4045 if ( !elem || elem->GetType() != SMDSAbs_Face )
4047 SMESH::Controls::TSequenceOfXYZ aPoints;
4048 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4049 double aValue = aQualityFunc.GetValue( aPoints );
4050 if ( aValue > maxRatio )
4054 if ( maxRatio <= theTgtAspectRatio ) {
4055 //MESSAGE("-- quality achieved --");
4058 if (it+1 == theNbIterations) {
4059 //MESSAGE("-- Iteration limit exceeded --");
4061 } // smoothing iterations
4063 // MESSAGE(" Face id: " << *fId <<
4064 // " Nb iterstions: " << it <<
4065 // " Displacement: " << maxDisplacement <<
4066 // " Aspect Ratio " << maxRatio);
4068 // ---------------------------------------
4069 // new nodes positions are computed,
4070 // record movement in DS and set new UV
4071 // ---------------------------------------
4072 nodeToMove = setMovableNodes.begin();
4073 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4074 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4075 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4076 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4077 if ( node_uv != uvMap.end() ) {
4078 gp_XY* uv = node_uv->second;
4080 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4084 // move medium nodes of quadratic elements
4087 vector<const SMDS_MeshNode*> nodes;
4089 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4090 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4092 const SMDS_MeshElement* QF = *elemIt;
4093 if ( QF->IsQuadratic() )
4095 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4096 SMDS_MeshElement::iterator() );
4097 nodes.push_back( nodes[0] );
4099 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4101 if ( !surface.IsNull() )
4103 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4104 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4105 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4106 xyz = surface->Value( uv.X(), uv.Y() );
4109 xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4111 if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4112 // we have to move a medium node
4113 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4119 } // loop on face ids
4125 //=======================================================================
4126 //function : isReverse
4127 //purpose : Return true if normal of prevNodes is not co-directied with
4128 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4129 // iNotSame is where prevNodes and nextNodes are different.
4130 // If result is true then future volume orientation is OK
4131 //=======================================================================
4133 bool isReverse(const SMDS_MeshElement* face,
4134 const vector<const SMDS_MeshNode*>& prevNodes,
4135 const vector<const SMDS_MeshNode*>& nextNodes,
4139 SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4140 SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4141 gp_XYZ extrDir( pN - pP ), faceNorm;
4142 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4144 return faceNorm * extrDir < 0.0;
4147 //================================================================================
4149 * \brief Assure that theElemSets[0] holds elements, not nodes
4151 //================================================================================
4153 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4155 if ( !theElemSets[0].empty() &&
4156 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4158 std::swap( theElemSets[0], theElemSets[1] );
4160 else if ( !theElemSets[1].empty() &&
4161 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4163 std::swap( theElemSets[0], theElemSets[1] );
4168 //=======================================================================
4170 * \brief Create elements by sweeping an element
4171 * \param elem - element to sweep
4172 * \param newNodesItVec - nodes generated from each node of the element
4173 * \param newElems - generated elements
4174 * \param nbSteps - number of sweeping steps
4175 * \param srcElements - to append elem for each generated element
4177 //=======================================================================
4179 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4180 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4181 list<const SMDS_MeshElement*>& newElems,
4182 const size_t nbSteps,
4183 SMESH_SequenceOfElemPtr& srcElements)
4185 SMESHDS_Mesh* aMesh = GetMeshDS();
4187 const int nbNodes = elem->NbNodes();
4188 const int nbCorners = elem->NbCornerNodes();
4189 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4190 polyhedron creation !!! */
4191 // Loop on elem nodes:
4192 // find new nodes and detect same nodes indices
4193 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4194 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4195 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4196 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4198 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4199 vector<int> sames(nbNodes);
4200 vector<bool> isSingleNode(nbNodes);
4202 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4203 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4204 const SMDS_MeshNode* node = nnIt->first;
4205 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4206 if ( listNewNodes.empty() )
4209 itNN [ iNode ] = listNewNodes.begin();
4210 prevNod[ iNode ] = node;
4211 nextNod[ iNode ] = listNewNodes.front();
4213 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4214 corner node of linear */
4215 if ( prevNod[ iNode ] != nextNod [ iNode ])
4216 nbDouble += !isSingleNode[iNode];
4218 if( iNode < nbCorners ) { // check corners only
4219 if ( prevNod[ iNode ] == nextNod [ iNode ])
4220 sames[nbSame++] = iNode;
4222 iNotSameNode = iNode;
4226 if ( nbSame == nbNodes || nbSame > 2) {
4227 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4231 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4233 // fix nodes order to have bottom normal external
4234 if ( baseType == SMDSEntity_Polygon )
4236 std::reverse( itNN.begin(), itNN.end() );
4237 std::reverse( prevNod.begin(), prevNod.end() );
4238 std::reverse( midlNod.begin(), midlNod.end() );
4239 std::reverse( nextNod.begin(), nextNod.end() );
4240 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4244 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4245 SMDS_MeshCell::applyInterlace( ind, itNN );
4246 SMDS_MeshCell::applyInterlace( ind, prevNod );
4247 SMDS_MeshCell::applyInterlace( ind, nextNod );
4248 SMDS_MeshCell::applyInterlace( ind, midlNod );
4249 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4252 sames[nbSame] = iNotSameNode;
4253 for ( int j = 0; j <= nbSame; ++j )
4254 for ( size_t i = 0; i < ind.size(); ++i )
4255 if ( ind[i] == sames[j] )
4260 iNotSameNode = sames[nbSame];
4264 else if ( elem->GetType() == SMDSAbs_Edge )
4266 // orient a new face same as adjacent one
4268 const SMDS_MeshElement* e;
4269 TIDSortedElemSet dummy;
4270 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4271 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4272 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4274 // there is an adjacent face, check order of nodes in it
4275 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4278 std::swap( itNN[0], itNN[1] );
4279 std::swap( prevNod[0], prevNod[1] );
4280 std::swap( nextNod[0], nextNod[1] );
4281 std::swap( isSingleNode[0], isSingleNode[1] );
4283 sames[0] = 1 - sames[0];
4284 iNotSameNode = 1 - iNotSameNode;
4289 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4291 iSameNode = sames[ nbSame-1 ];
4292 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4293 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4294 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4297 if ( baseType == SMDSEntity_Polygon )
4299 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4300 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4302 else if ( baseType == SMDSEntity_Quad_Polygon )
4304 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4305 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4308 // make new elements
4309 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4312 for ( iNode = 0; iNode < nbNodes; iNode++ )
4314 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4315 nextNod[ iNode ] = *itNN[ iNode ]++;
4318 SMDS_MeshElement* aNewElem = 0;
4319 /*if(!elem->IsPoly())*/ {
4320 switch ( baseType ) {
4322 case SMDSEntity_Node: { // sweep NODE
4323 if ( nbSame == 0 ) {
4324 if ( isSingleNode[0] )
4325 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4327 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4333 case SMDSEntity_Edge: { // sweep EDGE
4334 if ( nbDouble == 0 )
4336 if ( nbSame == 0 ) // ---> quadrangle
4337 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4338 nextNod[ 1 ], nextNod[ 0 ] );
4339 else // ---> triangle
4340 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4341 nextNod[ iNotSameNode ] );
4343 else // ---> polygon
4345 vector<const SMDS_MeshNode*> poly_nodes;
4346 poly_nodes.push_back( prevNod[0] );
4347 poly_nodes.push_back( prevNod[1] );
4348 if ( prevNod[1] != nextNod[1] )
4350 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4351 poly_nodes.push_back( nextNod[1] );
4353 if ( prevNod[0] != nextNod[0] )
4355 poly_nodes.push_back( nextNod[0] );
4356 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4358 switch ( poly_nodes.size() ) {
4360 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4363 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4364 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4367 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4372 case SMDSEntity_Triangle: // TRIANGLE --->
4374 if ( nbDouble > 0 ) break;
4375 if ( nbSame == 0 ) // ---> pentahedron
4376 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4377 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4379 else if ( nbSame == 1 ) // ---> pyramid
4380 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4381 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4382 nextNod[ iSameNode ]);
4384 else // 2 same nodes: ---> tetrahedron
4385 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4386 nextNod[ iNotSameNode ]);
4389 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4393 if ( nbDouble+nbSame == 2 )
4395 if(nbSame==0) { // ---> quadratic quadrangle
4396 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4397 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4399 else { //(nbSame==1) // ---> quadratic triangle
4401 return; // medium node on axis
4403 else if(sames[0]==0)
4404 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4405 prevNod[2], midlNod[1], nextNod[2] );
4407 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4408 prevNod[2], nextNod[2], midlNod[0]);
4411 else if ( nbDouble == 3 )
4413 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4414 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4415 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4422 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4423 if ( nbDouble > 0 ) break;
4425 if ( nbSame == 0 ) // ---> hexahedron
4426 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4427 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4429 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4430 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4431 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4432 nextNod[ iSameNode ]);
4433 newElems.push_back( aNewElem );
4434 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4435 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4436 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4438 else if ( nbSame == 2 ) { // ---> pentahedron
4439 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4440 // iBeforeSame is same too
4441 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4442 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4443 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4445 // iAfterSame is same too
4446 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4447 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4448 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4452 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4453 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4454 if ( nbDouble+nbSame != 3 ) break;
4456 // ---> pentahedron with 15 nodes
4457 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4458 nextNod[0], nextNod[1], nextNod[2],
4459 prevNod[3], prevNod[4], prevNod[5],
4460 nextNod[3], nextNod[4], nextNod[5],
4461 midlNod[0], midlNod[1], midlNod[2]);
4463 else if(nbSame==1) {
4464 // ---> 2d order pyramid of 13 nodes
4465 int apex = iSameNode;
4466 int i0 = ( apex + 1 ) % nbCorners;
4467 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4471 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4472 nextNod[i0], nextNod[i1], prevNod[apex],
4473 prevNod[i01], midlNod[i0],
4474 nextNod[i01], midlNod[i1],
4475 prevNod[i1a], prevNod[i0a],
4476 nextNod[i0a], nextNod[i1a]);
4478 else if(nbSame==2) {
4479 // ---> 2d order tetrahedron of 10 nodes
4480 int n1 = iNotSameNode;
4481 int n2 = ( n1 + 1 ) % nbCorners;
4482 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4486 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4487 prevNod[n12], prevNod[n23], prevNod[n31],
4488 midlNod[n1], nextNod[n12], nextNod[n31]);
4492 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4494 if ( nbDouble != 4 ) break;
4495 // ---> hexahedron with 20 nodes
4496 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4497 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4498 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4499 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4500 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4502 else if(nbSame==1) {
4503 // ---> pyramid + pentahedron - can not be created since it is needed
4504 // additional middle node at the center of face
4505 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4508 else if( nbSame == 2 ) {
4509 if ( nbDouble != 2 ) break;
4510 // ---> 2d order Pentahedron with 15 nodes
4512 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4513 // iBeforeSame is same too
4520 // iAfterSame is same too
4530 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4531 prevNod[n4], prevNod[n5], nextNod[n5],
4532 prevNod[n12], midlNod[n2], nextNod[n12],
4533 prevNod[n45], midlNod[n5], nextNod[n45],
4534 prevNod[n14], prevNod[n25], nextNod[n25]);
4538 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4540 if( nbSame == 0 && nbDouble == 9 ) {
4541 // ---> tri-quadratic hexahedron with 27 nodes
4542 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4543 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4544 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4545 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4546 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4547 prevNod[8], // bottom center
4548 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4549 nextNod[8], // top center
4550 midlNod[8]);// elem center
4558 case SMDSEntity_Polygon: { // sweep POLYGON
4560 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4561 // ---> hexagonal prism
4562 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4563 prevNod[3], prevNod[4], prevNod[5],
4564 nextNod[0], nextNod[1], nextNod[2],
4565 nextNod[3], nextNod[4], nextNod[5]);
4569 case SMDSEntity_Ball:
4574 } // switch ( baseType )
4577 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4579 if ( baseType != SMDSEntity_Polygon )
4581 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4582 SMDS_MeshCell::applyInterlace( ind, prevNod );
4583 SMDS_MeshCell::applyInterlace( ind, nextNod );
4584 SMDS_MeshCell::applyInterlace( ind, midlNod );
4585 SMDS_MeshCell::applyInterlace( ind, itNN );
4586 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4587 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4589 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4590 vector<int> quantities (nbNodes + 2);
4591 polyedre_nodes.clear();
4595 for (int inode = 0; inode < nbNodes; inode++)
4596 polyedre_nodes.push_back( prevNod[inode] );
4597 quantities.push_back( nbNodes );
4600 polyedre_nodes.push_back( nextNod[0] );
4601 for (int inode = nbNodes; inode-1; --inode )
4602 polyedre_nodes.push_back( nextNod[inode-1] );
4603 quantities.push_back( nbNodes );
4611 const int iQuad = elem->IsQuadratic();
4612 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4614 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4615 int inextface = (iface+1+iQuad) % nbNodes;
4616 int imid = (iface+1) % nbNodes;
4617 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4618 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4619 polyedre_nodes.push_back( prevNod[iface] ); // 1
4620 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4622 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4623 polyedre_nodes.push_back( nextNod[iface] ); // 2
4625 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4626 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4628 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4629 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4631 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4632 if ( nbFaceNodes > 2 )
4633 quantities.push_back( nbFaceNodes );
4634 else // degenerated face
4635 polyedre_nodes.resize( prevNbNodes );
4637 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4639 } // try to create a polyherdal prism
4642 newElems.push_back( aNewElem );
4643 myLastCreatedElems.push_back(aNewElem);
4644 srcElements.push_back( elem );
4647 // set new prev nodes
4648 for ( iNode = 0; iNode < nbNodes; iNode++ )
4649 prevNod[ iNode ] = nextNod[ iNode ];
4654 //=======================================================================
4656 * \brief Create 1D and 2D elements around swept elements
4657 * \param mapNewNodes - source nodes and ones generated from them
4658 * \param newElemsMap - source elements and ones generated from them
4659 * \param elemNewNodesMap - nodes generated from each node of each element
4660 * \param elemSet - all swept elements
4661 * \param nbSteps - number of sweeping steps
4662 * \param srcElements - to append elem for each generated element
4664 //=======================================================================
4666 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4667 TTElemOfElemListMap & newElemsMap,
4668 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4669 TIDSortedElemSet& elemSet,
4671 SMESH_SequenceOfElemPtr& srcElements)
4673 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4674 SMESHDS_Mesh* aMesh = GetMeshDS();
4676 // Find nodes belonging to only one initial element - sweep them into edges.
4678 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4679 for ( ; nList != mapNewNodes.end(); nList++ )
4681 const SMDS_MeshNode* node =
4682 static_cast<const SMDS_MeshNode*>( nList->first );
4683 if ( newElemsMap.count( node ))
4684 continue; // node was extruded into edge
4685 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4686 int nbInitElems = 0;
4687 const SMDS_MeshElement* el = 0;
4688 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4689 while ( eIt->more() && nbInitElems < 2 ) {
4690 const SMDS_MeshElement* e = eIt->next();
4691 SMDSAbs_ElementType type = e->GetType();
4692 if ( type == SMDSAbs_Volume ||
4696 if ( type > highType ) {
4703 if ( nbInitElems == 1 ) {
4704 bool NotCreateEdge = el && el->IsMediumNode(node);
4705 if(!NotCreateEdge) {
4706 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4707 list<const SMDS_MeshElement*> newEdges;
4708 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4713 // Make a ceiling for each element ie an equal element of last new nodes.
4714 // Find free links of faces - make edges and sweep them into faces.
4716 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4718 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4719 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4720 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4722 const SMDS_MeshElement* elem = itElem->first;
4723 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4725 if(itElem->second.size()==0) continue;
4727 const bool isQuadratic = elem->IsQuadratic();
4729 if ( elem->GetType() == SMDSAbs_Edge ) {
4730 // create a ceiling edge
4731 if ( !isQuadratic ) {
4732 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4733 vecNewNodes[ 1 ]->second.back())) {
4734 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4735 vecNewNodes[ 1 ]->second.back()));
4736 srcElements.push_back( elem );
4740 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4741 vecNewNodes[ 1 ]->second.back(),
4742 vecNewNodes[ 2 ]->second.back())) {
4743 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4744 vecNewNodes[ 1 ]->second.back(),
4745 vecNewNodes[ 2 ]->second.back()));
4746 srcElements.push_back( elem );
4750 if ( elem->GetType() != SMDSAbs_Face )
4753 bool hasFreeLinks = false;
4755 TIDSortedElemSet avoidSet;
4756 avoidSet.insert( elem );
4758 set<const SMDS_MeshNode*> aFaceLastNodes;
4759 int iNode, nbNodes = vecNewNodes.size();
4760 if ( !isQuadratic ) {
4761 // loop on the face nodes
4762 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4763 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4764 // look for free links of the face
4765 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4766 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4767 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4768 // check if a link n1-n2 is free
4769 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4770 hasFreeLinks = true;
4771 // make a new edge and a ceiling for a new edge
4772 const SMDS_MeshElement* edge;
4773 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4774 myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4775 srcElements.push_back( myLastCreatedElems.back() );
4777 n1 = vecNewNodes[ iNode ]->second.back();
4778 n2 = vecNewNodes[ iNext ]->second.back();
4779 if ( !aMesh->FindEdge( n1, n2 )) {
4780 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4781 srcElements.push_back( edge );
4786 else { // elem is quadratic face
4787 int nbn = nbNodes/2;
4788 for ( iNode = 0; iNode < nbn; iNode++ ) {
4789 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4790 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4791 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4792 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4793 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4794 // check if a link is free
4795 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4796 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4797 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4798 hasFreeLinks = true;
4799 // make an edge and a ceiling for a new edge
4801 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4802 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4803 srcElements.push_back( elem );
4805 n1 = vecNewNodes[ iNode ]->second.back();
4806 n2 = vecNewNodes[ iNext ]->second.back();
4807 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4808 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4809 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4810 srcElements.push_back( elem );
4814 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4815 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4819 // sweep free links into faces
4821 if ( hasFreeLinks ) {
4822 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4823 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4825 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4826 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4827 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4828 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4829 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4831 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4832 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4833 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4835 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4836 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4837 std::advance( v, volNb );
4838 // find indices of free faces of a volume and their source edges
4839 list< int > freeInd;
4840 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4841 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4842 int iF, nbF = vTool.NbFaces();
4843 for ( iF = 0; iF < nbF; iF ++ ) {
4844 if ( vTool.IsFreeFace( iF ) &&
4845 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4846 initNodeSet != faceNodeSet) // except an initial face
4848 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4850 if ( faceNodeSet == initNodeSetNoCenter )
4852 freeInd.push_back( iF );
4853 // find source edge of a free face iF
4854 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4855 vector<const SMDS_MeshNode*>::iterator lastCommom;
4856 commonNodes.resize( nbNodes, 0 );
4857 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4858 initNodeSet.begin(), initNodeSet.end(),
4859 commonNodes.begin());
4860 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4861 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4863 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4865 if ( !srcEdges.back() )
4867 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4868 << iF << " of volume #" << vTool.ID() << endl;
4873 if ( freeInd.empty() )
4876 // create wall faces for all steps;
4877 // if such a face has been already created by sweep of edge,
4878 // assure that its orientation is OK
4879 for ( int iStep = 0; iStep < nbSteps; iStep++ )
4881 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4882 vTool.SetExternalNormal();
4883 const int nextShift = vTool.IsForward() ? +1 : -1;
4884 list< int >::iterator ind = freeInd.begin();
4885 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4886 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4888 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4889 int nbn = vTool.NbFaceNodes( *ind );
4890 const SMDS_MeshElement * f = 0;
4891 if ( nbn == 3 ) ///// triangle
4893 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4895 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4897 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4899 nodes[ 1 + nextShift ] };
4901 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4903 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4907 else if ( nbn == 4 ) ///// quadrangle
4909 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4911 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4913 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4914 nodes[ 2 ], nodes[ 2+nextShift ] };
4916 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4918 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4919 newOrder[ 2 ], newOrder[ 3 ]));
4922 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4924 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4926 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4928 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4930 nodes[2 + 2*nextShift],
4931 nodes[3 - 2*nextShift],
4933 nodes[3 + 2*nextShift]};
4935 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4937 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4945 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4947 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4948 nodes[1], nodes[3], nodes[5], nodes[7] );
4950 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4952 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4953 nodes[4 - 2*nextShift],
4955 nodes[4 + 2*nextShift],
4957 nodes[5 - 2*nextShift],
4959 nodes[5 + 2*nextShift] };
4961 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4963 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4964 newOrder[ 2 ], newOrder[ 3 ],
4965 newOrder[ 4 ], newOrder[ 5 ],
4966 newOrder[ 6 ], newOrder[ 7 ]));
4969 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4971 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4972 SMDSAbs_Face, /*noMedium=*/false);
4974 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4976 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4977 nodes[4 - 2*nextShift],
4979 nodes[4 + 2*nextShift],
4981 nodes[5 - 2*nextShift],
4983 nodes[5 + 2*nextShift],
4986 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4988 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4989 newOrder[ 2 ], newOrder[ 3 ],
4990 newOrder[ 4 ], newOrder[ 5 ],
4991 newOrder[ 6 ], newOrder[ 7 ],
4995 else //////// polygon
4997 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4998 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5000 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5002 if ( !vTool.IsForward() )
5003 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5005 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5007 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5011 while ( srcElements.size() < myLastCreatedElems.size() )
5012 srcElements.push_back( *srcEdge );
5014 } // loop on free faces
5016 // go to the next volume
5018 while ( iVol++ < nbVolumesByStep ) v++;
5021 } // loop on volumes of one step
5022 } // sweep free links into faces
5024 // Make a ceiling face with a normal external to a volume
5026 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5027 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5028 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5030 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5031 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5032 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5036 lastVol.SetExternalNormal();
5037 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5038 const int nbn = lastVol.NbFaceNodes( iF );
5039 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5040 if ( !hasFreeLinks ||
5041 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5043 const vector<int>& interlace =
5044 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5045 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5047 AddElement( nodeVec, anyFace.Init( elem ));
5049 while ( srcElements.size() < myLastCreatedElems.size() )
5050 srcElements.push_back( elem );
5053 } // loop on swept elements
5056 //=======================================================================
5057 //function : RotationSweep
5059 //=======================================================================
5061 SMESH_MeshEditor::PGroupIDs
5062 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5063 const gp_Ax1& theAxis,
5064 const double theAngle,
5065 const int theNbSteps,
5066 const double theTol,
5067 const bool theMakeGroups,
5068 const bool theMakeWalls)
5072 setElemsFirst( theElemSets );
5073 myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5074 myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5076 // source elements for each generated one
5077 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5078 srcElems.reserve( theElemSets[0].size() );
5079 srcNodes.reserve( theElemSets[1].size() );
5082 aTrsf.SetRotation( theAxis, theAngle );
5084 aTrsf2.SetRotation( theAxis, theAngle/2. );
5086 gp_Lin aLine( theAxis );
5087 double aSqTol = theTol * theTol;
5089 SMESHDS_Mesh* aMesh = GetMeshDS();
5091 TNodeOfNodeListMap mapNewNodes;
5092 TElemOfVecOfNnlmiMap mapElemNewNodes;
5093 TTElemOfElemListMap newElemsMap;
5095 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5096 myMesh->NbFaces(ORDER_QUADRATIC) +
5097 myMesh->NbVolumes(ORDER_QUADRATIC) );
5098 // loop on theElemSets
5099 TIDSortedElemSet::iterator itElem;
5100 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5102 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5103 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5104 const SMDS_MeshElement* elem = *itElem;
5105 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5107 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5108 newNodesItVec.reserve( elem->NbNodes() );
5110 // loop on elem nodes
5111 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5112 while ( itN->more() )
5114 const SMDS_MeshNode* node = cast2Node( itN->next() );
5116 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5118 aXYZ.Coord( coord[0], coord[1], coord[2] );
5119 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5121 // check if a node has been already sweeped
5122 TNodeOfNodeListMapItr nIt =
5123 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5124 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5125 if ( listNewNodes.empty() )
5127 // check if we are to create medium nodes between corner ones
5128 bool needMediumNodes = false;
5129 if ( isQuadraticMesh )
5131 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5132 while (it->more() && !needMediumNodes )
5134 const SMDS_MeshElement* invElem = it->next();
5135 if ( invElem != elem && !theElems.count( invElem )) continue;
5136 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5137 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5138 needMediumNodes = true;
5143 const SMDS_MeshNode * newNode = node;
5144 for ( int i = 0; i < theNbSteps; i++ ) {
5146 if ( needMediumNodes ) // create a medium node
5148 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
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 );
5153 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5156 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5158 // create a corner node
5159 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5160 myLastCreatedNodes.push_back(newNode);
5161 srcNodes.push_back( node );
5162 listNewNodes.push_back( newNode );
5165 listNewNodes.push_back( newNode );
5166 // if ( needMediumNodes )
5167 // listNewNodes.push_back( newNode );
5171 newNodesItVec.push_back( nIt );
5173 // make new elements
5174 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5179 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5181 PGroupIDs newGroupIDs;
5182 if ( theMakeGroups )
5183 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5188 //=======================================================================
5189 //function : ExtrusParam
5190 //purpose : standard construction
5191 //=======================================================================
5193 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5194 const int theNbSteps,
5195 const std::list<double>& theScales,
5196 const std::list<double>& theAngles,
5197 const gp_XYZ* theBasePoint,
5199 const double theTolerance):
5201 myBaseP( Precision::Infinite(), 0, 0 ),
5202 myFlags( theFlags ),
5203 myTolerance( theTolerance ),
5204 myElemsToUse( NULL )
5206 mySteps = new TColStd_HSequenceOfReal;
5207 const double stepSize = theStep.Magnitude();
5208 for (int i=1; i<=theNbSteps; i++ )
5209 mySteps->Append( stepSize );
5211 if ( !theScales.empty() )
5213 if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5214 linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5216 // add medium scales
5217 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5218 myScales.reserve( theNbSteps * 2 );
5219 myScales.push_back( 0.5 * ( *s1 + 1. ));
5220 myScales.push_back( *s1 );
5221 for ( ; s2 != theScales.end(); s1 = s2++ )
5223 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5224 myScales.push_back( *s2 );
5228 if ( !theAngles.empty() )
5230 std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5231 if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5232 linearAngleVariation( theNbSteps, angles );
5234 // accumulate angles
5237 std::list<double>::iterator a1 = angles.begin(), a2;
5238 for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5243 while ( nbAngles++ < theNbSteps )
5244 angles.push_back( angles.back() );
5246 // add medium angles
5247 a2 = angles.begin(), a1 = a2++;
5248 myAngles.push_back( 0.5 * *a1 );
5249 myAngles.push_back( *a1 );
5250 for ( ; a2 != angles.end(); a1 = a2++ )
5252 myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5253 myAngles.push_back( *a2 );
5259 myBaseP = *theBasePoint;
5262 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5263 ( theTolerance > 0 ))
5265 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5269 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5273 //=======================================================================
5274 //function : ExtrusParam
5275 //purpose : steps are given explicitly
5276 //=======================================================================
5278 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5279 Handle(TColStd_HSequenceOfReal) theSteps,
5281 const double theTolerance):
5283 mySteps( theSteps ),
5284 myFlags( theFlags ),
5285 myTolerance( theTolerance ),
5286 myElemsToUse( NULL )
5288 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5289 ( theTolerance > 0 ))
5291 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5295 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5299 //=======================================================================
5300 //function : ExtrusParam
5301 //purpose : for extrusion by normal
5302 //=======================================================================
5304 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5305 const int theNbSteps,
5309 mySteps( new TColStd_HSequenceOfReal ),
5310 myFlags( theFlags ),
5312 myElemsToUse( NULL )
5314 for (int i = 0; i < theNbSteps; i++ )
5315 mySteps->Append( theStepSize );
5319 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5323 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5327 //=======================================================================
5328 //function : ExtrusParam
5329 //purpose : for extrusion along path
5330 //=======================================================================
5332 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5333 const gp_Pnt* theBasePoint,
5334 const std::list<double>& theScales,
5335 const bool theMakeGroups )
5336 : myBaseP( Precision::Infinite(), 0, 0 ),
5337 myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5338 myPathPoints( thePoints )
5342 myBaseP = theBasePoint->XYZ();
5345 if ( !theScales.empty() )
5347 // add medium scales
5348 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5349 myScales.reserve( thePoints.size() * 2 );
5350 myScales.push_back( 0.5 * ( 1. + *s1 ));
5351 myScales.push_back( *s1 );
5352 for ( ; s2 != theScales.end(); s1 = s2++ )
5354 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5355 myScales.push_back( *s2 );
5359 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5362 //=======================================================================
5363 //function : ExtrusParam::SetElementsToUse
5364 //purpose : stores elements to use for extrusion by normal, depending on
5365 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5366 // define myBaseP for scaling
5367 //=======================================================================
5369 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5370 const TIDSortedElemSet& nodes )
5372 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5374 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5376 myBaseP.SetCoord( 0.,0.,0. );
5377 TIDSortedElemSet newNodes;
5379 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5380 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5382 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5383 TIDSortedElemSet::const_iterator itElem = elements.begin();
5384 for ( ; itElem != elements.end(); itElem++ )
5386 const SMDS_MeshElement* elem = *itElem;
5387 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5388 while ( itN->more() ) {
5389 const SMDS_MeshElement* node = itN->next();
5390 if ( newNodes.insert( node ).second )
5391 myBaseP += SMESH_NodeXYZ( node );
5395 myBaseP /= newNodes.size();
5399 //=======================================================================
5400 //function : ExtrusParam::beginStepIter
5401 //purpose : prepare iteration on steps
5402 //=======================================================================
5404 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5406 myWithMediumNodes = withMediumNodes;
5410 //=======================================================================
5411 //function : ExtrusParam::moreSteps
5412 //purpose : are there more steps?
5413 //=======================================================================
5415 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5417 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5419 //=======================================================================
5420 //function : ExtrusParam::nextStep
5421 //purpose : returns the next step
5422 //=======================================================================
5424 double SMESH_MeshEditor::ExtrusParam::nextStep()
5427 if ( !myCurSteps.empty() )
5429 res = myCurSteps.back();
5430 myCurSteps.pop_back();
5432 else if ( myNextStep <= mySteps->Length() )
5434 myCurSteps.push_back( mySteps->Value( myNextStep ));
5436 if ( myWithMediumNodes )
5438 myCurSteps.back() /= 2.;
5439 myCurSteps.push_back( myCurSteps.back() );
5446 //=======================================================================
5447 //function : ExtrusParam::makeNodesByDir
5448 //purpose : create nodes for standard extrusion
5449 //=======================================================================
5451 int SMESH_MeshEditor::ExtrusParam::
5452 makeNodesByDir( SMESHDS_Mesh* mesh,
5453 const SMDS_MeshNode* srcNode,
5454 std::list<const SMDS_MeshNode*> & newNodes,
5455 const bool makeMediumNodes)
5457 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5460 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5462 p += myDir.XYZ() * nextStep();
5463 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5464 newNodes.push_back( newNode );
5467 if ( !myScales.empty() || !myAngles.empty() )
5469 gp_XYZ center = myBaseP;
5470 gp_Ax1 ratationAxis( center, myDir );
5473 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5474 size_t i = !makeMediumNodes;
5475 for ( beginStepIter( makeMediumNodes );
5477 ++nIt, i += 1 + !makeMediumNodes )
5479 center += myDir.XYZ() * nextStep();
5481 gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5483 if ( i < myScales.size() )
5485 xyz = ( myScales[i] * ( xyz - center )) + center;
5488 if ( !myAngles.empty() )
5490 rotation.SetRotation( ratationAxis, myAngles[i] );
5491 rotation.Transforms( xyz );
5495 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5503 //=======================================================================
5504 //function : ExtrusParam::makeNodesByDirAndSew
5505 //purpose : create nodes for standard extrusion with sewing
5506 //=======================================================================
5508 int SMESH_MeshEditor::ExtrusParam::
5509 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5510 const SMDS_MeshNode* srcNode,
5511 std::list<const SMDS_MeshNode*> & newNodes,
5512 const bool makeMediumNodes)
5514 gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5517 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5519 P1 += myDir.XYZ() * nextStep();
5521 // try to search in sequence of existing nodes
5522 // if myNodes.size()>0 we 'nave to use given sequence
5523 // else - use all nodes of mesh
5524 const SMDS_MeshNode * node = 0;
5525 if ( myNodes.Length() > 0 )
5527 for ( int i = 1; i <= myNodes.Length(); i++ )
5529 SMESH_NodeXYZ P2 = myNodes.Value(i);
5530 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5532 node = myNodes.Value(i);
5539 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5542 SMESH_NodeXYZ P2 = itn->next();
5543 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5552 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5554 newNodes.push_back( node );
5561 //=======================================================================
5562 //function : ExtrusParam::makeNodesByNormal2D
5563 //purpose : create nodes for extrusion using normals of faces
5564 //=======================================================================
5566 int SMESH_MeshEditor::ExtrusParam::
5567 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5568 const SMDS_MeshNode* srcNode,
5569 std::list<const SMDS_MeshNode*> & newNodes,
5570 const bool makeMediumNodes)
5572 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5574 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5576 // get normals to faces sharing srcNode
5577 vector< gp_XYZ > norms, baryCenters;
5578 gp_XYZ norm, avgNorm( 0,0,0 );
5579 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5580 while ( faceIt->more() )
5582 const SMDS_MeshElement* face = faceIt->next();
5583 if ( myElemsToUse && !myElemsToUse->count( face ))
5585 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5587 norms.push_back( norm );
5589 if ( !alongAvgNorm )
5593 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5594 bc += SMESH_NodeXYZ( nIt->next() );
5595 baryCenters.push_back( bc / nbN );
5600 if ( norms.empty() ) return 0;
5602 double normSize = avgNorm.Modulus();
5603 if ( normSize < std::numeric_limits<double>::min() )
5606 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5609 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5612 avgNorm /= normSize;
5615 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5618 double stepSize = nextStep();
5620 if ( norms.size() > 1 )
5622 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5624 // translate plane of a face
5625 baryCenters[ iF ] += norms[ iF ] * stepSize;
5627 // find point of intersection of the face plane located at baryCenters[ iF ]
5628 // and avgNorm located at pNew
5629 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5630 double dot = ( norms[ iF ] * avgNorm );
5631 if ( dot < std::numeric_limits<double>::min() )
5632 dot = stepSize * 1e-3;
5633 double step = -( norms[ iF ] * pNew + d ) / dot;
5634 pNew += step * avgNorm;
5639 pNew += stepSize * avgNorm;
5643 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5644 newNodes.push_back( newNode );
5649 //=======================================================================
5650 //function : ExtrusParam::makeNodesByNormal1D
5651 //purpose : create nodes for extrusion using normals of edges
5652 //=======================================================================
5654 int SMESH_MeshEditor::ExtrusParam::
5655 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5656 const SMDS_MeshNode* srcNode,
5657 std::list<const SMDS_MeshNode*> & newNodes,
5658 const bool makeMediumNodes)
5660 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5664 //=======================================================================
5665 //function : ExtrusParam::makeNodesAlongTrack
5666 //purpose : create nodes for extrusion along path
5667 //=======================================================================
5669 int SMESH_MeshEditor::ExtrusParam::
5670 makeNodesAlongTrack( SMESHDS_Mesh* mesh,
5671 const SMDS_MeshNode* srcNode,
5672 std::list<const SMDS_MeshNode*> & newNodes,
5673 const bool makeMediumNodes)
5675 const Standard_Real aTolAng=1.e-4;
5677 gp_Pnt aV0x = myBaseP;
5678 gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
5680 const PathPoint& aPP0 = myPathPoints[0];
5681 gp_Pnt aP0x = aPP0.myPnt;
5682 gp_Dir aDT0x= aPP0.myTgt;
5684 std::vector< gp_Pnt > centers;
5685 centers.reserve( NbSteps() * 2 );
5687 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5689 for ( size_t j = 1; j < myPathPoints.size(); ++j )
5691 const PathPoint& aPP = myPathPoints[j];
5692 const gp_Pnt& aP1x = aPP.myPnt;
5693 const gp_Dir& aDT1x = aPP.myTgt;
5696 gp_Vec aV01x( aP0x, aP1x );
5697 aTrsf.SetTranslation( aV01x );
5698 gp_Pnt aV1x = aV0x.Transformed( aTrsf );
5699 gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
5701 // rotation 1 [ T1,T0 ]
5702 Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
5703 if ( fabs( aAngleT1T0 ) > aTolAng )
5705 gp_Dir aDT1T0 = aDT1x ^ aDT0x;
5706 aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
5708 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5712 if ( aPP.myAngle != 0. )
5714 aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
5715 aPN1 = aPN1.Transformed( aTrsfRot );
5719 if ( makeMediumNodes )
5721 // create additional node
5722 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
5723 const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
5724 newNodes.push_back( newNode );
5727 const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5728 newNodes.push_back( newNode );
5730 centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
5731 centers.push_back( aV1x );
5740 if ( !myScales.empty() )
5743 std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
5744 for ( size_t i = !makeMediumNodes;
5745 i < myScales.size() && node != newNodes.end();
5746 i += ( 1 + !makeMediumNodes ), ++node )
5748 aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
5749 gp_Pnt aN = SMESH_NodeXYZ( *node );
5750 gp_Pnt aP = aN.Transformed( aTrsfScale );
5751 mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
5755 return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
5758 //=======================================================================
5759 //function : ExtrusionSweep
5761 //=======================================================================
5763 SMESH_MeshEditor::PGroupIDs
5764 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5765 const gp_Vec& theStep,
5766 const int theNbSteps,
5767 TTElemOfElemListMap& newElemsMap,
5769 const double theTolerance)
5771 std::list<double> dummy;
5772 ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
5773 theFlags, theTolerance );
5774 return ExtrusionSweep( theElems, aParams, newElemsMap );
5780 //=======================================================================
5781 //function : getOriFactor
5782 //purpose : Return -1 or 1 depending on if order of given nodes corresponds to
5783 // edge curve orientation
5784 //=======================================================================
5786 double getOriFactor( const TopoDS_Edge& edge,
5787 const SMDS_MeshNode* n1,
5788 const SMDS_MeshNode* n2,
5789 SMESH_MesherHelper& helper)
5791 double u1 = helper.GetNodeU( edge, n1, n2 );
5792 double u2 = helper.GetNodeU( edge, n2, n1 );
5793 return u1 < u2 ? 1. : -1.;
5797 //=======================================================================
5798 //function : ExtrusionSweep
5800 //=======================================================================
5802 SMESH_MeshEditor::PGroupIDs
5803 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5804 ExtrusParam& theParams,
5805 TTElemOfElemListMap& newElemsMap)
5809 setElemsFirst( theElemSets );
5810 myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5811 myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5813 // source elements for each generated one
5814 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5815 srcElems.reserve( theElemSets[0].size() );
5816 srcNodes.reserve( theElemSets[1].size() );
5818 const int nbSteps = theParams.NbSteps();
5819 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5821 TNodeOfNodeListMap mapNewNodes;
5822 TElemOfVecOfNnlmiMap mapElemNewNodes;
5824 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5825 myMesh->NbFaces(ORDER_QUADRATIC) +
5826 myMesh->NbVolumes(ORDER_QUADRATIC) );
5828 TIDSortedElemSet::iterator itElem;
5829 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5831 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5832 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5834 // check element type
5835 const SMDS_MeshElement* elem = *itElem;
5836 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5839 const size_t nbNodes = elem->NbNodes();
5840 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5841 newNodesItVec.reserve( nbNodes );
5843 // loop on elem nodes
5844 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
5845 while ( itN->more() )
5847 // check if a node has been already sweeped
5848 const SMDS_MeshNode* node = itN->next();
5849 TNodeOfNodeListMap::iterator nIt =
5850 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5851 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5852 if ( listNewNodes.empty() )
5856 // check if we are to create medium nodes between corner ones
5857 bool needMediumNodes = false;
5858 if ( isQuadraticMesh )
5860 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5861 while (it->more() && !needMediumNodes )
5863 const SMDS_MeshElement* invElem = it->next();
5864 if ( invElem != elem && !theElems.count( invElem )) continue;
5865 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5866 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5867 needMediumNodes = true;
5870 // create nodes for all steps
5871 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5873 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5874 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5876 myLastCreatedNodes.push_back( *newNodesIt );
5877 srcNodes.push_back( node );
5882 if ( theParams.ToMakeBoundary() )
5884 GetMeshDS()->Modified();
5885 throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
5887 break; // newNodesItVec will be shorter than nbNodes
5890 newNodesItVec.push_back( nIt );
5892 // make new elements
5893 if ( newNodesItVec.size() == nbNodes )
5894 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5898 if ( theParams.ToMakeBoundary() ) {
5899 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5901 PGroupIDs newGroupIDs;
5902 if ( theParams.ToMakeGroups() )
5903 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5908 //=======================================================================
5909 //function : ExtrusionAlongTrack
5911 //=======================================================================
5912 SMESH_MeshEditor::Extrusion_Error
5913 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5914 SMESH_Mesh* theTrackMesh,
5915 SMDS_ElemIteratorPtr theTrackIterator,
5916 const SMDS_MeshNode* theN1,
5917 std::list<double>& theAngles,
5918 const bool theAngleVariation,
5919 std::list<double>& theScales,
5920 const bool theScaleVariation,
5921 const gp_Pnt* theRefPoint,
5922 const bool theMakeGroups)
5927 if ( theElements[0].empty() && theElements[1].empty() )
5928 return EXTR_NO_ELEMENTS;
5930 ASSERT( theTrackMesh );
5931 if ( ! theTrackIterator || !theTrackIterator->more() )
5932 return EXTR_NO_ELEMENTS;
5934 // 2. Get ordered nodes
5935 SMESH_MeshAlgos::TElemGroupVector branchEdges;
5936 SMESH_MeshAlgos::TNodeGroupVector branchNods;
5937 SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
5938 if ( branchEdges.empty() )
5939 return EXTR_PATH_NOT_EDGE;
5941 if ( branchEdges.size() > 1 )
5942 return EXTR_BAD_PATH_SHAPE;
5944 std::vector< const SMDS_MeshNode* >& pathNodes = branchNods[0];
5945 std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
5946 if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
5947 return EXTR_BAD_STARTING_NODE;
5949 if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
5951 // add medium nodes to pathNodes
5952 std::vector< const SMDS_MeshNode* > pathNodes2;
5953 std::vector< const SMDS_MeshElement* > pathEdges2;
5954 pathNodes2.reserve( pathNodes.size() * 2 );
5955 pathEdges2.reserve( pathEdges.size() * 2 );
5956 for ( size_t i = 0; i < pathEdges.size(); ++i )
5958 pathNodes2.push_back( pathNodes[i] );
5959 pathEdges2.push_back( pathEdges[i] );
5960 if ( pathEdges[i]->IsQuadratic() )
5962 pathNodes2.push_back( pathEdges[i]->GetNode(2) );
5963 pathEdges2.push_back( pathEdges[i] );
5966 pathNodes2.push_back( pathNodes.back() );
5967 pathEdges.swap( pathEdges2 );
5968 pathNodes.swap( pathNodes2 );
5971 // 3. Get path data at pathNodes
5973 std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
5975 if ( theAngleVariation )
5976 linearAngleVariation( points.size()-1, theAngles );
5977 if ( theScaleVariation )
5978 linearScaleVariation( points.size()-1, theScales );
5980 theAngles.push_front( 0 ); // for the 1st point that is not transformed
5981 std::list<double>::iterator angle = theAngles.begin();
5983 SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
5985 std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
5986 std::map< int, double >::iterator id2factor;
5987 SMESH_MesherHelper pathHelper( *theTrackMesh );
5988 gp_Pnt p; gp_Vec tangent;
5989 const double tol2 = gp::Resolution() * gp::Resolution();
5991 for ( size_t i = 0; i < pathNodes.size(); ++i )
5993 ExtrusParam::PathPoint & point = points[ i ];
5995 point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
5997 if ( angle != theAngles.end() )
5998 point.myAngle = *angle++;
6000 tangent.SetCoord( 0,0,0 );
6001 const int shapeID = pathNodes[ i ]->GetShapeID();
6002 const TopoDS_Shape& shape = pathMeshDS->IndexToShape( shapeID );
6003 TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
6004 switch ( shapeType )
6008 TopoDS_Edge edge = TopoDS::Edge( shape );
6009 id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
6010 if ( id2factor->second == 0 )
6012 if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6013 else id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6015 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6016 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6017 curve->D1( u, p, tangent );
6018 tangent *= id2factor->second;
6024 PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6025 while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6027 int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6028 for ( int di = -1; di <= 0; ++di )
6031 if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6033 TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6034 id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6035 if ( id2factor->second == 0 )
6038 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6040 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6042 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6043 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6045 curve->D1( u, p, du );
6046 double size2 = du.SquareMagnitude();
6047 if ( du.SquareMagnitude() > tol2 )
6049 tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6061 for ( int di = -1; di <= 1; di += 2 )
6064 if ( j < pathNodes.size() )
6066 gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6067 double size2 = dir.SquareMagnitude();
6069 tangent += dir.Divided( Sqrt( size2 )) * di;
6073 } // switch ( shapeType )
6075 if ( tangent.SquareMagnitude() < tol2 )
6076 return EXTR_CANT_GET_TANGENT;
6078 point.myTgt = tangent;
6080 } // loop on pathNodes
6083 ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6084 TTElemOfElemListMap newElemsMap;
6086 ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6091 //=======================================================================
6092 //function : linearAngleVariation
6093 //purpose : spread values over nbSteps
6094 //=======================================================================
6096 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6097 list<double>& Angles)
6099 int nbAngles = Angles.size();
6100 if( nbSteps > nbAngles && nbAngles > 0 )
6102 vector<double> theAngles(nbAngles);
6103 theAngles.assign( Angles.begin(), Angles.end() );
6106 double rAn2St = double( nbAngles ) / double( nbSteps );
6107 double angPrev = 0, angle;
6108 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6110 double angCur = rAn2St * ( iSt+1 );
6111 double angCurFloor = floor( angCur );
6112 double angPrevFloor = floor( angPrev );
6113 if ( angPrevFloor == angCurFloor )
6114 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6116 int iP = int( angPrevFloor );
6117 double angPrevCeil = ceil(angPrev);
6118 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6120 int iC = int( angCurFloor );
6121 if ( iC < nbAngles )
6122 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6124 iP = int( angPrevCeil );
6126 angle += theAngles[ iC ];
6128 res.push_back(angle);
6135 //=======================================================================
6136 //function : linearScaleVariation
6137 //purpose : spread values over nbSteps
6138 //=======================================================================
6140 void SMESH_MeshEditor::linearScaleVariation(const int theNbSteps,
6141 std::list<double>& theScales)
6143 int nbScales = theScales.size();
6144 std::vector<double> myScales;
6145 myScales.reserve( theNbSteps );
6146 std::list<double>::const_iterator scale = theScales.begin();
6147 double prevScale = 1.0;
6148 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6150 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6151 int stDelta = Max( 1, iStep - myScales.size());
6152 double scDelta = ( *scale - prevScale ) / stDelta;
6153 for ( int iStep = 0; iStep < stDelta; ++iStep )
6155 myScales.push_back( prevScale + scDelta );
6156 prevScale = myScales.back();
6160 theScales.assign( myScales.begin(), myScales.end() );
6163 //================================================================================
6165 * \brief Move or copy theElements applying theTrsf to their nodes
6166 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6167 * \param theTrsf - transformation to apply
6168 * \param theCopy - if true, create translated copies of theElems
6169 * \param theMakeGroups - if true and theCopy, create translated groups
6170 * \param theTargetMesh - mesh to copy translated elements into
6171 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6173 //================================================================================
6175 SMESH_MeshEditor::PGroupIDs
6176 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6177 const gp_Trsf& theTrsf,
6179 const bool theMakeGroups,
6180 SMESH_Mesh* theTargetMesh)
6183 myLastCreatedElems.reserve( theElems.size() );
6185 bool needReverse = false;
6186 string groupPostfix;
6187 switch ( theTrsf.Form() ) {
6190 groupPostfix = "mirrored";
6193 groupPostfix = "mirrored";
6197 groupPostfix = "mirrored";
6200 groupPostfix = "rotated";
6202 case gp_Translation:
6203 groupPostfix = "translated";
6206 groupPostfix = "scaled";
6208 case gp_CompoundTrsf: // different scale by axis
6209 groupPostfix = "scaled";
6212 needReverse = false;
6213 groupPostfix = "transformed";
6216 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6217 SMESHDS_Mesh* aMesh = GetMeshDS();
6219 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6220 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6221 SMESH_MeshEditor::ElemFeatures elemType;
6223 // map old node to new one
6224 TNodeNodeMap nodeMap;
6226 // elements sharing moved nodes; those of them which have all
6227 // nodes mirrored but are not in theElems are to be reversed
6228 TIDSortedElemSet inverseElemSet;
6230 // source elements for each generated one
6231 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6233 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6234 TIDSortedElemSet orphanNode;
6236 if ( theElems.empty() ) // transform the whole mesh
6239 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6240 while ( eIt->more() ) theElems.insert( eIt->next() );
6242 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6243 while ( nIt->more() )
6245 const SMDS_MeshNode* node = nIt->next();
6246 if ( node->NbInverseElements() == 0)
6247 orphanNode.insert( node );
6251 // loop on elements to transform nodes : first orphan nodes then elems
6252 TIDSortedElemSet::iterator itElem;
6253 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6254 for (int i=0; i<2; i++)
6255 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6257 const SMDS_MeshElement* elem = *itElem;
6261 // loop on elem nodes
6263 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6264 while ( itN->more() )
6266 const SMDS_MeshNode* node = cast2Node( itN->next() );
6267 // check if a node has been already transformed
6268 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6269 nodeMap.insert( make_pair ( node, node ));
6270 if ( !n2n_isnew.second )
6273 node->GetXYZ( coord );
6274 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6275 if ( theTargetMesh ) {
6276 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6277 n2n_isnew.first->second = newNode;
6278 myLastCreatedNodes.push_back(newNode);
6279 srcNodes.push_back( node );
6281 else if ( theCopy ) {
6282 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6283 n2n_isnew.first->second = newNode;
6284 myLastCreatedNodes.push_back(newNode);
6285 srcNodes.push_back( node );
6288 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6289 // node position on shape becomes invalid
6290 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6291 ( SMDS_SpacePosition::originSpacePosition() );
6294 // keep inverse elements
6295 if ( !theCopy && !theTargetMesh && needReverse ) {
6296 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6297 while ( invElemIt->more() ) {
6298 const SMDS_MeshElement* iel = invElemIt->next();
6299 inverseElemSet.insert( iel );
6303 } // loop on elems in { &orphanNode, &theElems };
6305 // either create new elements or reverse mirrored ones
6306 if ( !theCopy && !needReverse && !theTargetMesh )
6309 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6311 // Replicate or reverse elements
6313 std::vector<int> iForw;
6314 vector<const SMDS_MeshNode*> nodes;
6315 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6317 const SMDS_MeshElement* elem = *itElem;
6318 if ( !elem ) continue;
6320 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6321 size_t nbNodes = elem->NbNodes();
6322 if ( geomType == SMDSGeom_NONE ) continue; // node
6324 nodes.resize( nbNodes );
6326 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6328 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6332 bool allTransformed = true;
6333 int nbFaces = aPolyedre->NbFaces();
6334 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6336 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6337 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6339 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6340 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6341 if ( nodeMapIt == nodeMap.end() )
6342 allTransformed = false; // not all nodes transformed
6344 nodes.push_back((*nodeMapIt).second);
6346 if ( needReverse && allTransformed )
6347 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6349 if ( !allTransformed )
6350 continue; // not all nodes transformed
6352 else // ----------------------- the rest element types
6354 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6355 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6356 const vector<int>& i = needReverse ? iRev : iForw;
6358 // find transformed nodes
6360 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6361 while ( itN->more() ) {
6362 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6363 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6364 if ( nodeMapIt == nodeMap.end() )
6365 break; // not all nodes transformed
6366 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6368 if ( iNode != nbNodes )
6369 continue; // not all nodes transformed
6373 // copy in this or a new mesh
6374 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6375 srcElems.push_back( elem );
6378 // reverse element as it was reversed by transformation
6380 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6383 } // loop on elements
6385 if ( editor && editor != this )
6386 myLastCreatedElems.swap( editor->myLastCreatedElems );
6388 PGroupIDs newGroupIDs;
6390 if ( ( theMakeGroups && theCopy ) ||
6391 ( theMakeGroups && theTargetMesh ) )
6392 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6397 //================================================================================
6399 * \brief Make an offset mesh from a source 2D mesh
6400 * \param [in] theElements - source faces
6401 * \param [in] theValue - offset value
6402 * \param [out] theTgtMesh - a mesh to add offset elements to
6403 * \param [in] theMakeGroups - to generate groups
6404 * \return PGroupIDs - IDs of created groups. NULL means failure
6406 //================================================================================
6408 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6409 const double theValue,
6410 SMESH_Mesh* theTgtMesh,
6411 const bool theMakeGroups,
6412 const bool theCopyElements,
6413 const bool theFixSelfIntersection)
6415 SMESHDS_Mesh* meshDS = GetMeshDS();
6416 SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6417 SMESH_MeshEditor tgtEditor( theTgtMesh );
6419 SMDS_ElemIteratorPtr eIt;
6420 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6421 else eIt = SMESHUtils::elemSetIterator( theElements );
6423 SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6424 SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6425 std::unique_ptr< SMDS_Mesh > offsetMesh
6426 ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6427 theFixSelfIntersection,
6428 new2OldFaces, new2OldNodes ));
6429 if ( offsetMesh->NbElements() == 0 )
6430 return PGroupIDs(); // MakeOffset() failed
6433 if ( theTgtMesh == myMesh && !theCopyElements )
6435 // clear the source elements
6436 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6437 else eIt = SMESHUtils::elemSetIterator( theElements );
6438 while ( eIt->more() )
6439 meshDS->RemoveFreeElement( eIt->next(), 0 );
6442 // offsetMesh->Modified();
6443 // offsetMesh->CompactMesh(); // make IDs start from 1
6445 // source elements for each generated one
6446 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6447 srcElems.reserve( new2OldFaces.size() );
6448 srcNodes.reserve( new2OldNodes.size() );
6451 myLastCreatedElems.reserve( new2OldFaces.size() );
6452 myLastCreatedNodes.reserve( new2OldNodes.size() );
6454 // copy offsetMesh to theTgtMesh
6456 int idShift = meshDS->MaxNodeID();
6457 for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6458 if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6461 if ( n->NbInverseElements() > 0 )
6464 const SMDS_MeshNode* n2 =
6465 tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6466 myLastCreatedNodes.push_back( n2 );
6467 srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6471 ElemFeatures elemType;
6472 for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6473 if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6476 elemType.myNodes.clear();
6477 for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6479 const SMDS_MeshNode* n2 = nIt->next();
6480 elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6482 tgtEditor.AddElement( elemType.myNodes, elemType );
6483 srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6486 myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6488 PGroupIDs newGroupIDs;
6489 if ( theMakeGroups )
6490 newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6492 newGroupIDs.reset( new std::list< int > );
6497 //=======================================================================
6499 * \brief Create groups of elements made during transformation
6500 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6501 * \param elemGens - elements making corresponding myLastCreatedElems
6502 * \param postfix - to push_back to names of new groups
6503 * \param targetMesh - mesh to create groups in
6504 * \param topPresent - is there are "top" elements that are created by sweeping
6506 //=======================================================================
6508 SMESH_MeshEditor::PGroupIDs
6509 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6510 const SMESH_SequenceOfElemPtr& elemGens,
6511 const std::string& postfix,
6512 SMESH_Mesh* targetMesh,
6513 const bool topPresent)
6515 PGroupIDs newGroupIDs( new list<int> );
6516 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6518 // Sort existing groups by types and collect their names
6520 // containers to store an old group and generated new ones;
6521 // 1st new group is for result elems of different type than a source one;
6522 // 2nd new group is for same type result elems ("top" group at extrusion)
6524 using boost::make_tuple;
6525 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6526 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6527 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6529 set< string > groupNames;
6531 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6532 if ( !groupIt->more() ) return newGroupIDs;
6534 int newGroupID = mesh->GetGroupIds().back()+1;
6535 while ( groupIt->more() )
6537 SMESH_Group * group = groupIt->next();
6538 if ( !group ) continue;
6539 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6540 if ( !groupDS || groupDS->IsEmpty() ) continue;
6541 groupNames.insert ( group->GetName() );
6542 groupDS->SetStoreName( group->GetName() );
6543 const SMDSAbs_ElementType type = groupDS->GetType();
6544 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6545 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6546 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6547 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6550 // Loop on nodes and elements to add them in new groups
6552 vector< const SMDS_MeshElement* > resultElems;
6553 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6555 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6556 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6557 if ( gens.size() != elems.size() )
6558 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6560 // loop on created elements
6561 for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6563 const SMDS_MeshElement* sourceElem = gens[ iElem ];
6564 if ( !sourceElem ) {
6565 MESSAGE("generateGroups(): NULL source element");
6568 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6569 if ( groupsOldNew.empty() ) { // no groups of this type at all
6570 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6571 ++iElem; // skip all elements made by sourceElem
6574 // collect all elements made by the iElem-th sourceElem
6575 resultElems.clear();
6576 if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6577 if ( resElem != sourceElem )
6578 resultElems.push_back( resElem );
6579 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6580 if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6581 if ( resElem != sourceElem )
6582 resultElems.push_back( resElem );
6584 const SMDS_MeshElement* topElem = 0;
6585 if ( isNodes ) // there must be a top element
6587 topElem = resultElems.back();
6588 resultElems.pop_back();
6592 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6593 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6594 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6596 topElem = *resElemIt;
6597 *resElemIt = 0; // erase *resElemIt
6601 // add resultElems to groups originted from ones the sourceElem belongs to
6602 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6603 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6605 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6606 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6608 // fill in a new group
6609 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6610 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6611 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6613 newGroup.Add( *resElemIt );
6615 // fill a "top" group
6618 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6619 newTopGroup.Add( topElem );
6623 } // loop on created elements
6624 }// loop on nodes and elements
6626 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6628 list<int> topGrouIds;
6629 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6631 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6632 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6633 orderedOldNewGroups[i]->get<2>() };
6634 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6636 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6637 if ( newGroupDS->IsEmpty() )
6639 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6644 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6647 const bool isTop = ( topPresent &&
6648 newGroupDS->GetType() == oldGroupDS->GetType() &&
6651 string name = oldGroupDS->GetStoreName();
6652 { // remove trailing whitespaces (issue 22599)
6653 size_t size = name.size();
6654 while ( size > 1 && isspace( name[ size-1 ]))
6656 if ( size != name.size() )
6658 name.resize( size );
6659 oldGroupDS->SetStoreName( name.c_str() );
6662 if ( !targetMesh ) {
6663 string suffix = ( isTop ? "top": postfix.c_str() );
6667 while ( !groupNames.insert( name ).second ) // name exists
6668 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6673 newGroupDS->SetStoreName( name.c_str() );
6675 // make a SMESH_Groups
6676 mesh->AddGroup( newGroupDS );
6678 topGrouIds.push_back( newGroupDS->GetID() );
6680 newGroupIDs->push_back( newGroupDS->GetID() );
6684 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6689 //================================================================================
6691 * * \brief Return list of group of nodes close to each other within theTolerance
6692 * * Search among theNodes or in the whole mesh if theNodes is empty using
6693 * * an Octree algorithm
6694 * \param [in,out] theNodes - the nodes to treat
6695 * \param [in] theTolerance - the tolerance
6696 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
6697 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
6698 * corner and medium nodes in separate groups
6700 //================================================================================
6702 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6703 const double theTolerance,
6704 TListOfListOfNodes & theGroupsOfNodes,
6705 bool theSeparateCornersAndMedium)
6709 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
6710 myMesh->NbFaces ( ORDER_QUADRATIC ) +
6711 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
6712 theSeparateCornersAndMedium = false;
6714 TIDSortedNodeSet& corners = theNodes;
6715 TIDSortedNodeSet medium;
6717 if ( theNodes.empty() ) // get all nodes in the mesh
6719 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
6720 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
6721 if ( theSeparateCornersAndMedium )
6722 while ( nIt->more() )
6724 const SMDS_MeshNode* n = nIt->next();
6725 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
6726 nodeSet->insert( nodeSet->end(), n );
6729 while ( nIt->more() )
6730 theNodes.insert( theNodes.end(), nIt->next() );
6732 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
6734 TIDSortedNodeSet::iterator nIt = corners.begin();
6735 while ( nIt != corners.end() )
6736 if ( SMESH_MesherHelper::IsMedium( *nIt ))
6738 medium.insert( medium.end(), *nIt );
6739 corners.erase( nIt++ );
6747 if ( !corners.empty() )
6748 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
6749 if ( !medium.empty() )
6750 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
6753 //=======================================================================
6754 //function : SimplifyFace
6755 //purpose : split a chain of nodes into several closed chains
6756 //=======================================================================
6758 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6759 vector<const SMDS_MeshNode *>& poly_nodes,
6760 vector<int>& quantities) const
6762 int nbNodes = faceNodes.size();
6763 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
6767 size_t prevNbQuant = quantities.size();
6769 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
6770 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
6771 map< const SMDS_MeshNode*, int >::iterator nInd;
6773 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
6774 simpleNodes.push_back( faceNodes[0] );
6775 for ( int iCur = 1; iCur < nbNodes; iCur++ )
6777 if ( faceNodes[ iCur ] != simpleNodes.back() )
6779 int index = simpleNodes.size();
6780 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
6781 int prevIndex = nInd->second;
6782 if ( prevIndex < index )
6785 int loopLen = index - prevIndex;
6788 // store the sub-loop
6789 quantities.push_back( loopLen );
6790 for ( int i = prevIndex; i < index; i++ )
6791 poly_nodes.push_back( simpleNodes[ i ]);
6793 simpleNodes.resize( prevIndex+1 );
6797 simpleNodes.push_back( faceNodes[ iCur ]);
6802 if ( simpleNodes.size() > 2 )
6804 quantities.push_back( simpleNodes.size() );
6805 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
6808 return quantities.size() - prevNbQuant;
6811 //=======================================================================
6812 //function : MergeNodes
6813 //purpose : In each group, the cdr of nodes are substituted by the first one
6815 //=======================================================================
6817 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
6818 const bool theAvoidMakingHoles)
6822 SMESHDS_Mesh* mesh = GetMeshDS();
6824 TNodeNodeMap nodeNodeMap; // node to replace - new node
6825 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6826 list< int > rmElemIds, rmNodeIds;
6827 vector< ElemFeatures > newElemDefs;
6829 // Fill nodeNodeMap and elems
6831 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6832 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
6834 list<const SMDS_MeshNode*>& nodes = *grIt;
6835 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6836 const SMDS_MeshNode* nToKeep = *nIt;
6837 for ( ++nIt; nIt != nodes.end(); nIt++ )
6839 const SMDS_MeshNode* nToRemove = *nIt;
6840 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
6841 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6842 while ( invElemIt->more() ) {
6843 const SMDS_MeshElement* elem = invElemIt->next();
6849 // Apply recursive replacements (BUG 0020185)
6850 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
6851 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
6853 const SMDS_MeshNode* nToKeep = nnIt->second;
6854 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
6855 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
6857 nToKeep = nnIt_i->second;
6858 nnIt->second = nToKeep;
6859 nnIt_i = nodeNodeMap.find( nToKeep );
6863 if ( theAvoidMakingHoles )
6865 // find elements whose topology changes
6867 vector<const SMDS_MeshElement*> pbElems;
6868 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6869 for ( ; eIt != elems.end(); ++eIt )
6871 const SMDS_MeshElement* elem = *eIt;
6872 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6873 while ( itN->more() )
6875 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6876 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6877 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
6879 // several nodes of elem stick
6880 pbElems.push_back( elem );
6885 // exclude from merge nodes causing spoiling element
6886 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
6888 bool nodesExcluded = false;
6889 for ( size_t i = 0; i < pbElems.size(); ++i )
6891 size_t prevNbMergeNodes = nodeNodeMap.size();
6892 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
6893 prevNbMergeNodes < nodeNodeMap.size() )
6894 nodesExcluded = true;
6896 if ( !nodesExcluded )
6901 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
6903 const SMDS_MeshNode* nToRemove = nnIt->first;
6904 const SMDS_MeshNode* nToKeep = nnIt->second;
6905 if ( nToRemove != nToKeep )
6907 rmNodeIds.push_back( nToRemove->GetID() );
6908 AddToSameGroups( nToKeep, nToRemove, mesh );
6909 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
6910 // w/o creating node in place of merged ones.
6911 SMDS_PositionPtr pos = nToRemove->GetPosition();
6912 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6913 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6914 sm->SetIsAlwaysComputed( true );
6918 // Change element nodes or remove an element
6920 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6921 for ( ; eIt != elems.end(); eIt++ )
6923 const SMDS_MeshElement* elem = *eIt;
6924 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
6926 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
6928 rmElemIds.push_back( elem->GetID() );
6930 for ( size_t i = 0; i < newElemDefs.size(); ++i )
6932 if ( i > 0 || !mesh->ChangeElementNodes( elem,
6933 & newElemDefs[i].myNodes[0],
6934 newElemDefs[i].myNodes.size() ))
6938 newElemDefs[i].SetID( elem->GetID() );
6939 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6940 if ( !keepElem ) rmElemIds.pop_back();
6944 newElemDefs[i].SetID( -1 );
6946 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
6947 if ( sm && newElem )
6948 sm->AddElement( newElem );
6949 if ( elem != newElem )
6950 ReplaceElemInGroups( elem, newElem, mesh );
6955 // Remove bad elements, then equal nodes (order important)
6956 Remove( rmElemIds, /*isNodes=*/false );
6957 Remove( rmNodeIds, /*isNodes=*/true );
6962 //=======================================================================
6963 //function : applyMerge
6964 //purpose : Compute new connectivity of an element after merging nodes
6965 // \param [in] elems - the element
6966 // \param [out] newElemDefs - definition(s) of result element(s)
6967 // \param [inout] nodeNodeMap - nodes to merge
6968 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
6969 // after merging (but not degenerated), removes nodes causing
6970 // the invalidity from \a nodeNodeMap.
6971 // \return bool - true if the element should be removed
6972 //=======================================================================
6974 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
6975 vector< ElemFeatures >& newElemDefs,
6976 TNodeNodeMap& nodeNodeMap,
6977 const bool avoidMakingHoles )
6979 bool toRemove = false; // to remove elem
6980 int nbResElems = 1; // nb new elements
6982 newElemDefs.resize(nbResElems);
6983 newElemDefs[0].Init( elem );
6984 newElemDefs[0].myNodes.clear();
6986 set<const SMDS_MeshNode*> nodeSet;
6987 vector< const SMDS_MeshNode*> curNodes;
6988 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
6991 const int nbNodes = elem->NbNodes();
6992 SMDSAbs_EntityType entity = elem->GetEntityType();
6994 curNodes.resize( nbNodes );
6995 uniqueNodes.resize( nbNodes );
6996 iRepl.resize( nbNodes );
6997 int iUnique = 0, iCur = 0, nbRepl = 0;
6999 // Get new seq of nodes
7001 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7002 while ( itN->more() )
7004 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7006 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7007 if ( nnIt != nodeNodeMap.end() ) {
7010 curNodes[ iCur ] = n;
7011 bool isUnique = nodeSet.insert( n ).second;
7013 uniqueNodes[ iUnique++ ] = n;
7015 iRepl[ nbRepl++ ] = iCur;
7019 // Analyse element topology after replacement
7021 int nbUniqueNodes = nodeSet.size();
7022 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7027 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7029 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7030 int nbCorners = nbNodes / 2;
7031 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7033 int iNext = ( iCur + 1 ) % nbCorners;
7034 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7036 int iMedium = iCur + nbCorners;
7037 vector< const SMDS_MeshNode* >::iterator i =
7038 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7040 curNodes[ iMedium ]);
7041 if ( i != uniqueNodes.end() )
7044 for ( ; i+1 != uniqueNodes.end(); ++i )
7053 case SMDSEntity_Polygon:
7054 case SMDSEntity_Quad_Polygon: // Polygon
7056 ElemFeatures* elemType = & newElemDefs[0];
7057 const bool isQuad = elemType->myIsQuad;
7059 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7060 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7062 // a polygon can divide into several elements
7063 vector<const SMDS_MeshNode *> polygons_nodes;
7064 vector<int> quantities;
7065 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7066 newElemDefs.resize( nbResElems );
7067 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7069 ElemFeatures* elemType = & newElemDefs[iface];
7070 if ( iface ) elemType->Init( elem );
7072 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7073 int nbNewNodes = quantities[iface];
7074 face_nodes.assign( polygons_nodes.begin() + inode,
7075 polygons_nodes.begin() + inode + nbNewNodes );
7076 inode += nbNewNodes;
7077 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7079 bool isValid = ( nbNewNodes % 2 == 0 );
7080 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7081 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7082 elemType->SetQuad( isValid );
7083 if ( isValid ) // put medium nodes after corners
7084 SMDS_MeshCell::applyInterlaceRev
7085 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7086 nbNewNodes ), face_nodes );
7088 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7090 nbUniqueNodes = newElemDefs[0].myNodes.size();
7094 case SMDSEntity_Polyhedra: // Polyhedral volume
7096 if ( nbUniqueNodes >= 4 )
7098 // each face has to be analyzed in order to check volume validity
7099 if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7101 int nbFaces = aPolyedre->NbFaces();
7103 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7104 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7105 vector<const SMDS_MeshNode *> faceNodes;
7109 for (int iface = 1; iface <= nbFaces; iface++)
7111 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7112 faceNodes.resize( nbFaceNodes );
7113 for (int inode = 1; inode <= nbFaceNodes; inode++)
7115 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7116 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7117 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7118 faceNode = (*nnIt).second;
7119 faceNodes[inode - 1] = faceNode;
7121 SimplifyFace(faceNodes, poly_nodes, quantities);
7124 if ( quantities.size() > 3 )
7126 // TODO: remove coincident faces
7128 nbUniqueNodes = newElemDefs[0].myNodes.size();
7136 // TODO not all the possible cases are solved. Find something more generic?
7137 case SMDSEntity_Edge: //////// EDGE
7138 case SMDSEntity_Triangle: //// TRIANGLE
7139 case SMDSEntity_Quad_Triangle:
7140 case SMDSEntity_Tetra:
7141 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7145 case SMDSEntity_Quad_Edge:
7149 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7151 if ( nbUniqueNodes < 3 )
7153 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7154 toRemove = true; // opposite nodes stick
7159 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7168 if ( nbUniqueNodes == 6 &&
7170 ( nbRepl == 1 || iRepl[1] >= 4 ))
7176 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7185 if ( nbUniqueNodes == 7 &&
7187 ( nbRepl == 1 || iRepl[1] != 8 ))
7193 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7195 if ( nbUniqueNodes == 4 ) {
7196 // ---------------------------------> tetrahedron
7197 if ( curNodes[3] == curNodes[4] &&
7198 curNodes[3] == curNodes[5] ) {
7202 else if ( curNodes[0] == curNodes[1] &&
7203 curNodes[0] == curNodes[2] ) {
7204 // bottom nodes stick: set a top before
7205 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7206 uniqueNodes[ 0 ] = curNodes [ 5 ];
7207 uniqueNodes[ 1 ] = curNodes [ 4 ];
7208 uniqueNodes[ 2 ] = curNodes [ 3 ];
7211 else if (( curNodes[0] == curNodes[3] ) +
7212 ( curNodes[1] == curNodes[4] ) +
7213 ( curNodes[2] == curNodes[5] ) == 2 ) {
7214 // a lateral face turns into a line
7218 else if ( nbUniqueNodes == 5 ) {
7219 // PENTAHEDRON --------------------> pyramid
7220 if ( curNodes[0] == curNodes[3] )
7222 uniqueNodes[ 0 ] = curNodes[ 1 ];
7223 uniqueNodes[ 1 ] = curNodes[ 4 ];
7224 uniqueNodes[ 2 ] = curNodes[ 5 ];
7225 uniqueNodes[ 3 ] = curNodes[ 2 ];
7226 uniqueNodes[ 4 ] = curNodes[ 0 ];
7229 if ( curNodes[1] == curNodes[4] )
7231 uniqueNodes[ 0 ] = curNodes[ 0 ];
7232 uniqueNodes[ 1 ] = curNodes[ 2 ];
7233 uniqueNodes[ 2 ] = curNodes[ 5 ];
7234 uniqueNodes[ 3 ] = curNodes[ 3 ];
7235 uniqueNodes[ 4 ] = curNodes[ 1 ];
7238 if ( curNodes[2] == curNodes[5] )
7240 uniqueNodes[ 0 ] = curNodes[ 0 ];
7241 uniqueNodes[ 1 ] = curNodes[ 3 ];
7242 uniqueNodes[ 2 ] = curNodes[ 4 ];
7243 uniqueNodes[ 3 ] = curNodes[ 1 ];
7244 uniqueNodes[ 4 ] = curNodes[ 2 ];
7250 case SMDSEntity_Hexa:
7252 //////////////////////////////////// HEXAHEDRON
7253 SMDS_VolumeTool hexa (elem);
7254 hexa.SetExternalNormal();
7255 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7256 //////////////////////// HEX ---> tetrahedron
7257 for ( int iFace = 0; iFace < 6; iFace++ ) {
7258 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7259 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7260 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7261 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7262 // one face turns into a point ...
7263 int pickInd = ind[ 0 ];
7264 int iOppFace = hexa.GetOppFaceIndex( iFace );
7265 ind = hexa.GetFaceNodesIndices( iOppFace );
7267 uniqueNodes.clear();
7268 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7269 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7272 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7274 if ( nbStick == 1 ) {
7275 // ... and the opposite one - into a triangle.
7277 uniqueNodes.push_back( curNodes[ pickInd ]);
7284 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7285 //////////////////////// HEX ---> prism
7286 int nbTria = 0, iTria[3];
7287 const int *ind; // indices of face nodes
7288 // look for triangular faces
7289 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7290 ind = hexa.GetFaceNodesIndices( iFace );
7291 TIDSortedNodeSet faceNodes;
7292 for ( iCur = 0; iCur < 4; iCur++ )
7293 faceNodes.insert( curNodes[ind[iCur]] );
7294 if ( faceNodes.size() == 3 )
7295 iTria[ nbTria++ ] = iFace;
7297 // check if triangles are opposite
7298 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7300 // set nodes of the bottom triangle
7301 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7303 for ( iCur = 0; iCur < 4; iCur++ )
7304 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7305 indB.push_back( ind[iCur] );
7306 if ( !hexa.IsForward() )
7307 std::swap( indB[0], indB[2] );
7308 for ( iCur = 0; iCur < 3; iCur++ )
7309 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7310 // set nodes of the top triangle
7311 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7312 for ( iCur = 0; iCur < 3; ++iCur )
7313 for ( int j = 0; j < 4; ++j )
7314 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7316 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7323 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7324 //////////////////// HEXAHEDRON ---> pyramid
7325 for ( int iFace = 0; iFace < 6; iFace++ ) {
7326 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7327 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7328 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7329 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7330 // one face turns into a point ...
7331 int iOppFace = hexa.GetOppFaceIndex( iFace );
7332 ind = hexa.GetFaceNodesIndices( iOppFace );
7333 uniqueNodes.clear();
7334 for ( iCur = 0; iCur < 4; iCur++ ) {
7335 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7338 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7340 if ( uniqueNodes.size() == 4 ) {
7341 // ... and the opposite one is a quadrangle
7343 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7344 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7352 if ( toRemove && nbUniqueNodes > 4 ) {
7353 ////////////////// HEXAHEDRON ---> polyhedron
7354 hexa.SetExternalNormal();
7355 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7356 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7357 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7358 quantities.reserve( 6 ); quantities.clear();
7359 for ( int iFace = 0; iFace < 6; iFace++ )
7361 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7362 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7363 curNodes[ind[1]] == curNodes[ind[3]] )
7366 break; // opposite nodes stick
7369 for ( iCur = 0; iCur < 4; iCur++ )
7371 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7372 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7374 if ( nodeSet.size() < 3 )
7375 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7377 quantities.push_back( nodeSet.size() );
7379 if ( quantities.size() >= 4 )
7382 nbUniqueNodes = poly_nodes.size();
7383 newElemDefs[0].SetPoly(true);
7387 } // case HEXAHEDRON
7392 } // switch ( entity )
7394 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7396 // erase from nodeNodeMap nodes whose merge spoils elem
7397 vector< const SMDS_MeshNode* > noMergeNodes;
7398 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7399 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7400 nodeNodeMap.erase( noMergeNodes[i] );
7403 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7405 uniqueNodes.resize( nbUniqueNodes );
7407 if ( !toRemove && nbResElems == 0 )
7410 newElemDefs.resize( nbResElems );
7416 // ========================================================
7417 // class : ComparableElement
7418 // purpose : allow comparing elements basing on their nodes
7419 // ========================================================
7421 class ComparableElement : public boost::container::flat_set< int >
7423 typedef boost::container::flat_set< int > int_set;
7425 const SMDS_MeshElement* myElem;
7427 mutable int myGroupID;
7431 ComparableElement( const SMDS_MeshElement* theElem ):
7432 myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7434 this->reserve( theElem->NbNodes() );
7435 for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7437 int id = nodeIt->next()->GetID();
7443 const SMDS_MeshElement* GetElem() const { return myElem; }
7445 int& GroupID() const { return myGroupID; }
7446 //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7448 ComparableElement( const ComparableElement& theSource ) // move copy
7450 ComparableElement& src = const_cast< ComparableElement& >( theSource );
7451 (int_set&) (*this ) = boost::move( src );
7452 myElem = src.myElem;
7453 mySumID = src.mySumID;
7454 myGroupID = src.myGroupID;
7457 static int HashCode(const ComparableElement& se, int limit )
7459 return ::HashCode( se.mySumID, limit );
7461 static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7463 return ( se1 == se2 );
7468 //=======================================================================
7469 //function : FindEqualElements
7470 //purpose : Return list of group of elements built on the same nodes.
7471 // Search among theElements or in the whole mesh if theElements is empty
7472 //=======================================================================
7474 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet & theElements,
7475 TListOfListOfElementsID & theGroupsOfElementsID )
7479 SMDS_ElemIteratorPtr elemIt;
7480 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7481 else elemIt = SMESHUtils::elemSetIterator( theElements );
7483 typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7484 typedef std::list<int> TGroupOfElems;
7485 TMapOfElements mapOfElements;
7486 std::vector< TGroupOfElems > arrayOfGroups;
7487 TGroupOfElems groupOfElems;
7489 while ( elemIt->more() )
7491 const SMDS_MeshElement* curElem = elemIt->next();
7492 if ( curElem->IsNull() )
7494 ComparableElement compElem = curElem;
7496 const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7497 if ( elemInSet.GetElem() != curElem ) // coincident elem
7499 int& iG = elemInSet.GroupID();
7502 iG = arrayOfGroups.size();
7503 arrayOfGroups.push_back( groupOfElems );
7504 arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7506 arrayOfGroups[ iG ].push_back( curElem->GetID() );
7510 groupOfElems.clear();
7511 std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7512 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7514 if ( groupIt->size() > 1 ) {
7515 //groupOfElems.sort(); -- theElements are sorted already
7516 theGroupsOfElementsID.emplace_back( *groupIt );
7521 //=======================================================================
7522 //function : MergeElements
7523 //purpose : In each given group, substitute all elements by the first one.
7524 //=======================================================================
7526 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7530 typedef list<int> TListOfIDs;
7531 TListOfIDs rmElemIds; // IDs of elems to remove
7533 SMESHDS_Mesh* aMesh = GetMeshDS();
7535 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7536 while ( groupsIt != theGroupsOfElementsID.end() ) {
7537 TListOfIDs& aGroupOfElemID = *groupsIt;
7538 aGroupOfElemID.sort();
7539 int elemIDToKeep = aGroupOfElemID.front();
7540 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7541 aGroupOfElemID.pop_front();
7542 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7543 while ( idIt != aGroupOfElemID.end() ) {
7544 int elemIDToRemove = *idIt;
7545 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7546 // add the kept element in groups of removed one (PAL15188)
7547 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7548 rmElemIds.push_back( elemIDToRemove );
7554 Remove( rmElemIds, false );
7557 //=======================================================================
7558 //function : MergeEqualElements
7559 //purpose : Remove all but one of elements built on the same nodes.
7560 //=======================================================================
7562 void SMESH_MeshEditor::MergeEqualElements()
7564 TIDSortedElemSet aMeshElements; /* empty input ==
7565 to merge equal elements in the whole mesh */
7566 TListOfListOfElementsID aGroupsOfElementsID;
7567 FindEqualElements( aMeshElements, aGroupsOfElementsID );
7568 MergeElements( aGroupsOfElementsID );
7571 //=======================================================================
7572 //function : findAdjacentFace
7574 //=======================================================================
7576 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7577 const SMDS_MeshNode* n2,
7578 const SMDS_MeshElement* elem)
7580 TIDSortedElemSet elemSet, avoidSet;
7582 avoidSet.insert ( elem );
7583 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7586 //=======================================================================
7587 //function : findSegment
7588 //purpose : Return a mesh segment by two nodes one of which can be medium
7589 //=======================================================================
7591 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7592 const SMDS_MeshNode* n2)
7594 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7595 while ( it->more() )
7597 const SMDS_MeshElement* seg = it->next();
7598 if ( seg->GetNodeIndex( n2 ) >= 0 )
7604 //=======================================================================
7605 //function : FindFreeBorder
7607 //=======================================================================
7609 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7611 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7612 const SMDS_MeshNode* theSecondNode,
7613 const SMDS_MeshNode* theLastNode,
7614 list< const SMDS_MeshNode* > & theNodes,
7615 list< const SMDS_MeshElement* >& theFaces)
7617 if ( !theFirstNode || !theSecondNode )
7619 // find border face between theFirstNode and theSecondNode
7620 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7624 theFaces.push_back( curElem );
7625 theNodes.push_back( theFirstNode );
7626 theNodes.push_back( theSecondNode );
7628 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7629 //TIDSortedElemSet foundElems;
7630 bool needTheLast = ( theLastNode != 0 );
7632 vector<const SMDS_MeshNode*> nodes;
7634 while ( nStart != theLastNode ) {
7635 if ( nStart == theFirstNode )
7636 return !needTheLast;
7638 // find all free border faces sharing nStart
7640 list< const SMDS_MeshElement* > curElemList;
7641 list< const SMDS_MeshNode* > nStartList;
7642 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7643 while ( invElemIt->more() ) {
7644 const SMDS_MeshElement* e = invElemIt->next();
7645 //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
7648 nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7649 SMDS_MeshElement::iterator() );
7650 nodes.push_back( nodes[ 0 ]);
7653 int iNode = 0, nbNodes = nodes.size() - 1;
7654 for ( iNode = 0; iNode < nbNodes; iNode++ )
7655 if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7656 ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7657 ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
7659 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
7660 curElemList.push_back( e );
7664 // analyse the found
7666 int nbNewBorders = curElemList.size();
7667 if ( nbNewBorders == 0 ) {
7668 // no free border furthermore
7669 return !needTheLast;
7671 else if ( nbNewBorders == 1 ) {
7672 // one more element found
7674 nStart = nStartList.front();
7675 curElem = curElemList.front();
7676 theFaces.push_back( curElem );
7677 theNodes.push_back( nStart );
7680 // several continuations found
7681 list< const SMDS_MeshElement* >::iterator curElemIt;
7682 list< const SMDS_MeshNode* >::iterator nStartIt;
7683 // check if one of them reached the last node
7684 if ( needTheLast ) {
7685 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7686 curElemIt!= curElemList.end();
7687 curElemIt++, nStartIt++ )
7688 if ( *nStartIt == theLastNode ) {
7689 theFaces.push_back( *curElemIt );
7690 theNodes.push_back( *nStartIt );
7694 // find the best free border by the continuations
7695 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7696 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7697 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7698 curElemIt!= curElemList.end();
7699 curElemIt++, nStartIt++ )
7701 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7702 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7703 // find one more free border
7704 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7708 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7709 // choice: clear a worse one
7710 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7711 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7712 contNodes[ iWorse ].clear();
7713 contFaces[ iWorse ].clear();
7716 if ( contNodes[0].empty() && contNodes[1].empty() )
7719 // push_back the best free border
7720 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7721 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7722 //theNodes.pop_back(); // remove nIgnore
7723 theNodes.pop_back(); // remove nStart
7724 //theFaces.pop_back(); // remove curElem
7725 theNodes.splice( theNodes.end(), *cNL );
7726 theFaces.splice( theFaces.end(), *cFL );
7729 } // several continuations found
7730 } // while ( nStart != theLastNode )
7735 //=======================================================================
7736 //function : CheckFreeBorderNodes
7737 //purpose : Return true if the tree nodes are on a free border
7738 //=======================================================================
7740 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7741 const SMDS_MeshNode* theNode2,
7742 const SMDS_MeshNode* theNode3)
7744 list< const SMDS_MeshNode* > nodes;
7745 list< const SMDS_MeshElement* > faces;
7746 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7749 //=======================================================================
7750 //function : SewFreeBorder
7752 //warning : for border-to-side sewing theSideSecondNode is considered as
7753 // the last side node and theSideThirdNode is not used
7754 //=======================================================================
7756 SMESH_MeshEditor::Sew_Error
7757 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7758 const SMDS_MeshNode* theBordSecondNode,
7759 const SMDS_MeshNode* theBordLastNode,
7760 const SMDS_MeshNode* theSideFirstNode,
7761 const SMDS_MeshNode* theSideSecondNode,
7762 const SMDS_MeshNode* theSideThirdNode,
7763 const bool theSideIsFreeBorder,
7764 const bool toCreatePolygons,
7765 const bool toCreatePolyedrs)
7769 Sew_Error aResult = SEW_OK;
7771 // ====================================
7772 // find side nodes and elements
7773 // ====================================
7775 list< const SMDS_MeshNode* > nSide[ 2 ];
7776 list< const SMDS_MeshElement* > eSide[ 2 ];
7777 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7778 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7782 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7783 nSide[0], eSide[0])) {
7784 MESSAGE(" Free Border 1 not found " );
7785 aResult = SEW_BORDER1_NOT_FOUND;
7787 if (theSideIsFreeBorder) {
7790 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7791 nSide[1], eSide[1])) {
7792 MESSAGE(" Free Border 2 not found " );
7793 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7796 if ( aResult != SEW_OK )
7799 if (!theSideIsFreeBorder) {
7803 // -------------------------------------------------------------------------
7805 // 1. If nodes to merge are not coincident, move nodes of the free border
7806 // from the coord sys defined by the direction from the first to last
7807 // nodes of the border to the correspondent sys of the side 2
7808 // 2. On the side 2, find the links most co-directed with the correspondent
7809 // links of the free border
7810 // -------------------------------------------------------------------------
7812 // 1. Since sewing may break if there are volumes to split on the side 2,
7813 // we won't move nodes but just compute new coordinates for them
7814 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7815 TNodeXYZMap nBordXYZ;
7816 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7817 list< const SMDS_MeshNode* >::iterator nBordIt;
7819 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7820 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7821 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7822 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7823 double tol2 = 1.e-8;
7824 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7825 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7826 // Need node movement.
7828 // find X and Z axes to create trsf
7829 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7831 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7833 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7836 gp_Ax3 toBordAx( Pb1, Zb, X );
7837 gp_Ax3 fromSideAx( Ps1, Zs, X );
7838 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7840 gp_Trsf toBordSys, fromSide2Sys;
7841 toBordSys.SetTransformation( toBordAx );
7842 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7843 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7846 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7847 const SMDS_MeshNode* n = *nBordIt;
7848 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7849 toBordSys.Transforms( xyz );
7850 fromSide2Sys.Transforms( xyz );
7851 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7855 // just insert nodes XYZ in the nBordXYZ map
7856 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7857 const SMDS_MeshNode* n = *nBordIt;
7858 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7862 // 2. On the side 2, find the links most co-directed with the correspondent
7863 // links of the free border
7865 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7866 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7867 sideNodes.push_back( theSideFirstNode );
7869 bool hasVolumes = false;
7870 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7871 set<long> foundSideLinkIDs, checkedLinkIDs;
7872 SMDS_VolumeTool volume;
7873 //const SMDS_MeshNode* faceNodes[ 4 ];
7875 const SMDS_MeshNode* sideNode;
7876 const SMDS_MeshElement* sideElem = 0;
7877 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7878 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7879 nBordIt = bordNodes.begin();
7881 // border node position and border link direction to compare with
7882 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7883 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7884 // choose next side node by link direction or by closeness to
7885 // the current border node:
7886 bool searchByDir = ( *nBordIt != theBordLastNode );
7888 // find the next node on the Side 2
7890 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7892 checkedLinkIDs.clear();
7893 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7895 // loop on inverse elements of current node (prevSideNode) on the Side 2
7896 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7897 while ( invElemIt->more() )
7899 const SMDS_MeshElement* elem = invElemIt->next();
7900 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7901 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7902 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7903 bool isVolume = volume.Set( elem );
7904 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7905 if ( isVolume ) // --volume
7907 else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7908 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7909 SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
7910 while ( nIt->more() ) {
7911 nodes[ iNode ] = cast2Node( nIt->next() );
7912 if ( nodes[ iNode++ ] == prevSideNode )
7913 iPrevNode = iNode - 1;
7915 // there are 2 links to check
7920 // loop on links, to be precise, on the second node of links
7921 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7922 const SMDS_MeshNode* n = nodes[ iNode ];
7924 if ( !volume.IsLinked( n, prevSideNode ))
7928 if ( iNode ) // a node before prevSideNode
7929 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7930 else // a node after prevSideNode
7931 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7933 // check if this link was already used
7934 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7935 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7936 if (!isJustChecked &&
7937 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7939 // test a link geometrically
7940 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7941 bool linkIsBetter = false;
7942 double dot = 0.0, dist = 0.0;
7943 if ( searchByDir ) { // choose most co-directed link
7944 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7945 linkIsBetter = ( dot > maxDot );
7947 else { // choose link with the node closest to bordPos
7948 dist = ( nextXYZ - bordPos ).SquareModulus();
7949 linkIsBetter = ( dist < minDist );
7951 if ( linkIsBetter ) {
7960 } // loop on inverse elements of prevSideNode
7963 MESSAGE(" Can't find path by links of the Side 2 ");
7964 return SEW_BAD_SIDE_NODES;
7966 sideNodes.push_back( sideNode );
7967 sideElems.push_back( sideElem );
7968 foundSideLinkIDs.insert ( linkID );
7969 prevSideNode = sideNode;
7971 if ( *nBordIt == theBordLastNode )
7972 searchByDir = false;
7974 // find the next border link to compare with
7975 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7976 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7977 // move to next border node if sideNode is before forward border node (bordPos)
7978 while ( *nBordIt != theBordLastNode && !searchByDir ) {
7979 prevBordNode = *nBordIt;
7981 bordPos = nBordXYZ[ *nBordIt ];
7982 bordDir = bordPos - nBordXYZ[ prevBordNode ];
7983 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7987 while ( sideNode != theSideSecondNode );
7989 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7990 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7991 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7993 } // end nodes search on the side 2
7995 // ============================
7996 // sew the border to the side 2
7997 // ============================
7999 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8000 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8002 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8003 if ( toMergeConformal && toCreatePolygons )
8005 // do not merge quadrangles if polygons are OK (IPAL0052824)
8006 eIt[0] = eSide[0].begin();
8007 eIt[1] = eSide[1].begin();
8008 bool allQuads[2] = { true, true };
8009 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8010 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8011 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8013 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8016 TListOfListOfNodes nodeGroupsToMerge;
8017 if (( toMergeConformal ) ||
8018 ( theSideIsFreeBorder && !theSideThirdNode )) {
8020 // all nodes are to be merged
8022 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8023 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8024 nIt[0]++, nIt[1]++ )
8026 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8027 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8028 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8033 // insert new nodes into the border and the side to get equal nb of segments
8035 // get normalized parameters of nodes on the borders
8036 vector< double > param[ 2 ];
8037 param[0].resize( maxNbNodes );
8038 param[1].resize( maxNbNodes );
8040 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8041 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8042 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8043 const SMDS_MeshNode* nPrev = *nIt;
8044 double bordLength = 0;
8045 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8046 const SMDS_MeshNode* nCur = *nIt;
8047 gp_XYZ segment (nCur->X() - nPrev->X(),
8048 nCur->Y() - nPrev->Y(),
8049 nCur->Z() - nPrev->Z());
8050 double segmentLen = segment.Modulus();
8051 bordLength += segmentLen;
8052 param[ iBord ][ iNode ] = bordLength;
8055 // normalize within [0,1]
8056 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8057 param[ iBord ][ iNode ] /= bordLength;
8061 // loop on border segments
8062 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8063 int i[ 2 ] = { 0, 0 };
8064 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8065 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8067 // element can be split while iterating on border if it has two edges in the border
8068 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8069 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8071 TElemOfNodeListMap insertMap;
8072 TElemOfNodeListMap::iterator insertMapIt;
8074 // key: elem to insert nodes into
8075 // value: 2 nodes to insert between + nodes to be inserted
8077 bool next[ 2 ] = { false, false };
8079 // find min adjacent segment length after sewing
8080 double nextParam = 10., prevParam = 0;
8081 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8082 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8083 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8084 if ( i[ iBord ] > 0 )
8085 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8087 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8088 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8089 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8091 // choose to insert or to merge nodes
8092 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8093 if ( Abs( du ) <= minSegLen * 0.2 ) {
8096 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8097 const SMDS_MeshNode* n0 = *nIt[0];
8098 const SMDS_MeshNode* n1 = *nIt[1];
8099 nodeGroupsToMerge.back().push_back( n1 );
8100 nodeGroupsToMerge.back().push_back( n0 );
8101 // position of node of the border changes due to merge
8102 param[ 0 ][ i[0] ] += du;
8103 // move n1 for the sake of elem shape evaluation during insertion.
8104 // n1 will be removed by MergeNodes() anyway
8105 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8106 next[0] = next[1] = true;
8111 int intoBord = ( du < 0 ) ? 0 : 1;
8112 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8113 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8114 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8115 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8116 if ( intoBord == 1 ) {
8117 // move node of the border to be on a link of elem of the side
8118 SMESH_NodeXYZ p1( n1 ), p2( n2 );
8119 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8120 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8121 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8123 elemReplaceMapIt = elemReplaceMap.find( elem );
8124 if ( elemReplaceMapIt != elemReplaceMap.end() )
8125 elem = elemReplaceMapIt->second;
8127 insertMapIt = insertMap.find( elem );
8128 bool notFound = ( insertMapIt == insertMap.end() );
8129 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8131 // insert into another link of the same element:
8132 // 1. perform insertion into the other link of the elem
8133 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8134 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8135 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8136 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8137 // 2. perform insertion into the link of adjacent faces
8138 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8139 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8141 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8142 InsertNodesIntoLink( seg, n12, n22, nodeList );
8144 if (toCreatePolyedrs) {
8145 // perform insertion into the links of adjacent volumes
8146 UpdateVolumes(n12, n22, nodeList);
8148 // 3. find an element appeared on n1 and n2 after the insertion
8149 insertMap.erase( insertMapIt );
8150 const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8151 elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8154 if ( notFound || otherLink ) {
8155 // add element and nodes of the side into the insertMap
8156 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8157 (*insertMapIt).second.push_back( n1 );
8158 (*insertMapIt).second.push_back( n2 );
8160 // add node to be inserted into elem
8161 (*insertMapIt).second.push_back( nIns );
8162 next[ 1 - intoBord ] = true;
8165 // go to the next segment
8166 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8167 if ( next[ iBord ] ) {
8168 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8170 nPrev[ iBord ] = *nIt[ iBord ];
8171 nIt[ iBord ]++; i[ iBord ]++;
8175 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8177 // perform insertion of nodes into elements
8179 for (insertMapIt = insertMap.begin();
8180 insertMapIt != insertMap.end();
8183 const SMDS_MeshElement* elem = (*insertMapIt).first;
8184 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8185 if ( nodeList.size() < 3 ) continue;
8186 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8187 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8189 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8191 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8192 InsertNodesIntoLink( seg, n1, n2, nodeList );
8195 if ( !theSideIsFreeBorder ) {
8196 // look for and insert nodes into the faces adjacent to elem
8197 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8198 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8201 if (toCreatePolyedrs) {
8202 // perform insertion into the links of adjacent volumes
8203 UpdateVolumes(n1, n2, nodeList);
8206 } // end: insert new nodes
8208 MergeNodes ( nodeGroupsToMerge );
8211 // Remove coincident segments
8214 TIDSortedElemSet segments;
8215 SMESH_SequenceOfElemPtr newFaces;
8216 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8218 if ( !myLastCreatedElems[i] ) continue;
8219 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8220 segments.insert( segments.end(), myLastCreatedElems[i] );
8222 newFaces.push_back( myLastCreatedElems[i] );
8224 // get segments adjacent to merged nodes
8225 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8226 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8228 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8229 if ( nodes.front()->IsNull() ) continue;
8230 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8231 while ( segIt->more() )
8232 segments.insert( segIt->next() );
8236 TListOfListOfElementsID equalGroups;
8237 if ( !segments.empty() )
8238 FindEqualElements( segments, equalGroups );
8239 if ( !equalGroups.empty() )
8241 // remove from segments those that will be removed
8242 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8243 for ( ; itGroups != equalGroups.end(); ++itGroups )
8245 list< int >& group = *itGroups;
8246 list< int >::iterator id = group.begin();
8247 for ( ++id; id != group.end(); ++id )
8248 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8249 segments.erase( seg );
8251 // remove equal segments
8252 MergeElements( equalGroups );
8254 // restore myLastCreatedElems
8255 myLastCreatedElems = newFaces;
8256 TIDSortedElemSet::iterator seg = segments.begin();
8257 for ( ; seg != segments.end(); ++seg )
8258 myLastCreatedElems.push_back( *seg );
8264 //=======================================================================
8265 //function : InsertNodesIntoLink
8266 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8267 // and theBetweenNode2 and split theElement
8268 //=======================================================================
8270 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8271 const SMDS_MeshNode* theBetweenNode1,
8272 const SMDS_MeshNode* theBetweenNode2,
8273 list<const SMDS_MeshNode*>& theNodesToInsert,
8274 const bool toCreatePoly)
8276 if ( !theElement ) return;
8278 SMESHDS_Mesh *aMesh = GetMeshDS();
8279 vector<const SMDS_MeshElement*> newElems;
8281 if ( theElement->GetType() == SMDSAbs_Edge )
8283 theNodesToInsert.push_front( theBetweenNode1 );
8284 theNodesToInsert.push_back ( theBetweenNode2 );
8285 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8286 const SMDS_MeshNode* n1 = *n;
8287 for ( ++n; n != theNodesToInsert.end(); ++n )
8289 const SMDS_MeshNode* n2 = *n;
8290 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8291 AddToSameGroups( seg, theElement, aMesh );
8293 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8296 theNodesToInsert.pop_front();
8297 theNodesToInsert.pop_back();
8299 if ( theElement->IsQuadratic() ) // add a not split part
8301 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8302 theElement->end_nodes() );
8303 int iOther = 0, nbN = nodes.size();
8304 for ( ; iOther < nbN; ++iOther )
8305 if ( nodes[iOther] != theBetweenNode1 &&
8306 nodes[iOther] != theBetweenNode2 )
8310 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8311 AddToSameGroups( seg, theElement, aMesh );
8313 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8315 else if ( iOther == 2 )
8317 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8318 AddToSameGroups( seg, theElement, aMesh );
8320 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8323 // treat new elements
8324 for ( size_t i = 0; i < newElems.size(); ++i )
8327 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8328 myLastCreatedElems.push_back( newElems[i] );
8330 ReplaceElemInGroups( theElement, newElems, aMesh );
8331 aMesh->RemoveElement( theElement );
8334 } // if ( theElement->GetType() == SMDSAbs_Edge )
8336 const SMDS_MeshElement* theFace = theElement;
8337 if ( theFace->GetType() != SMDSAbs_Face ) return;
8339 // find indices of 2 link nodes and of the rest nodes
8340 int iNode = 0, il1, il2, i3, i4;
8341 il1 = il2 = i3 = i4 = -1;
8342 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8344 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8345 while ( nodeIt->more() ) {
8346 const SMDS_MeshNode* n = nodeIt->next();
8347 if ( n == theBetweenNode1 )
8349 else if ( n == theBetweenNode2 )
8355 nodes[ iNode++ ] = n;
8357 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8360 // arrange link nodes to go one after another regarding the face orientation
8361 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8362 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8367 aNodesToInsert.reverse();
8369 // check that not link nodes of a quadrangles are in good order
8370 int nbFaceNodes = theFace->NbNodes();
8371 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8377 if (toCreatePoly || theFace->IsPoly()) {
8380 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8382 // add nodes of face up to first node of link
8384 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8385 while ( nodeIt->more() && !isFLN ) {
8386 const SMDS_MeshNode* n = nodeIt->next();
8387 poly_nodes[iNode++] = n;
8388 isFLN = ( n == nodes[il1] );
8390 // add nodes to insert
8391 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8392 for (; nIt != aNodesToInsert.end(); nIt++) {
8393 poly_nodes[iNode++] = *nIt;
8395 // add nodes of face starting from last node of link
8396 while ( nodeIt->more() ) {
8397 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8398 poly_nodes[iNode++] = n;
8402 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8405 else if ( !theFace->IsQuadratic() )
8407 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8408 int nbLinkNodes = 2 + aNodesToInsert.size();
8409 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8410 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8411 linkNodes[ 0 ] = nodes[ il1 ];
8412 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8413 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8414 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8415 linkNodes[ iNode++ ] = *nIt;
8417 // decide how to split a quadrangle: compare possible variants
8418 // and choose which of splits to be a quadrangle
8419 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8420 if ( nbFaceNodes == 3 ) {
8421 iBestQuad = nbSplits;
8424 else if ( nbFaceNodes == 4 ) {
8425 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8426 double aBestRate = DBL_MAX;
8427 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8429 double aBadRate = 0;
8430 // evaluate elements quality
8431 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8432 if ( iSplit == iQuad ) {
8433 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8437 aBadRate += getBadRate( &quad, aCrit );
8440 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8442 nodes[ iSplit < iQuad ? i4 : i3 ]);
8443 aBadRate += getBadRate( &tria, aCrit );
8447 if ( aBadRate < aBestRate ) {
8449 aBestRate = aBadRate;
8454 // create new elements
8456 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8458 if ( iSplit == iBestQuad )
8459 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8464 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8466 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8469 const SMDS_MeshNode* newNodes[ 4 ];
8470 newNodes[ 0 ] = linkNodes[ i1 ];
8471 newNodes[ 1 ] = linkNodes[ i2 ];
8472 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8473 newNodes[ 3 ] = nodes[ i4 ];
8474 if (iSplit == iBestQuad)
8475 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8477 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8479 } // end if(!theFace->IsQuadratic())
8481 else { // theFace is quadratic
8482 // we have to split theFace on simple triangles and one simple quadrangle
8484 int nbshift = tmp*2;
8485 // shift nodes in nodes[] by nbshift
8487 for(i=0; i<nbshift; i++) {
8488 const SMDS_MeshNode* n = nodes[0];
8489 for(j=0; j<nbFaceNodes-1; j++) {
8490 nodes[j] = nodes[j+1];
8492 nodes[nbFaceNodes-1] = n;
8494 il1 = il1 - nbshift;
8495 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8496 // n0 n1 n2 n0 n1 n2
8497 // +-----+-----+ +-----+-----+
8506 // create new elements
8508 if ( nbFaceNodes == 6 ) { // quadratic triangle
8509 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8510 if ( theFace->IsMediumNode(nodes[il1]) ) {
8511 // create quadrangle
8512 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8518 // create quadrangle
8519 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8525 else { // nbFaceNodes==8 - quadratic quadrangle
8526 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8527 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8528 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8529 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8530 // create quadrangle
8531 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8537 // create quadrangle
8538 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8544 // create needed triangles using n1,n2,n3 and inserted nodes
8545 int nbn = 2 + aNodesToInsert.size();
8546 vector<const SMDS_MeshNode*> aNodes(nbn);
8547 aNodes[0 ] = nodes[n1];
8548 aNodes[nbn-1] = nodes[n2];
8549 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8550 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8551 aNodes[iNode++] = *nIt;
8553 for ( i = 1; i < nbn; i++ )
8554 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8557 // remove the old face
8558 for ( size_t i = 0; i < newElems.size(); ++i )
8561 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8562 myLastCreatedElems.push_back( newElems[i] );
8564 ReplaceElemInGroups( theFace, newElems, aMesh );
8565 aMesh->RemoveElement(theFace);
8567 } // InsertNodesIntoLink()
8569 //=======================================================================
8570 //function : UpdateVolumes
8572 //=======================================================================
8574 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8575 const SMDS_MeshNode* theBetweenNode2,
8576 list<const SMDS_MeshNode*>& theNodesToInsert)
8580 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8581 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8582 const SMDS_MeshElement* elem = invElemIt->next();
8584 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8585 SMDS_VolumeTool aVolume (elem);
8586 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8589 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8590 int iface, nbFaces = aVolume.NbFaces();
8591 vector<const SMDS_MeshNode *> poly_nodes;
8592 vector<int> quantities (nbFaces);
8594 for (iface = 0; iface < nbFaces; iface++) {
8595 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8596 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8597 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8599 for (int inode = 0; inode < nbFaceNodes; inode++) {
8600 poly_nodes.push_back(faceNodes[inode]);
8602 if (nbInserted == 0) {
8603 if (faceNodes[inode] == theBetweenNode1) {
8604 if (faceNodes[inode + 1] == theBetweenNode2) {
8605 nbInserted = theNodesToInsert.size();
8607 // add nodes to insert
8608 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8609 for (; nIt != theNodesToInsert.end(); nIt++) {
8610 poly_nodes.push_back(*nIt);
8614 else if (faceNodes[inode] == theBetweenNode2) {
8615 if (faceNodes[inode + 1] == theBetweenNode1) {
8616 nbInserted = theNodesToInsert.size();
8618 // add nodes to insert in reversed order
8619 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8621 for (; nIt != theNodesToInsert.begin(); nIt--) {
8622 poly_nodes.push_back(*nIt);
8624 poly_nodes.push_back(*nIt);
8631 quantities[iface] = nbFaceNodes + nbInserted;
8634 // Replace the volume
8635 SMESHDS_Mesh *aMesh = GetMeshDS();
8637 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8639 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8640 myLastCreatedElems.push_back( newElem );
8641 ReplaceElemInGroups( elem, newElem, aMesh );
8643 aMesh->RemoveElement( elem );
8649 //================================================================================
8651 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8653 //================================================================================
8655 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8656 vector<const SMDS_MeshNode *> & nodes,
8657 vector<int> & nbNodeInFaces )
8660 nbNodeInFaces.clear();
8661 SMDS_VolumeTool vTool ( elem );
8662 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8664 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8665 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8666 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8671 //=======================================================================
8673 * \brief Convert elements contained in a sub-mesh to quadratic
8674 * \return int - nb of checked elements
8676 //=======================================================================
8678 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8679 SMESH_MesherHelper& theHelper,
8680 const bool theForce3d)
8682 //MESSAGE("convertElemToQuadratic");
8684 if( !theSm ) return nbElem;
8686 vector<int> nbNodeInFaces;
8687 vector<const SMDS_MeshNode *> nodes;
8688 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8689 while(ElemItr->more())
8692 const SMDS_MeshElement* elem = ElemItr->next();
8693 if( !elem ) continue;
8695 // analyse a necessity of conversion
8696 const SMDSAbs_ElementType aType = elem->GetType();
8697 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8699 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8700 bool hasCentralNodes = false;
8701 if ( elem->IsQuadratic() )
8704 switch ( aGeomType ) {
8705 case SMDSEntity_Quad_Triangle:
8706 case SMDSEntity_Quad_Quadrangle:
8707 case SMDSEntity_Quad_Hexa:
8708 case SMDSEntity_Quad_Penta:
8709 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8711 case SMDSEntity_BiQuad_Triangle:
8712 case SMDSEntity_BiQuad_Quadrangle:
8713 case SMDSEntity_TriQuad_Hexa:
8714 case SMDSEntity_BiQuad_Penta:
8715 alreadyOK = theHelper.GetIsBiQuadratic();
8716 hasCentralNodes = true;
8721 // take into account already present medium nodes
8723 case SMDSAbs_Volume:
8724 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8726 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8728 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8734 // get elem data needed to re-create it
8736 const int id = elem->GetID();
8737 const int nbNodes = elem->NbCornerNodes();
8738 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8739 if ( aGeomType == SMDSEntity_Polyhedra )
8740 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8741 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8742 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8744 // remove a linear element
8745 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8747 // remove central nodes of biquadratic elements (biquad->quad conversion)
8748 if ( hasCentralNodes )
8749 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8750 if ( nodes[i]->NbInverseElements() == 0 )
8751 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8753 const SMDS_MeshElement* NewElem = 0;
8759 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8767 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8770 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8773 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8777 case SMDSAbs_Volume :
8781 case SMDSEntity_Tetra:
8782 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8784 case SMDSEntity_Pyramid:
8785 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8787 case SMDSEntity_Penta:
8788 case SMDSEntity_Quad_Penta:
8789 case SMDSEntity_BiQuad_Penta:
8790 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8792 case SMDSEntity_Hexa:
8793 case SMDSEntity_Quad_Hexa:
8794 case SMDSEntity_TriQuad_Hexa:
8795 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8796 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8798 case SMDSEntity_Hexagonal_Prism:
8800 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8807 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8808 if( NewElem && NewElem->getshapeId() < 1 )
8809 theSm->AddElement( NewElem );
8813 //=======================================================================
8814 //function : ConvertToQuadratic
8816 //=======================================================================
8818 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8820 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8821 SMESHDS_Mesh* meshDS = GetMeshDS();
8823 SMESH_MesherHelper aHelper(*myMesh);
8825 aHelper.SetIsQuadratic( true );
8826 aHelper.SetIsBiQuadratic( theToBiQuad );
8827 aHelper.SetElementsOnShape(true);
8828 aHelper.ToFixNodeParameters( true );
8830 // convert elements assigned to sub-meshes
8831 int nbCheckedElems = 0;
8832 if ( myMesh->HasShapeToMesh() )
8834 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8836 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8837 while ( smIt->more() ) {
8838 SMESH_subMesh* sm = smIt->next();
8839 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8840 aHelper.SetSubShape( sm->GetSubShape() );
8841 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8847 // convert elements NOT assigned to sub-meshes
8848 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8849 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8851 aHelper.SetElementsOnShape(false);
8852 SMESHDS_SubMesh *smDS = 0;
8855 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8856 while( aEdgeItr->more() )
8858 const SMDS_MeshEdge* edge = aEdgeItr->next();
8859 if ( !edge->IsQuadratic() )
8861 int id = edge->GetID();
8862 const SMDS_MeshNode* n1 = edge->GetNode(0);
8863 const SMDS_MeshNode* n2 = edge->GetNode(1);
8865 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8867 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8868 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8872 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8877 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8878 while( aFaceItr->more() )
8880 const SMDS_MeshFace* face = aFaceItr->next();
8881 if ( !face ) continue;
8883 const SMDSAbs_EntityType type = face->GetEntityType();
8887 case SMDSEntity_Quad_Triangle:
8888 case SMDSEntity_Quad_Quadrangle:
8889 alreadyOK = !theToBiQuad;
8890 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8892 case SMDSEntity_BiQuad_Triangle:
8893 case SMDSEntity_BiQuad_Quadrangle:
8894 alreadyOK = theToBiQuad;
8895 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8897 default: alreadyOK = false;
8902 const int id = face->GetID();
8903 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8905 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8907 SMDS_MeshFace * NewFace = 0;
8910 case SMDSEntity_Triangle:
8911 case SMDSEntity_Quad_Triangle:
8912 case SMDSEntity_BiQuad_Triangle:
8913 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8914 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8915 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8918 case SMDSEntity_Quadrangle:
8919 case SMDSEntity_Quad_Quadrangle:
8920 case SMDSEntity_BiQuad_Quadrangle:
8921 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8922 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8923 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8927 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8929 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8933 vector<int> nbNodeInFaces;
8934 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8935 while(aVolumeItr->more())
8937 const SMDS_MeshVolume* volume = aVolumeItr->next();
8938 if ( !volume ) continue;
8940 const SMDSAbs_EntityType type = volume->GetEntityType();
8941 if ( volume->IsQuadratic() )
8946 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8947 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8948 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
8949 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
8950 default: alreadyOK = true;
8954 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8958 const int id = volume->GetID();
8959 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8960 if ( type == SMDSEntity_Polyhedra )
8961 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
8962 else if ( type == SMDSEntity_Hexagonal_Prism )
8963 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8965 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8967 SMDS_MeshVolume * NewVolume = 0;
8970 case SMDSEntity_Tetra:
8971 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8973 case SMDSEntity_Hexa:
8974 case SMDSEntity_Quad_Hexa:
8975 case SMDSEntity_TriQuad_Hexa:
8976 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8977 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8978 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8979 if ( nodes[i]->NbInverseElements() == 0 )
8980 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8982 case SMDSEntity_Pyramid:
8983 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8984 nodes[3], nodes[4], id, theForce3d);
8986 case SMDSEntity_Penta:
8987 case SMDSEntity_Quad_Penta:
8988 case SMDSEntity_BiQuad_Penta:
8989 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8990 nodes[3], nodes[4], nodes[5], id, theForce3d);
8991 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
8992 if ( nodes[i]->NbInverseElements() == 0 )
8993 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8995 case SMDSEntity_Hexagonal_Prism:
8997 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8999 ReplaceElemInGroups(volume, NewVolume, meshDS);
9004 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9005 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9006 // aHelper.FixQuadraticElements(myError);
9007 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9011 //================================================================================
9013 * \brief Makes given elements quadratic
9014 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9015 * \param theElements - elements to make quadratic
9017 //================================================================================
9019 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9020 TIDSortedElemSet& theElements,
9021 const bool theToBiQuad)
9023 if ( theElements.empty() ) return;
9025 // we believe that all theElements are of the same type
9026 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9028 // get all nodes shared by theElements
9029 TIDSortedNodeSet allNodes;
9030 TIDSortedElemSet::iterator eIt = theElements.begin();
9031 for ( ; eIt != theElements.end(); ++eIt )
9032 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9034 // complete theElements with elements of lower dim whose all nodes are in allNodes
9036 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9037 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9038 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9039 for ( ; nIt != allNodes.end(); ++nIt )
9041 const SMDS_MeshNode* n = *nIt;
9042 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9043 while ( invIt->more() )
9045 const SMDS_MeshElement* e = invIt->next();
9046 const SMDSAbs_ElementType type = e->GetType();
9047 if ( e->IsQuadratic() )
9049 quadAdjacentElems[ type ].insert( e );
9052 switch ( e->GetEntityType() ) {
9053 case SMDSEntity_Quad_Triangle:
9054 case SMDSEntity_Quad_Quadrangle:
9055 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9056 case SMDSEntity_BiQuad_Triangle:
9057 case SMDSEntity_BiQuad_Quadrangle:
9058 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9059 default: alreadyOK = true;
9064 if ( type >= elemType )
9065 continue; // same type or more complex linear element
9067 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9068 continue; // e is already checked
9072 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9073 while ( nodeIt->more() && allIn )
9074 allIn = allNodes.count( nodeIt->next() );
9076 theElements.insert(e );
9080 SMESH_MesherHelper helper(*myMesh);
9081 helper.SetIsQuadratic( true );
9082 helper.SetIsBiQuadratic( theToBiQuad );
9084 // add links of quadratic adjacent elements to the helper
9086 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9087 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9088 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9090 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9092 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9093 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9094 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9096 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9098 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9099 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9100 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9102 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9105 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9107 SMESHDS_Mesh* meshDS = GetMeshDS();
9108 SMESHDS_SubMesh* smDS = 0;
9109 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9111 const SMDS_MeshElement* elem = *eIt;
9114 int nbCentralNodes = 0;
9115 switch ( elem->GetEntityType() ) {
9116 // linear convertible
9117 case SMDSEntity_Edge:
9118 case SMDSEntity_Triangle:
9119 case SMDSEntity_Quadrangle:
9120 case SMDSEntity_Tetra:
9121 case SMDSEntity_Pyramid:
9122 case SMDSEntity_Hexa:
9123 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9124 // quadratic that can become bi-quadratic
9125 case SMDSEntity_Quad_Triangle:
9126 case SMDSEntity_Quad_Quadrangle:
9127 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9129 case SMDSEntity_BiQuad_Triangle:
9130 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9131 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9133 default: alreadyOK = true;
9135 if ( alreadyOK ) continue;
9137 const SMDSAbs_ElementType type = elem->GetType();
9138 const int id = elem->GetID();
9139 const int nbNodes = elem->NbCornerNodes();
9140 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9142 helper.SetSubShape( elem->getshapeId() );
9144 if ( !smDS || !smDS->Contains( elem ))
9145 smDS = meshDS->MeshElements( elem->getshapeId() );
9146 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9148 SMDS_MeshElement * newElem = 0;
9151 case 4: // cases for most frequently used element types go first (for optimization)
9152 if ( type == SMDSAbs_Volume )
9153 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9155 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9158 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9159 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9162 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9165 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9168 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9169 nodes[4], id, theForce3d);
9172 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9173 nodes[4], nodes[5], id, theForce3d);
9177 ReplaceElemInGroups( elem, newElem, meshDS);
9178 if( newElem && smDS )
9179 smDS->AddElement( newElem );
9181 // remove central nodes
9182 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9183 if ( nodes[i]->NbInverseElements() == 0 )
9184 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9186 } // loop on theElements
9189 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9190 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9191 // helper.FixQuadraticElements( myError );
9192 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9196 //=======================================================================
9198 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9199 * \return int - nb of checked elements
9201 //=======================================================================
9203 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9204 SMDS_ElemIteratorPtr theItr,
9205 const int theShapeID)
9208 SMESHDS_Mesh* meshDS = GetMeshDS();
9209 ElemFeatures elemType;
9210 vector<const SMDS_MeshNode *> nodes;
9212 while( theItr->more() )
9214 const SMDS_MeshElement* elem = theItr->next();
9216 if( elem && elem->IsQuadratic())
9219 int nbCornerNodes = elem->NbCornerNodes();
9220 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9222 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9224 //remove a quadratic element
9225 if ( !theSm || !theSm->Contains( elem ))
9226 theSm = meshDS->MeshElements( elem->getshapeId() );
9227 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9229 // remove medium nodes
9230 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9231 if ( nodes[i]->NbInverseElements() == 0 )
9232 meshDS->RemoveFreeNode( nodes[i], theSm );
9234 // add a linear element
9235 nodes.resize( nbCornerNodes );
9236 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9237 ReplaceElemInGroups(elem, newElem, meshDS);
9238 if( theSm && newElem )
9239 theSm->AddElement( newElem );
9245 //=======================================================================
9246 //function : ConvertFromQuadratic
9248 //=======================================================================
9250 bool SMESH_MeshEditor::ConvertFromQuadratic()
9252 int nbCheckedElems = 0;
9253 if ( myMesh->HasShapeToMesh() )
9255 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9257 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9258 while ( smIt->more() ) {
9259 SMESH_subMesh* sm = smIt->next();
9260 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9261 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9267 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9268 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9270 SMESHDS_SubMesh *aSM = 0;
9271 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9279 //================================================================================
9281 * \brief Return true if all medium nodes of the element are in the node set
9283 //================================================================================
9285 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9287 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9288 if ( !nodeSet.count( elem->GetNode(i) ))
9294 //================================================================================
9296 * \brief Makes given elements linear
9298 //================================================================================
9300 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9302 if ( theElements.empty() ) return;
9304 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9305 set<int> mediumNodeIDs;
9306 TIDSortedElemSet::iterator eIt = theElements.begin();
9307 for ( ; eIt != theElements.end(); ++eIt )
9309 const SMDS_MeshElement* e = *eIt;
9310 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9311 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9314 // replace given elements by linear ones
9315 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9316 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9318 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9319 // except those elements sharing medium nodes of quadratic element whose medium nodes
9320 // are not all in mediumNodeIDs
9322 // get remaining medium nodes
9323 TIDSortedNodeSet mediumNodes;
9324 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9325 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9326 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9327 mediumNodes.insert( mediumNodes.end(), n );
9329 // find more quadratic elements to convert
9330 TIDSortedElemSet moreElemsToConvert;
9331 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9332 for ( ; nIt != mediumNodes.end(); ++nIt )
9334 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9335 while ( invIt->more() )
9337 const SMDS_MeshElement* e = invIt->next();
9338 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9340 // find a more complex element including e and
9341 // whose medium nodes are not in mediumNodes
9342 bool complexFound = false;
9343 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9345 SMDS_ElemIteratorPtr invIt2 =
9346 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9347 while ( invIt2->more() )
9349 const SMDS_MeshElement* eComplex = invIt2->next();
9350 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9352 int nbCommonNodes = SMESH_MeshAlgos::NbCommonNodes( e, eComplex );
9353 if ( nbCommonNodes == e->NbNodes())
9355 complexFound = true;
9356 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9362 if ( !complexFound )
9363 moreElemsToConvert.insert( e );
9367 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9368 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9371 //=======================================================================
9372 //function : SewSideElements
9374 //=======================================================================
9376 SMESH_MeshEditor::Sew_Error
9377 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9378 TIDSortedElemSet& theSide2,
9379 const SMDS_MeshNode* theFirstNode1,
9380 const SMDS_MeshNode* theFirstNode2,
9381 const SMDS_MeshNode* theSecondNode1,
9382 const SMDS_MeshNode* theSecondNode2)
9386 if ( theSide1.size() != theSide2.size() )
9387 return SEW_DIFF_NB_OF_ELEMENTS;
9389 Sew_Error aResult = SEW_OK;
9391 // 1. Build set of faces representing each side
9392 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9393 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9395 // =======================================================================
9396 // 1. Build set of faces representing each side:
9397 // =======================================================================
9398 // a. build set of nodes belonging to faces
9399 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9400 // c. create temporary faces representing side of volumes if correspondent
9401 // face does not exist
9403 SMESHDS_Mesh* aMesh = GetMeshDS();
9404 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9405 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9406 TIDSortedElemSet faceSet1, faceSet2;
9407 set<const SMDS_MeshElement*> volSet1, volSet2;
9408 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9409 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9410 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9411 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9412 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9413 int iSide, iFace, iNode;
9415 list<const SMDS_MeshElement* > tempFaceList;
9416 for ( iSide = 0; iSide < 2; iSide++ ) {
9417 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9418 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9419 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9420 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9421 set<const SMDS_MeshElement*>::iterator vIt;
9422 TIDSortedElemSet::iterator eIt;
9423 set<const SMDS_MeshNode*>::iterator nIt;
9425 // check that given nodes belong to given elements
9426 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9427 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9428 int firstIndex = -1, secondIndex = -1;
9429 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9430 const SMDS_MeshElement* elem = *eIt;
9431 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9432 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9433 if ( firstIndex > -1 && secondIndex > -1 ) break;
9435 if ( firstIndex < 0 || secondIndex < 0 ) {
9436 // we can simply return until temporary faces created
9437 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9440 // -----------------------------------------------------------
9441 // 1a. Collect nodes of existing faces
9442 // and build set of face nodes in order to detect missing
9443 // faces corresponding to sides of volumes
9444 // -----------------------------------------------------------
9446 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9448 // loop on the given element of a side
9449 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9450 //const SMDS_MeshElement* elem = *eIt;
9451 const SMDS_MeshElement* elem = *eIt;
9452 if ( elem->GetType() == SMDSAbs_Face ) {
9453 faceSet->insert( elem );
9454 set <const SMDS_MeshNode*> faceNodeSet;
9455 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9456 while ( nodeIt->more() ) {
9457 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9458 nodeSet->insert( n );
9459 faceNodeSet.insert( n );
9461 setOfFaceNodeSet.insert( faceNodeSet );
9463 else if ( elem->GetType() == SMDSAbs_Volume )
9464 volSet->insert( elem );
9466 // ------------------------------------------------------------------------------
9467 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9468 // ------------------------------------------------------------------------------
9470 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9471 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9472 while ( fIt->more() ) { // loop on faces sharing a node
9473 const SMDS_MeshElement* f = fIt->next();
9474 if ( faceSet->find( f ) == faceSet->end() ) {
9475 // check if all nodes are in nodeSet and
9476 // complete setOfFaceNodeSet if they are
9477 set <const SMDS_MeshNode*> faceNodeSet;
9478 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9479 bool allInSet = true;
9480 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9481 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9482 if ( nodeSet->find( n ) == nodeSet->end() )
9485 faceNodeSet.insert( n );
9488 faceSet->insert( f );
9489 setOfFaceNodeSet.insert( faceNodeSet );
9495 // -------------------------------------------------------------------------
9496 // 1c. Create temporary faces representing sides of volumes if correspondent
9497 // face does not exist
9498 // -------------------------------------------------------------------------
9500 if ( !volSet->empty() ) {
9501 //int nodeSetSize = nodeSet->size();
9503 // loop on given volumes
9504 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9505 SMDS_VolumeTool vol (*vIt);
9506 // loop on volume faces: find free faces
9507 // --------------------------------------
9508 list<const SMDS_MeshElement* > freeFaceList;
9509 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9510 if ( !vol.IsFreeFace( iFace ))
9512 // check if there is already a face with same nodes in a face set
9513 const SMDS_MeshElement* aFreeFace = 0;
9514 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9515 int nbNodes = vol.NbFaceNodes( iFace );
9516 set <const SMDS_MeshNode*> faceNodeSet;
9517 vol.GetFaceNodes( iFace, faceNodeSet );
9518 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9520 // no such a face is given but it still can exist, check it
9521 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9522 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9525 // create a temporary face
9526 if ( nbNodes == 3 ) {
9527 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9528 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9530 else if ( nbNodes == 4 ) {
9531 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9532 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9535 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9536 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9537 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9540 tempFaceList.push_back( aFreeFace );
9544 freeFaceList.push_back( aFreeFace );
9546 } // loop on faces of a volume
9548 // choose one of several free faces of a volume
9549 // --------------------------------------------
9550 if ( freeFaceList.size() > 1 ) {
9551 // choose a face having max nb of nodes shared by other elems of a side
9552 int maxNbNodes = -1;
9553 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9554 while ( fIt != freeFaceList.end() ) { // loop on free faces
9555 int nbSharedNodes = 0;
9556 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9557 while ( nodeIt->more() ) { // loop on free face nodes
9558 const SMDS_MeshNode* n =
9559 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9560 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9561 while ( invElemIt->more() ) {
9562 const SMDS_MeshElement* e = invElemIt->next();
9563 nbSharedNodes += faceSet->count( e );
9564 nbSharedNodes += elemSet->count( e );
9567 if ( nbSharedNodes > maxNbNodes ) {
9568 maxNbNodes = nbSharedNodes;
9569 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9571 else if ( nbSharedNodes == maxNbNodes ) {
9575 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9578 if ( freeFaceList.size() > 1 )
9580 // could not choose one face, use another way
9581 // choose a face most close to the bary center of the opposite side
9582 gp_XYZ aBC( 0., 0., 0. );
9583 set <const SMDS_MeshNode*> addedNodes;
9584 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9585 eIt = elemSet2->begin();
9586 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9587 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9588 while ( nodeIt->more() ) { // loop on free face nodes
9589 const SMDS_MeshNode* n =
9590 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9591 if ( addedNodes.insert( n ).second )
9592 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9595 aBC /= addedNodes.size();
9596 double minDist = DBL_MAX;
9597 fIt = freeFaceList.begin();
9598 while ( fIt != freeFaceList.end() ) { // loop on free faces
9600 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9601 while ( nodeIt->more() ) { // loop on free face nodes
9602 const SMDS_MeshNode* n =
9603 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9604 gp_XYZ p( n->X(),n->Y(),n->Z() );
9605 dist += ( aBC - p ).SquareModulus();
9607 if ( dist < minDist ) {
9609 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9612 fIt = freeFaceList.erase( fIt++ );
9615 } // choose one of several free faces of a volume
9617 if ( freeFaceList.size() == 1 ) {
9618 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9619 faceSet->insert( aFreeFace );
9620 // complete a node set with nodes of a found free face
9621 // for ( iNode = 0; iNode < ; iNode++ )
9622 // nodeSet->insert( fNodes[ iNode ] );
9625 } // loop on volumes of a side
9627 // // complete a set of faces if new nodes in a nodeSet appeared
9628 // // ----------------------------------------------------------
9629 // if ( nodeSetSize != nodeSet->size() ) {
9630 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9631 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9632 // while ( fIt->more() ) { // loop on faces sharing a node
9633 // const SMDS_MeshElement* f = fIt->next();
9634 // if ( faceSet->find( f ) == faceSet->end() ) {
9635 // // check if all nodes are in nodeSet and
9636 // // complete setOfFaceNodeSet if they are
9637 // set <const SMDS_MeshNode*> faceNodeSet;
9638 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9639 // bool allInSet = true;
9640 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9641 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9642 // if ( nodeSet->find( n ) == nodeSet->end() )
9643 // allInSet = false;
9645 // faceNodeSet.insert( n );
9647 // if ( allInSet ) {
9648 // faceSet->insert( f );
9649 // setOfFaceNodeSet.insert( faceNodeSet );
9655 } // Create temporary faces, if there are volumes given
9658 if ( faceSet1.size() != faceSet2.size() ) {
9659 // delete temporary faces: they are in reverseElements of actual nodes
9660 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9661 // while ( tmpFaceIt->more() )
9662 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9663 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9664 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9665 // aMesh->RemoveElement(*tmpFaceIt);
9666 MESSAGE("Diff nb of faces");
9667 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9670 // ============================================================
9671 // 2. Find nodes to merge:
9672 // bind a node to remove to a node to put instead
9673 // ============================================================
9675 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9676 if ( theFirstNode1 != theFirstNode2 )
9677 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9678 if ( theSecondNode1 != theSecondNode2 )
9679 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9681 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9682 set< long > linkIdSet; // links to process
9683 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9685 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9686 list< NLink > linkList[2];
9687 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9688 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9689 // loop on links in linkList; find faces by links and append links
9690 // of the found faces to linkList
9691 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9692 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9694 NLink link[] = { *linkIt[0], *linkIt[1] };
9695 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9696 if ( !linkIdSet.count( linkID ) )
9699 // by links, find faces in the face sets,
9700 // and find indices of link nodes in the found faces;
9701 // in a face set, there is only one or no face sharing a link
9702 // ---------------------------------------------------------------
9704 const SMDS_MeshElement* face[] = { 0, 0 };
9705 vector<const SMDS_MeshNode*> fnodes[2];
9706 int iLinkNode[2][2];
9707 TIDSortedElemSet avoidSet;
9708 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9709 const SMDS_MeshNode* n1 = link[iSide].first;
9710 const SMDS_MeshNode* n2 = link[iSide].second;
9711 //cout << "Side " << iSide << " ";
9712 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9713 // find a face by two link nodes
9714 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9715 *faceSetPtr[ iSide ], avoidSet,
9716 &iLinkNode[iSide][0],
9717 &iLinkNode[iSide][1] );
9720 //cout << " F " << face[ iSide]->GetID() <<endl;
9721 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9722 // put face nodes to fnodes
9723 SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9724 fnodes[ iSide ].assign( nIt, nEnd );
9725 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9729 // check similarity of elements of the sides
9730 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9731 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9732 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9733 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9736 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9738 break; // do not return because it's necessary to remove tmp faces
9741 // set nodes to merge
9742 // -------------------
9744 if ( face[0] && face[1] ) {
9745 const int nbNodes = face[0]->NbNodes();
9746 if ( nbNodes != face[1]->NbNodes() ) {
9747 MESSAGE("Diff nb of face nodes");
9748 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9749 break; // do not return because it s necessary to remove tmp faces
9751 bool reverse[] = { false, false }; // order of nodes in the link
9752 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9753 // analyse link orientation in faces
9754 int i1 = iLinkNode[ iSide ][ 0 ];
9755 int i2 = iLinkNode[ iSide ][ 1 ];
9756 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9758 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9759 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9760 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9762 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9763 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9766 // add other links of the faces to linkList
9767 // -----------------------------------------
9769 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9770 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9771 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9772 if ( !iter_isnew.second ) { // already in a set: no need to process
9773 linkIdSet.erase( iter_isnew.first );
9775 else // new in set == encountered for the first time: add
9777 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9778 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9779 linkList[0].push_back ( NLink( n1, n2 ));
9780 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9785 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9788 } // loop on link lists
9790 if ( aResult == SEW_OK &&
9791 ( //linkIt[0] != linkList[0].end() ||
9792 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9793 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9794 " " << (faceSetPtr[1]->empty()));
9795 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9798 // ====================================================================
9799 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9800 // ====================================================================
9802 // delete temporary faces
9803 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9804 // while ( tmpFaceIt->more() )
9805 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9806 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9807 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9808 aMesh->RemoveElement(*tmpFaceIt);
9810 if ( aResult != SEW_OK)
9813 list< int > nodeIDsToRemove;
9814 vector< const SMDS_MeshNode*> nodes;
9815 ElemFeatures elemType;
9817 // loop on nodes replacement map
9818 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9819 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9820 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9822 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9823 nodeIDsToRemove.push_back( nToRemove->GetID() );
9824 // loop on elements sharing nToRemove
9825 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9826 while ( invElemIt->more() ) {
9827 const SMDS_MeshElement* e = invElemIt->next();
9828 // get a new suite of nodes: make replacement
9829 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9830 nodes.resize( nbNodes );
9831 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9832 while ( nIt->more() ) {
9833 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9834 nnIt = nReplaceMap.find( n );
9835 if ( nnIt != nReplaceMap.end() ) {
9841 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9842 // elemIDsToRemove.push_back( e->GetID() );
9846 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9847 aMesh->RemoveElement( e );
9849 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9851 AddToSameGroups( newElem, e, aMesh );
9852 if ( int aShapeId = e->getshapeId() )
9853 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9859 Remove( nodeIDsToRemove, true );
9864 //================================================================================
9866 * \brief Find corresponding nodes in two sets of faces
9867 * \param theSide1 - first face set
9868 * \param theSide2 - second first face
9869 * \param theFirstNode1 - a boundary node of set 1
9870 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9871 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9872 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9873 * \param nReplaceMap - output map of corresponding nodes
9874 * \return bool - is a success or not
9876 //================================================================================
9879 //#define DEBUG_MATCHING_NODES
9882 SMESH_MeshEditor::Sew_Error
9883 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9884 set<const SMDS_MeshElement*>& theSide2,
9885 const SMDS_MeshNode* theFirstNode1,
9886 const SMDS_MeshNode* theFirstNode2,
9887 const SMDS_MeshNode* theSecondNode1,
9888 const SMDS_MeshNode* theSecondNode2,
9889 TNodeNodeMap & nReplaceMap)
9891 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9893 nReplaceMap.clear();
9894 //if ( theFirstNode1 != theFirstNode2 )
9895 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9896 //if ( theSecondNode1 != theSecondNode2 )
9897 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9899 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9900 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9902 list< NLink > linkList[2];
9903 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9904 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9906 // loop on links in linkList; find faces by links and append links
9907 // of the found faces to linkList
9908 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9909 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9910 NLink link[] = { *linkIt[0], *linkIt[1] };
9911 if ( linkSet.find( link[0] ) == linkSet.end() )
9914 // by links, find faces in the face sets,
9915 // and find indices of link nodes in the found faces;
9916 // in a face set, there is only one or no face sharing a link
9917 // ---------------------------------------------------------------
9919 const SMDS_MeshElement* face[] = { 0, 0 };
9920 list<const SMDS_MeshNode*> notLinkNodes[2];
9921 //bool reverse[] = { false, false }; // order of notLinkNodes
9923 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9925 const SMDS_MeshNode* n1 = link[iSide].first;
9926 const SMDS_MeshNode* n2 = link[iSide].second;
9927 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9928 set< const SMDS_MeshElement* > facesOfNode1;
9929 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9931 // during a loop of the first node, we find all faces around n1,
9932 // during a loop of the second node, we find one face sharing both n1 and n2
9933 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9934 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9935 while ( fIt->more() ) { // loop on faces sharing a node
9936 const SMDS_MeshElement* f = fIt->next();
9937 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9938 ! facesOfNode1.insert( f ).second ) // f encounters twice
9940 if ( face[ iSide ] ) {
9941 MESSAGE( "2 faces per link " );
9942 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9945 faceSet->erase( f );
9947 // get not link nodes
9948 int nbN = f->NbNodes();
9949 if ( f->IsQuadratic() )
9951 nbNodes[ iSide ] = nbN;
9952 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9953 int i1 = f->GetNodeIndex( n1 );
9954 int i2 = f->GetNodeIndex( n2 );
9955 int iEnd = nbN, iBeg = -1, iDelta = 1;
9956 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9958 std::swap( iEnd, iBeg ); iDelta = -1;
9963 if ( i == iEnd ) i = iBeg + iDelta;
9964 if ( i == i1 ) break;
9965 nodes.push_back ( f->GetNode( i ) );
9971 // check similarity of elements of the sides
9972 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9973 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9974 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9975 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9978 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9982 // set nodes to merge
9983 // -------------------
9985 if ( face[0] && face[1] ) {
9986 if ( nbNodes[0] != nbNodes[1] ) {
9987 MESSAGE("Diff nb of face nodes");
9988 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9990 #ifdef DEBUG_MATCHING_NODES
9991 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9992 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9993 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9995 int nbN = nbNodes[0];
9997 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9998 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9999 for ( int i = 0 ; i < nbN - 2; ++i ) {
10000 #ifdef DEBUG_MATCHING_NODES
10001 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10003 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10007 // add other links of the face 1 to linkList
10008 // -----------------------------------------
10010 const SMDS_MeshElement* f0 = face[0];
10011 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10012 for ( int i = 0; i < nbN; i++ )
10014 const SMDS_MeshNode* n2 = f0->GetNode( i );
10015 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10016 linkSet.insert( SMESH_TLink( n1, n2 ));
10017 if ( !iter_isnew.second ) { // already in a set: no need to process
10018 linkSet.erase( iter_isnew.first );
10020 else // new in set == encountered for the first time: add
10022 #ifdef DEBUG_MATCHING_NODES
10023 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10024 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10026 linkList[0].push_back ( NLink( n1, n2 ));
10027 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10032 } // loop on link lists
10037 namespace // automatically find theAffectedElems for DoubleNodes()
10039 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10041 //--------------------------------------------------------------------------------
10042 // Nodes shared by adjacent FissureBorder's.
10043 // 1 node if FissureBorder separates faces
10044 // 2 nodes if FissureBorder separates volumes
10047 const SMDS_MeshNode* _nodes[2];
10050 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10054 _nbNodes = bool( n1 ) + bool( n2 );
10055 if ( _nbNodes == 2 && n1 > n2 )
10056 std::swap( _nodes[0], _nodes[1] );
10058 bool operator<( const SubBorder& other ) const
10060 for ( int i = 0; i < _nbNodes; ++i )
10062 if ( _nodes[i] < other._nodes[i] ) return true;
10063 if ( _nodes[i] > other._nodes[i] ) return false;
10069 //--------------------------------------------------------------------------------
10070 // Map a SubBorder to all FissureBorder it bounds
10071 struct FissureBorder;
10072 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10073 typedef TBorderLinks::iterator TMappedSub;
10075 //--------------------------------------------------------------------------------
10077 * \brief Element border (volume facet or face edge) at a fissure
10079 struct FissureBorder
10081 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10082 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10084 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10085 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10087 FissureBorder( FissureBorder && from ) // move constructor
10089 std::swap( _nodes, from._nodes );
10090 std::swap( _sortedNodes, from._sortedNodes );
10091 _elems[0] = from._elems[0];
10092 _elems[1] = from._elems[1];
10095 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10096 std::vector< const SMDS_MeshElement* > & adjElems)
10097 : _nodes( elemToDuplicate->NbCornerNodes() )
10099 for ( size_t i = 0; i < _nodes.size(); ++i )
10100 _nodes[i] = elemToDuplicate->GetNode( i );
10102 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10103 findAdjacent( type, adjElems );
10106 FissureBorder( const SMDS_MeshNode** nodes,
10107 const size_t nbNodes,
10108 const SMDSAbs_ElementType adjElemsType,
10109 std::vector< const SMDS_MeshElement* > & adjElems)
10110 : _nodes( nodes, nodes + nbNodes )
10112 findAdjacent( adjElemsType, adjElems );
10115 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10116 std::vector< const SMDS_MeshElement* > & adjElems)
10118 _elems[0] = _elems[1] = 0;
10120 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10121 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10122 _elems[i] = adjElems[i];
10125 bool operator<( const FissureBorder& other ) const
10127 return GetSortedNodes() < other.GetSortedNodes();
10130 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10132 if ( _sortedNodes.empty() && !_nodes.empty() )
10134 FissureBorder* me = const_cast<FissureBorder*>( this );
10135 me->_sortedNodes = me->_nodes;
10136 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10138 return _sortedNodes;
10141 size_t NbSub() const
10143 return _nodes.size();
10146 SubBorder Sub(size_t i) const
10148 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10151 void AddSelfTo( TBorderLinks& borderLinks )
10153 _mappedSubs.resize( NbSub() );
10154 for ( size_t i = 0; i < NbSub(); ++i )
10156 TBorderLinks::iterator s2b =
10157 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10158 s2b->second.push_back( this );
10159 _mappedSubs[ i ] = s2b;
10168 const SMDS_MeshElement* GetMarkedElem() const
10170 if ( _nodes.empty() ) return 0; // cleared
10171 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10172 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10176 gp_XYZ GetNorm() const // normal to the border
10179 if ( _nodes.size() == 2 )
10181 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10182 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10184 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10187 gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10188 norm = bordDir ^ avgNorm;
10192 SMESH_NodeXYZ p0( _nodes[0] );
10193 SMESH_NodeXYZ p1( _nodes[1] );
10194 SMESH_NodeXYZ p2( _nodes[2] );
10195 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10197 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10203 void ChooseSide() // mark an _elem located at positive side of fissure
10205 _elems[0]->setIsMarked( true );
10206 gp_XYZ norm = GetNorm();
10207 double maxX = norm.Coord(1);
10208 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10209 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10212 _elems[0]->setIsMarked( false );
10213 _elems[1]->setIsMarked( true );
10217 }; // struct FissureBorder
10219 //--------------------------------------------------------------------------------
10221 * \brief Classifier of elements at fissure edge
10223 class FissureNormal
10225 std::vector< gp_XYZ > _normals;
10229 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10232 _normals.reserve(2);
10233 _normals.push_back( bord.GetNorm() );
10234 if ( _normals.size() == 2 )
10235 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10238 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10241 switch ( _normals.size() ) {
10244 isIn = !isOut( n, _normals[0], elem );
10249 bool in1 = !isOut( n, _normals[0], elem );
10250 bool in2 = !isOut( n, _normals[1], elem );
10251 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10258 //================================================================================
10260 * \brief Classify an element by a plane passing through a node
10262 //================================================================================
10264 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10266 SMESH_NodeXYZ p = n;
10268 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10270 SMESH_NodeXYZ pi = elem->GetNode( i );
10271 sumDot += norm * ( pi - p );
10273 return sumDot < -1e-100;
10276 //================================================================================
10278 * \brief Find FissureBorder's by nodes to duplicate
10280 //================================================================================
10282 void findFissureBorders( const TIDSortedElemSet& theNodes,
10283 std::vector< FissureBorder > & theFissureBorders )
10285 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10286 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10288 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10289 if ( n->NbInverseElements( elemType ) == 0 )
10291 elemType = SMDSAbs_Face;
10292 if ( n->NbInverseElements( elemType ) == 0 )
10295 // unmark elements touching the fissure
10296 for ( ; nIt != theNodes.end(); ++nIt )
10297 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10299 // loop on elements touching the fissure to get their borders belonging to the fissure
10300 std::set< FissureBorder > fissureBorders;
10301 std::vector< const SMDS_MeshElement* > adjElems;
10302 std::vector< const SMDS_MeshNode* > nodes;
10303 SMDS_VolumeTool volTool;
10304 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10306 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10307 while ( invIt->more() )
10309 const SMDS_MeshElement* eInv = invIt->next();
10310 if ( eInv->isMarked() ) continue;
10311 eInv->setIsMarked( true );
10313 if ( elemType == SMDSAbs_Volume )
10315 volTool.Set( eInv );
10316 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10317 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10319 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10320 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10322 bool allOnFissure = true;
10323 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10324 if (( allOnFissure = theNodes.count( nn[ iN ])))
10325 nodes.push_back( nn[ iN ]);
10326 if ( allOnFissure )
10327 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10328 elemType, adjElems )));
10331 else // elemType == SMDSAbs_Face
10333 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10334 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10335 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10337 nn[1] = eInv->GetNode( iN );
10338 onFissure1 = theNodes.count( nn[1] );
10339 if ( onFissure0 && onFissure1 )
10340 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10342 onFissure0 = onFissure1;
10348 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10349 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10350 for ( ; bord != fissureBorders.end(); ++bord )
10352 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10355 } // findFissureBorders()
10357 //================================================================================
10359 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10360 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10361 * \param [in] theNodesNot - nodes not to duplicate
10362 * \param [out] theAffectedElems - the found elements
10364 //================================================================================
10366 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10367 TIDSortedElemSet& theAffectedElems)
10369 if ( theElemsOrNodes.empty() ) return;
10371 // find FissureBorder's
10373 std::vector< FissureBorder > fissure;
10374 std::vector< const SMDS_MeshElement* > elemsByFacet;
10376 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10377 if ( (*elIt)->GetType() == SMDSAbs_Node )
10379 findFissureBorders( theElemsOrNodes, fissure );
10383 fissure.reserve( theElemsOrNodes.size() );
10384 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10385 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10387 if ( fissure.empty() )
10390 // fill borderLinks
10392 TBorderLinks borderLinks;
10394 for ( size_t i = 0; i < fissure.size(); ++i )
10396 fissure[i].AddSelfTo( borderLinks );
10399 // get theAffectedElems
10401 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10402 for ( size_t i = 0; i < fissure.size(); ++i )
10403 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10405 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10406 false, /*markElem=*/true );
10409 std::vector<const SMDS_MeshNode *> facetNodes;
10410 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10411 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10413 // choose a side of fissure
10414 fissure[0].ChooseSide();
10415 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10417 size_t nbCheckedBorders = 0;
10418 while ( nbCheckedBorders < fissure.size() )
10420 // find a FissureBorder to treat
10421 FissureBorder* bord = 0;
10422 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10423 if ( fissure[i].GetMarkedElem() )
10424 bord = & fissure[i];
10425 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10426 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10428 bord = & fissure[i];
10429 bord->ChooseSide();
10430 theAffectedElems.insert( bord->GetMarkedElem() );
10432 if ( !bord ) return;
10433 ++nbCheckedBorders;
10435 // treat FissureBorder's linked to bord
10436 fissureNodes.clear();
10437 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10438 for ( size_t i = 0; i < bord->NbSub(); ++i )
10440 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10441 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10442 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10443 const SubBorder& sb = l2b->first;
10444 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10446 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10448 for ( int j = 0; j < sb._nbNodes; ++j )
10449 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10453 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10454 // until an elem adjacent to a neighbour FissureBorder is found
10455 facetNodes.clear();
10456 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10457 facetNodes.resize( sb._nbNodes + 1 );
10461 // check if bordElem is adjacent to a neighbour FissureBorder
10462 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10464 FissureBorder* bord2 = linkedBorders[j];
10465 if ( bord2 == bord ) continue;
10466 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10469 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10474 // find the next bordElem
10475 const SMDS_MeshElement* nextBordElem = 0;
10476 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10478 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10479 if ( fissureNodes.count( n )) continue;
10481 facetNodes[ sb._nbNodes ] = n;
10482 elemsByFacet.clear();
10483 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10485 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10486 if ( elemsByFacet[ iE ] != bordElem &&
10487 !elemsByFacet[ iE ]->isMarked() )
10489 theAffectedElems.insert( elemsByFacet[ iE ]);
10490 elemsByFacet[ iE ]->setIsMarked( true );
10491 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10492 nextBordElem = elemsByFacet[ iE ];
10496 bordElem = nextBordElem;
10498 } // while ( bordElem )
10500 linkedBorders.clear(); // not to treat this link any more
10502 } // loop on SubBorder's of a FissureBorder
10506 } // loop on FissureBorder's
10509 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10511 // mark nodes of theAffectedElems
10512 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10514 // unmark nodes of the fissure
10515 elIt = theElemsOrNodes.begin();
10516 if ( (*elIt)->GetType() == SMDSAbs_Node )
10517 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10519 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10521 std::vector< gp_XYZ > normVec;
10523 // loop on nodes of the fissure, add elements having marked nodes
10524 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10526 const SMDS_MeshElement* e = (*elIt);
10527 if ( e->GetType() != SMDSAbs_Node )
10528 e->setIsMarked( true ); // avoid adding a fissure element
10530 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10532 const SMDS_MeshNode* n = e->GetNode( iN );
10533 if ( fissEdgeNodes2Norm.count( n ))
10536 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10537 while ( invIt->more() )
10539 const SMDS_MeshElement* eInv = invIt->next();
10540 if ( eInv->isMarked() ) continue;
10541 eInv->setIsMarked( true );
10543 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10544 while( nIt->more() )
10545 if ( nIt->next()->isMarked())
10547 theAffectedElems.insert( eInv );
10548 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10549 n->setIsMarked( false );
10556 // add elements on the fissure edge
10557 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10558 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10560 const SMDS_MeshNode* edgeNode = n2N->first;
10561 const FissureNormal & normals = n2N->second;
10563 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10564 while ( invIt->more() )
10566 const SMDS_MeshElement* eInv = invIt->next();
10567 if ( eInv->isMarked() ) continue;
10568 eInv->setIsMarked( true );
10570 // classify eInv using normals
10571 bool toAdd = normals.IsIn( edgeNode, eInv );
10572 if ( toAdd ) // check if all nodes lie on the fissure edge
10574 bool notOnEdge = false;
10575 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
10576 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10581 theAffectedElems.insert( eInv );
10587 } // findAffectedElems()
10590 //================================================================================
10592 * \brief Create elements equal (on same nodes) to given ones
10593 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10594 * elements of the uppest dimension are duplicated.
10596 //================================================================================
10598 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10600 ClearLastCreated();
10601 SMESHDS_Mesh* mesh = GetMeshDS();
10603 // get an element type and an iterator over elements
10605 SMDSAbs_ElementType type = SMDSAbs_All;
10606 SMDS_ElemIteratorPtr elemIt;
10607 if ( theElements.empty() )
10609 if ( mesh->NbNodes() == 0 )
10611 // get most complex type
10612 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10613 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10614 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10616 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10617 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10620 elemIt = mesh->elementsIterator( type );
10626 //type = (*theElements.begin())->GetType();
10627 elemIt = SMESHUtils::elemSetIterator( theElements );
10630 // un-mark all elements to avoid duplicating just created elements
10631 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10633 // duplicate elements
10635 ElemFeatures elemType;
10637 vector< const SMDS_MeshNode* > nodes;
10638 while ( elemIt->more() )
10640 const SMDS_MeshElement* elem = elemIt->next();
10641 if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10642 ( elem->isMarked() ))
10645 elemType.Init( elem, /*basicOnly=*/false );
10646 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10648 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10649 newElem->setIsMarked( true );
10653 //================================================================================
10655 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10656 \param theElems - the list of elements (edges or faces) to be replicated
10657 The nodes for duplication could be found from these elements
10658 \param theNodesNot - list of nodes to NOT replicate
10659 \param theAffectedElems - the list of elements (cells and edges) to which the
10660 replicated nodes should be associated to.
10661 \return TRUE if operation has been completed successfully, FALSE otherwise
10663 //================================================================================
10665 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10666 const TIDSortedElemSet& theNodesNot,
10667 const TIDSortedElemSet& theAffectedElems )
10669 ClearLastCreated();
10671 if ( theElems.size() == 0 )
10674 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10679 TNodeNodeMap anOldNodeToNewNode;
10680 // duplicate elements and nodes
10681 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10682 // replce nodes by duplications
10683 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10687 //================================================================================
10689 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10690 \param theMeshDS - mesh instance
10691 \param theElems - the elements replicated or modified (nodes should be changed)
10692 \param theNodesNot - nodes to NOT replicate
10693 \param theNodeNodeMap - relation of old node to new created node
10694 \param theIsDoubleElem - flag os to replicate element or modify
10695 \return TRUE if operation has been completed successfully, FALSE otherwise
10697 //================================================================================
10699 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10700 const TIDSortedElemSet& theElems,
10701 const TIDSortedElemSet& theNodesNot,
10702 TNodeNodeMap& theNodeNodeMap,
10703 const bool theIsDoubleElem )
10705 // iterate through element and duplicate them (by nodes duplication)
10707 std::vector<const SMDS_MeshNode*> newNodes;
10708 ElemFeatures elemType;
10710 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10711 for ( ; elemItr != theElems.end(); ++elemItr )
10713 const SMDS_MeshElement* anElem = *elemItr;
10717 // duplicate nodes to duplicate element
10718 bool isDuplicate = false;
10719 newNodes.resize( anElem->NbNodes() );
10720 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10722 while ( anIter->more() )
10724 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10725 const SMDS_MeshNode* aNewNode = aCurrNode;
10726 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10727 if ( n2n != theNodeNodeMap.end() )
10729 aNewNode = n2n->second;
10731 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10734 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10735 copyPosition( aCurrNode, aNewNode );
10736 theNodeNodeMap[ aCurrNode ] = aNewNode;
10737 myLastCreatedNodes.push_back( aNewNode );
10739 isDuplicate |= (aCurrNode != aNewNode);
10740 newNodes[ ind++ ] = aNewNode;
10742 if ( !isDuplicate )
10745 if ( theIsDoubleElem )
10746 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10748 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10755 //================================================================================
10757 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10758 \param theNodes - identifiers of nodes to be doubled
10759 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10760 nodes. If list of element identifiers is empty then nodes are doubled but
10761 they not assigned to elements
10762 \return TRUE if operation has been completed successfully, FALSE otherwise
10764 //================================================================================
10766 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10767 const std::list< int >& theListOfModifiedElems )
10769 ClearLastCreated();
10771 if ( theListOfNodes.size() == 0 )
10774 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10778 // iterate through nodes and duplicate them
10780 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10782 std::list< int >::const_iterator aNodeIter;
10783 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10785 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10791 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10794 copyPosition( aNode, aNewNode );
10795 anOldNodeToNewNode[ aNode ] = aNewNode;
10796 myLastCreatedNodes.push_back( aNewNode );
10800 // Change nodes of elements
10802 std::vector<const SMDS_MeshNode*> aNodeArr;
10804 std::list< int >::const_iterator anElemIter;
10805 for ( anElemIter = theListOfModifiedElems.begin();
10806 anElemIter != theListOfModifiedElems.end();
10809 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10813 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10814 for( size_t i = 0; i < aNodeArr.size(); ++i )
10816 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10817 anOldNodeToNewNode.find( aNodeArr[ i ]);
10818 if ( n2n != anOldNodeToNewNode.end() )
10819 aNodeArr[ i ] = n2n->second;
10821 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10829 //================================================================================
10831 \brief Check if element located inside shape
10832 \return TRUE if IN or ON shape, FALSE otherwise
10834 //================================================================================
10836 template<class Classifier>
10837 bool isInside(const SMDS_MeshElement* theElem,
10838 Classifier& theClassifier,
10839 const double theTol)
10841 gp_XYZ centerXYZ (0, 0, 0);
10842 for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10843 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10845 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10846 theClassifier.Perform(aPnt, theTol);
10847 TopAbs_State aState = theClassifier.State();
10848 return (aState == TopAbs_IN || aState == TopAbs_ON );
10851 //================================================================================
10853 * \brief Classifier of the 3D point on the TopoDS_Face
10854 * with interaface suitable for isInside()
10856 //================================================================================
10858 struct _FaceClassifier
10860 Extrema_ExtPS _extremum;
10861 BRepAdaptor_Surface _surface;
10862 TopAbs_State _state;
10864 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10866 _extremum.Initialize( _surface,
10867 _surface.FirstUParameter(), _surface.LastUParameter(),
10868 _surface.FirstVParameter(), _surface.LastVParameter(),
10869 _surface.Tolerance(), _surface.Tolerance() );
10871 void Perform(const gp_Pnt& aPnt, double theTol)
10874 _state = TopAbs_OUT;
10875 _extremum.Perform(aPnt);
10876 if ( _extremum.IsDone() )
10877 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10878 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10880 TopAbs_State State() const
10887 //================================================================================
10889 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10890 This method is the first step of DoubleNodeElemGroupsInRegion.
10891 \param theElems - list of groups of elements (edges or faces) to be replicated
10892 \param theNodesNot - list of groups of nodes not to replicated
10893 \param theShape - shape to detect affected elements (element which geometric center
10894 located on or inside shape). If the shape is null, detection is done on faces orientations
10895 (select elements with a gravity center on the side given by faces normals).
10896 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10897 The replicated nodes should be associated to affected elements.
10899 \sa DoubleNodeElemGroupsInRegion()
10901 //================================================================================
10903 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10904 const TIDSortedElemSet& theNodesNot,
10905 const TopoDS_Shape& theShape,
10906 TIDSortedElemSet& theAffectedElems)
10908 if ( theShape.IsNull() )
10910 findAffectedElems( theElems, theAffectedElems );
10914 const double aTol = Precision::Confusion();
10915 std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10916 std::unique_ptr<_FaceClassifier> aFaceClassifier;
10917 if ( theShape.ShapeType() == TopAbs_SOLID )
10919 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10920 bsc3d->PerformInfinitePoint(aTol);
10922 else if (theShape.ShapeType() == TopAbs_FACE )
10924 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10927 // iterates on indicated elements and get elements by back references from their nodes
10928 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10929 for ( ; elemItr != theElems.end(); ++elemItr )
10931 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10932 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10933 while ( nodeItr->more() )
10935 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10936 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10938 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10939 while ( backElemItr->more() )
10941 const SMDS_MeshElement* curElem = backElemItr->next();
10942 if ( curElem && theElems.find(curElem) == theElems.end() &&
10944 isInside( curElem, *bsc3d, aTol ) :
10945 isInside( curElem, *aFaceClassifier, aTol )))
10946 theAffectedElems.insert( curElem );
10954 //================================================================================
10956 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10957 \param theElems - group of of elements (edges or faces) to be replicated
10958 \param theNodesNot - group of nodes not to replicate
10959 \param theShape - shape to detect affected elements (element which geometric center
10960 located on or inside shape).
10961 The replicated nodes should be associated to affected elements.
10962 \return TRUE if operation has been completed successfully, FALSE otherwise
10964 //================================================================================
10966 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10967 const TIDSortedElemSet& theNodesNot,
10968 const TopoDS_Shape& theShape )
10970 if ( theShape.IsNull() )
10973 const double aTol = Precision::Confusion();
10974 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10975 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
10976 if ( theShape.ShapeType() == TopAbs_SOLID )
10978 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10979 bsc3d->PerformInfinitePoint(aTol);
10981 else if (theShape.ShapeType() == TopAbs_FACE )
10983 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
10986 // iterates on indicated elements and get elements by back references from their nodes
10987 TIDSortedElemSet anAffected;
10988 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10989 for ( ; elemItr != theElems.end(); ++elemItr )
10991 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10995 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10996 while ( nodeItr->more() )
10998 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10999 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11001 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11002 while ( backElemItr->more() )
11004 const SMDS_MeshElement* curElem = backElemItr->next();
11005 if ( curElem && theElems.find(curElem) == theElems.end() &&
11007 isInside( curElem, *bsc3d, aTol ) :
11008 isInside( curElem, *aFaceClassifier, aTol )))
11009 anAffected.insert( curElem );
11013 return DoubleNodes( theElems, theNodesNot, anAffected );
11017 * \brief compute an oriented angle between two planes defined by four points.
11018 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11019 * @param p0 base of the rotation axe
11020 * @param p1 extremity of the rotation axe
11021 * @param g1 belongs to the first plane
11022 * @param g2 belongs to the second plane
11024 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11026 gp_Vec vref(p0, p1);
11029 gp_Vec n1 = vref.Crossed(v1);
11030 gp_Vec n2 = vref.Crossed(v2);
11032 return n2.AngleWithRef(n1, vref);
11034 catch ( Standard_Failure ) {
11036 return Max( v1.Magnitude(), v2.Magnitude() );
11040 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11041 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11042 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11043 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11044 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11045 * 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.
11046 * 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.
11047 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11048 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11049 * \param theElems - list of groups of volumes, where a group of volume is a set of
11050 * SMDS_MeshElements sorted by Id.
11051 * \param createJointElems - if TRUE, create the elements
11052 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11053 * the boundary between \a theDomains and the rest mesh
11054 * \return TRUE if operation has been completed successfully, FALSE otherwise
11056 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11057 bool createJointElems,
11058 bool onAllBoundaries)
11060 // MESSAGE("----------------------------------------------");
11061 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11062 // MESSAGE("----------------------------------------------");
11064 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11065 meshDS->BuildDownWardConnectivity(true);
11067 SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11069 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11070 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11071 // build the list of nodes shared by 2 or more domains, with their domain indexes
11073 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11074 std::map<int,int>celldom; // cell vtkId --> domain
11075 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11076 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11077 faceDomains.clear();
11079 cellDomains.clear();
11080 nodeDomains.clear();
11081 std::map<int,int> emptyMap;
11082 std::set<int> emptySet;
11085 //MESSAGE(".. Number of domains :"<<theElems.size());
11087 TIDSortedElemSet theRestDomElems;
11088 const int iRestDom = -1;
11089 const int idom0 = onAllBoundaries ? iRestDom : 0;
11090 const int nbDomains = theElems.size();
11092 // Check if the domains do not share an element
11093 for (int idom = 0; idom < nbDomains-1; idom++)
11095 // MESSAGE("... Check of domain #" << idom);
11096 const TIDSortedElemSet& domain = theElems[idom];
11097 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11098 for (; elemItr != domain.end(); ++elemItr)
11100 const SMDS_MeshElement* anElem = *elemItr;
11101 int idombisdeb = idom + 1 ;
11102 // check if the element belongs to a domain further in the list
11103 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11105 const TIDSortedElemSet& domainbis = theElems[idombis];
11106 if ( domainbis.count( anElem ))
11108 MESSAGE(".... Domain #" << idom);
11109 MESSAGE(".... Domain #" << idombis);
11110 throw SALOME_Exception("The domains are not disjoint.");
11117 for (int idom = 0; idom < nbDomains; idom++)
11120 // --- build a map (face to duplicate --> volume to modify)
11121 // with all the faces shared by 2 domains (group of elements)
11122 // and corresponding volume of this domain, for each shared face.
11123 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11125 //MESSAGE("... Neighbors of domain #" << idom);
11126 const TIDSortedElemSet& domain = theElems[idom];
11127 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11128 for (; elemItr != domain.end(); ++elemItr)
11130 const SMDS_MeshElement* anElem = *elemItr;
11133 int vtkId = anElem->GetVtkID();
11134 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11135 int neighborsVtkIds[NBMAXNEIGHBORS];
11136 int downIds[NBMAXNEIGHBORS];
11137 unsigned char downTypes[NBMAXNEIGHBORS];
11138 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11139 for (int n = 0; n < nbNeighbors; n++)
11141 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11142 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11143 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11146 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11148 // MESSAGE("Domain " << idombis);
11149 const TIDSortedElemSet& domainbis = theElems[idombis];
11150 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11152 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11154 DownIdType face(downIds[n], downTypes[n]);
11155 if (!faceDomains[face].count(idom))
11157 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11158 celldom[vtkId] = idom;
11159 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11163 theRestDomElems.insert( elem );
11164 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11165 celldom[neighborsVtkIds[n]] = iRestDom;
11173 //MESSAGE("Number of shared faces " << faceDomains.size());
11174 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11176 // --- explore the shared faces domain by domain,
11177 // explore the nodes of the face and see if they belong to a cell in the domain,
11178 // which has only a node or an edge on the border (not a shared face)
11180 for (int idomain = idom0; idomain < nbDomains; idomain++)
11182 //MESSAGE("Domain " << idomain);
11183 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11184 itface = faceDomains.begin();
11185 for (; itface != faceDomains.end(); ++itface)
11187 const std::map<int, int>& domvol = itface->second;
11188 if (!domvol.count(idomain))
11190 DownIdType face = itface->first;
11191 //MESSAGE(" --- face " << face.cellId);
11192 std::set<int> oldNodes;
11194 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11195 std::set<int>::iterator itn = oldNodes.begin();
11196 for (; itn != oldNodes.end(); ++itn)
11199 //MESSAGE(" node " << oldId);
11200 vtkCellLinks::Link l = (static_cast <vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11201 for (int i=0; i<l.ncells; i++)
11203 int vtkId = l.cells[i];
11204 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11205 if (!domain.count(anElem))
11207 int vtkType = grid->GetCellType(vtkId);
11208 int downId = grid->CellIdToDownId(vtkId);
11211 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11212 continue; // not OK at this stage of the algorithm:
11213 //no cells created after BuildDownWardConnectivity
11215 DownIdType aCell(downId, vtkType);
11216 cellDomains[aCell][idomain] = vtkId;
11217 celldom[vtkId] = idomain;
11218 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11224 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11225 // for each shared face, get the nodes
11226 // for each node, for each domain of the face, create a clone of the node
11228 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11229 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11230 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11232 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11233 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11234 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11236 //MESSAGE(".. Duplication of the nodes");
11237 for (int idomain = idom0; idomain < nbDomains; idomain++)
11239 itface = faceDomains.begin();
11240 for (; itface != faceDomains.end(); ++itface)
11242 const std::map<int, int>& domvol = itface->second;
11243 if (!domvol.count(idomain))
11245 DownIdType face = itface->first;
11246 //MESSAGE(" --- face " << face.cellId);
11247 std::set<int> oldNodes;
11249 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11250 std::set<int>::iterator itn = oldNodes.begin();
11251 for (; itn != oldNodes.end(); ++itn)
11254 if (nodeDomains[oldId].empty())
11256 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11257 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11259 std::map<int, int>::const_iterator itdom = domvol.begin();
11260 for (; itdom != domvol.end(); ++itdom)
11262 int idom = itdom->first;
11263 //MESSAGE(" domain " << idom);
11264 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11266 if (nodeDomains[oldId].size() >= 2) // a multiple node
11268 vector<int> orderedDoms;
11269 //MESSAGE("multiple node " << oldId);
11270 if (mutipleNodes.count(oldId))
11271 orderedDoms = mutipleNodes[oldId];
11274 map<int,int>::iterator it = nodeDomains[oldId].begin();
11275 for (; it != nodeDomains[oldId].end(); ++it)
11276 orderedDoms.push_back(it->first);
11278 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11279 //stringstream txt;
11280 //for (int i=0; i<orderedDoms.size(); i++)
11281 // txt << orderedDoms[i] << " ";
11282 //MESSAGE("orderedDoms " << txt.str());
11283 mutipleNodes[oldId] = orderedDoms;
11285 double *coords = grid->GetPoint(oldId);
11286 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11287 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11288 int newId = newNode->GetVtkID();
11289 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11290 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11297 //MESSAGE(".. Creation of elements");
11298 for (int idomain = idom0; idomain < nbDomains; idomain++)
11300 itface = faceDomains.begin();
11301 for (; itface != faceDomains.end(); ++itface)
11303 std::map<int, int> domvol = itface->second;
11304 if (!domvol.count(idomain))
11306 DownIdType face = itface->first;
11307 //MESSAGE(" --- face " << face.cellId);
11308 std::set<int> oldNodes;
11310 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11311 int nbMultipleNodes = 0;
11312 std::set<int>::iterator itn = oldNodes.begin();
11313 for (; itn != oldNodes.end(); ++itn)
11316 if (mutipleNodes.count(oldId))
11319 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11321 //MESSAGE("multiple Nodes detected on a shared face");
11322 int downId = itface->first.cellId;
11323 unsigned char cellType = itface->first.cellType;
11324 // --- shared edge or shared face ?
11325 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11328 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11329 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11330 if (mutipleNodes.count(nodes[i]))
11331 if (!mutipleNodesToFace.count(nodes[i]))
11332 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11334 else // shared face (between two volumes)
11336 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11337 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11338 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11339 for (int ie =0; ie < nbEdges; ie++)
11342 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11343 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11345 vector<int> vn0 = mutipleNodes[nodes[0]];
11346 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11348 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11349 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11350 if ( vn0[i0] == vn1[i1] )
11351 doms.push_back( vn0[ i0 ]);
11352 if ( doms.size() > 2 )
11354 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11355 double *coords = grid->GetPoint(nodes[0]);
11356 gp_Pnt p0(coords[0], coords[1], coords[2]);
11357 coords = grid->GetPoint(nodes[nbNodes - 1]);
11358 gp_Pnt p1(coords[0], coords[1], coords[2]);
11360 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11361 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11362 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11363 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11364 for ( size_t id = 0; id < doms.size(); id++ )
11366 int idom = doms[id];
11367 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11368 for ( int ivol = 0; ivol < nbvol; ivol++ )
11370 int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11371 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11372 if (domain.count(elem))
11374 const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11375 domvol[idom] = (SMDS_MeshVolume*) svol;
11376 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11377 double values[3] = { 0,0,0 };
11378 vtkIdType npts = 0;
11379 vtkIdType const *pts(nullptr);
11380 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11381 for ( vtkIdType i = 0; i < npts; ++i )
11383 double *coords = grid->GetPoint( pts[i] );
11384 for ( int j = 0; j < 3; ++j )
11385 values[j] += coords[j] / npts;
11389 gref.SetCoord( values[0], values[1], values[2] );
11390 angleDom[idom] = 0;
11394 gp_Pnt g( values[0], values[1], values[2] );
11395 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11396 //MESSAGE(" angle=" << angleDom[idom]);
11402 map<double, int> sortedDom; // sort domains by angle
11403 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11404 sortedDom[ia->second] = ia->first;
11405 vector<int> vnodes;
11407 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11409 vdom.push_back(ib->second);
11410 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11412 for (int ino = 0; ino < nbNodes; ino++)
11413 vnodes.push_back(nodes[ino]);
11414 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11423 // --- iterate on shared faces (volumes to modify, face to extrude)
11424 // get node id's of the face (id SMDS = id VTK)
11425 // create flat element with old and new nodes if requested
11427 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11428 // (domain1 X domain2) = domain1 + MAXINT*domain2
11430 std::map<int, std::map<long,int> > nodeQuadDomains;
11431 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11433 //MESSAGE(".. Creation of elements: simple junction");
11434 if (createJointElems)
11436 string joints2DName = "joints2D";
11437 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11438 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11439 string joints3DName = "joints3D";
11440 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11441 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11443 itface = faceDomains.begin();
11444 for (; itface != faceDomains.end(); ++itface)
11446 DownIdType face = itface->first;
11447 std::set<int> oldNodes;
11448 std::set<int>::iterator itn;
11450 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11452 std::map<int, int> domvol = itface->second;
11453 std::map<int, int>::iterator itdom = domvol.begin();
11454 int dom1 = itdom->first;
11455 int vtkVolId = itdom->second;
11457 int dom2 = itdom->first;
11458 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11460 stringstream grpname;
11463 grpname << dom1 << "_" << dom2;
11465 grpname << dom2 << "_" << dom1;
11466 string namegrp = grpname.str();
11467 if (!mapOfJunctionGroups.count(namegrp))
11468 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11469 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11471 sgrp->Add(vol->GetID());
11472 if (vol->GetType() == SMDSAbs_Volume)
11473 joints3DGrp->Add(vol->GetID());
11474 else if (vol->GetType() == SMDSAbs_Face)
11475 joints2DGrp->Add(vol->GetID());
11479 // --- create volumes on multiple domain intersection if requested
11480 // iterate on mutipleNodesToFace
11481 // iterate on edgesMultiDomains
11483 //MESSAGE(".. Creation of elements: multiple junction");
11484 if (createJointElems)
11486 // --- iterate on mutipleNodesToFace
11488 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11489 for (; itn != mutipleNodesToFace.end(); ++itn)
11491 int node = itn->first;
11492 vector<int> orderDom = itn->second;
11493 vector<vtkIdType> orderedNodes;
11494 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11495 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11496 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11498 stringstream grpname;
11500 grpname << 0 << "_" << 0;
11501 string namegrp = grpname.str();
11502 if (!mapOfJunctionGroups.count(namegrp))
11503 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11504 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11506 sgrp->Add(face->GetID());
11509 // --- iterate on edgesMultiDomains
11511 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11512 for (; ite != edgesMultiDomains.end(); ++ite)
11514 vector<int> nodes = ite->first;
11515 vector<int> orderDom = ite->second;
11516 vector<vtkIdType> orderedNodes;
11517 if (nodes.size() == 2)
11519 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11520 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11521 if ( orderDom.size() == 3 )
11522 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11523 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11525 for (int idom = orderDom.size()-1; idom >=0; idom--)
11526 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11527 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11529 string namegrp = "jointsMultiples";
11530 if (!mapOfJunctionGroups.count(namegrp))
11531 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11532 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11534 sgrp->Add(vol->GetID());
11538 //INFOS("Quadratic multiple joints not implemented");
11539 // TODO quadratic nodes
11544 // --- list the explicit faces and edges of the mesh that need to be modified,
11545 // i.e. faces and edges built with one or more duplicated nodes.
11546 // associate these faces or edges to their corresponding domain.
11547 // only the first domain found is kept when a face or edge is shared
11549 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11550 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11551 faceOrEdgeDom.clear();
11554 //MESSAGE(".. Modification of elements");
11555 for (int idomain = idom0; idomain < nbDomains; idomain++)
11557 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11558 for (; itnod != nodeDomains.end(); ++itnod)
11560 int oldId = itnod->first;
11561 //MESSAGE(" node " << oldId);
11562 vtkCellLinks::Link l = (static_cast< vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11563 for (int i = 0; i < l.ncells; i++)
11565 int vtkId = l.cells[i];
11566 int vtkType = grid->GetCellType(vtkId);
11567 int downId = grid->CellIdToDownId(vtkId);
11569 continue; // new cells: not to be modified
11570 DownIdType aCell(downId, vtkType);
11571 int volParents[1000];
11572 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11573 for (int j = 0; j < nbvol; j++)
11574 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11575 if (!feDom.count(vtkId))
11577 feDom[vtkId] = idomain;
11578 faceOrEdgeDom[aCell] = emptyMap;
11579 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11580 //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11581 // << " type " << vtkType << " downId " << downId);
11587 // --- iterate on shared faces (volumes to modify, face to extrude)
11588 // get node id's of the face
11589 // replace old nodes by new nodes in volumes, and update inverse connectivity
11591 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11592 for (int m=0; m<3; m++)
11594 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11595 itface = (*amap).begin();
11596 for (; itface != (*amap).end(); ++itface)
11598 DownIdType face = itface->first;
11599 std::set<int> oldNodes;
11600 std::set<int>::iterator itn;
11602 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11603 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11604 std::map<int, int> localClonedNodeIds;
11606 std::map<int, int> domvol = itface->second;
11607 std::map<int, int>::iterator itdom = domvol.begin();
11608 for (; itdom != domvol.end(); ++itdom)
11610 int idom = itdom->first;
11611 int vtkVolId = itdom->second;
11612 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11613 localClonedNodeIds.clear();
11614 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11617 if (nodeDomains[oldId].count(idom))
11619 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11620 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11623 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11628 // Remove empty groups (issue 0022812)
11629 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11630 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11632 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11633 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11636 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11637 grid->DeleteLinks();
11645 * \brief Double nodes on some external faces and create flat elements.
11646 * Flat elements are mainly used by some types of mechanic calculations.
11648 * Each group of the list must be constituted of faces.
11649 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11650 * @param theElems - list of groups of faces, where a group of faces is a set of
11651 * SMDS_MeshElements sorted by Id.
11652 * @return TRUE if operation has been completed successfully, FALSE otherwise
11654 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11656 // MESSAGE("-------------------------------------------------");
11657 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11658 // MESSAGE("-------------------------------------------------");
11660 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11662 // --- For each group of faces
11663 // duplicate the nodes, create a flat element based on the face
11664 // replace the nodes of the faces by their clones
11666 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11667 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11668 clonedNodes.clear();
11669 intermediateNodes.clear();
11670 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11671 mapOfJunctionGroups.clear();
11673 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11675 const TIDSortedElemSet& domain = theElems[idom];
11676 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11677 for ( ; elemItr != domain.end(); ++elemItr )
11679 const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11682 // MESSAGE("aFace=" << aFace->GetID());
11683 bool isQuad = aFace->IsQuadratic();
11684 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11686 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11688 SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11689 while (nodeIt->more())
11691 const SMDS_MeshNode* node = nodeIt->next();
11692 bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11694 ln2.push_back(node);
11696 ln0.push_back(node);
11698 const SMDS_MeshNode* clone = 0;
11699 if (!clonedNodes.count(node))
11701 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11702 copyPosition( node, clone );
11703 clonedNodes[node] = clone;
11706 clone = clonedNodes[node];
11709 ln3.push_back(clone);
11711 ln1.push_back(clone);
11713 const SMDS_MeshNode* inter = 0;
11714 if (isQuad && (!isMedium))
11716 if (!intermediateNodes.count(node))
11718 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11719 copyPosition( node, inter );
11720 intermediateNodes[node] = inter;
11723 inter = intermediateNodes[node];
11724 ln4.push_back(inter);
11728 // --- extrude the face
11730 vector<const SMDS_MeshNode*> ln;
11731 SMDS_MeshVolume* vol = 0;
11732 vtkIdType aType = aFace->GetVtkType();
11736 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11737 // MESSAGE("vol prism " << vol->GetID());
11738 ln.push_back(ln1[0]);
11739 ln.push_back(ln1[1]);
11740 ln.push_back(ln1[2]);
11743 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11744 // MESSAGE("vol hexa " << vol->GetID());
11745 ln.push_back(ln1[0]);
11746 ln.push_back(ln1[1]);
11747 ln.push_back(ln1[2]);
11748 ln.push_back(ln1[3]);
11750 case VTK_QUADRATIC_TRIANGLE:
11751 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11752 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11753 // MESSAGE("vol quad prism " << vol->GetID());
11754 ln.push_back(ln1[0]);
11755 ln.push_back(ln1[1]);
11756 ln.push_back(ln1[2]);
11757 ln.push_back(ln3[0]);
11758 ln.push_back(ln3[1]);
11759 ln.push_back(ln3[2]);
11761 case VTK_QUADRATIC_QUAD:
11762 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11763 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11764 // ln4[0], ln4[1], ln4[2], ln4[3]);
11765 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11766 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11767 ln4[0], ln4[1], ln4[2], ln4[3]);
11768 // MESSAGE("vol quad hexa " << vol->GetID());
11769 ln.push_back(ln1[0]);
11770 ln.push_back(ln1[1]);
11771 ln.push_back(ln1[2]);
11772 ln.push_back(ln1[3]);
11773 ln.push_back(ln3[0]);
11774 ln.push_back(ln3[1]);
11775 ln.push_back(ln3[2]);
11776 ln.push_back(ln3[3]);
11786 stringstream grpname;
11789 string namegrp = grpname.str();
11790 if (!mapOfJunctionGroups.count(namegrp))
11791 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11792 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11794 sgrp->Add(vol->GetID());
11797 // --- modify the face
11799 const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11806 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11807 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11808 * groups of faces to remove inside the object, (idem edges).
11809 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11811 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11812 const TopoDS_Shape& theShape,
11813 SMESH_NodeSearcher* theNodeSearcher,
11814 const char* groupName,
11815 std::vector<double>& nodesCoords,
11816 std::vector<std::vector<int> >& listOfListOfNodes)
11818 // MESSAGE("--------------------------------");
11819 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11820 // MESSAGE("--------------------------------");
11822 // --- zone of volumes to remove is given :
11823 // 1 either by a geom shape (one or more vertices) and a radius,
11824 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11825 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11826 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11827 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11828 // defined by it's name.
11830 SMESHDS_GroupBase* groupDS = 0;
11831 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11832 while ( groupIt->more() )
11835 SMESH_Group * group = groupIt->next();
11836 if ( !group ) continue;
11837 groupDS = group->GetGroupDS();
11838 if ( !groupDS || groupDS->IsEmpty() ) continue;
11839 std::string grpName = group->GetName();
11840 //MESSAGE("grpName=" << grpName);
11841 if (grpName == groupName)
11847 bool isNodeGroup = false;
11848 bool isNodeCoords = false;
11851 if (groupDS->GetType() != SMDSAbs_Node)
11853 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11856 if (nodesCoords.size() > 0)
11857 isNodeCoords = true; // a list o nodes given by their coordinates
11858 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11860 // --- define groups to build
11862 // --- group of SMDS volumes
11863 string grpvName = groupName;
11864 grpvName += "_vol";
11865 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11868 MESSAGE("group not created " << grpvName);
11871 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11873 // --- group of SMDS faces on the skin
11874 string grpsName = groupName;
11875 grpsName += "_skin";
11876 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11879 MESSAGE("group not created " << grpsName);
11882 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11884 // --- group of SMDS faces internal (several shapes)
11885 string grpiName = groupName;
11886 grpiName += "_internalFaces";
11887 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11890 MESSAGE("group not created " << grpiName);
11893 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11895 // --- group of SMDS faces internal (several shapes)
11896 string grpeiName = groupName;
11897 grpeiName += "_internalEdges";
11898 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11901 MESSAGE("group not created " << grpeiName);
11904 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11906 // --- build downward connectivity
11908 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11909 meshDS->BuildDownWardConnectivity(true);
11910 SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11912 // --- set of volumes detected inside
11914 std::set<int> setOfInsideVol;
11915 std::set<int> setOfVolToCheck;
11917 std::vector<gp_Pnt> gpnts;
11920 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11922 //MESSAGE("group of nodes provided");
11923 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11924 while ( elemIt->more() )
11926 const SMDS_MeshElement* elem = elemIt->next();
11929 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11932 SMDS_MeshElement* vol = 0;
11933 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11934 while (volItr->more())
11936 vol = (SMDS_MeshElement*)volItr->next();
11937 setOfInsideVol.insert(vol->GetVtkID());
11938 sgrp->Add(vol->GetID());
11942 else if (isNodeCoords)
11944 //MESSAGE("list of nodes coordinates provided");
11947 while ( i < nodesCoords.size()-2 )
11949 double x = nodesCoords[i++];
11950 double y = nodesCoords[i++];
11951 double z = nodesCoords[i++];
11952 gp_Pnt p = gp_Pnt(x, y ,z);
11953 gpnts.push_back(p);
11954 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11958 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11960 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11961 TopTools_IndexedMapOfShape vertexMap;
11962 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11963 gp_Pnt p = gp_Pnt(0,0,0);
11964 if (vertexMap.Extent() < 1)
11967 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11969 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11970 p = BRep_Tool::Pnt(vertex);
11971 gpnts.push_back(p);
11972 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11976 if (gpnts.size() > 0)
11978 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11979 //MESSAGE("startNode->nodeId " << nodeId);
11981 double radius2 = radius*radius;
11982 //MESSAGE("radius2 " << radius2);
11984 // --- volumes on start node
11986 setOfVolToCheck.clear();
11987 SMDS_MeshElement* startVol = 0;
11988 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11989 while (volItr->more())
11991 startVol = (SMDS_MeshElement*)volItr->next();
11992 setOfVolToCheck.insert(startVol->GetVtkID());
11994 if (setOfVolToCheck.empty())
11996 MESSAGE("No volumes found");
12000 // --- starting with central volumes then their neighbors, check if they are inside
12001 // or outside the domain, until no more new neighbor volume is inside.
12002 // Fill the group of inside volumes
12004 std::map<int, double> mapOfNodeDistance2;
12005 mapOfNodeDistance2.clear();
12006 std::set<int> setOfOutsideVol;
12007 while (!setOfVolToCheck.empty())
12009 std::set<int>::iterator it = setOfVolToCheck.begin();
12011 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12012 bool volInside = false;
12013 vtkIdType npts = 0;
12014 vtkIdType const *pts(nullptr);
12015 grid->GetCellPoints(vtkId, npts, pts);
12016 for (int i=0; i<npts; i++)
12018 double distance2 = 0;
12019 if (mapOfNodeDistance2.count(pts[i]))
12021 distance2 = mapOfNodeDistance2[pts[i]];
12022 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12026 double *coords = grid->GetPoint(pts[i]);
12027 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12029 for ( size_t j = 0; j < gpnts.size(); j++ )
12031 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12032 if (d2 < distance2)
12035 if (distance2 < radius2)
12039 mapOfNodeDistance2[pts[i]] = distance2;
12040 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12042 if (distance2 < radius2)
12044 volInside = true; // one or more nodes inside the domain
12045 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12051 setOfInsideVol.insert(vtkId);
12052 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12053 int neighborsVtkIds[NBMAXNEIGHBORS];
12054 int downIds[NBMAXNEIGHBORS];
12055 unsigned char downTypes[NBMAXNEIGHBORS];
12056 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12057 for (int n = 0; n < nbNeighbors; n++)
12058 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12059 setOfVolToCheck.insert(neighborsVtkIds[n]);
12063 setOfOutsideVol.insert(vtkId);
12064 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12066 setOfVolToCheck.erase(vtkId);
12070 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12071 // If yes, add the volume to the inside set
12073 bool addedInside = true;
12074 std::set<int> setOfVolToReCheck;
12075 while (addedInside)
12077 //MESSAGE(" --------------------------- re check");
12078 addedInside = false;
12079 std::set<int>::iterator itv = setOfInsideVol.begin();
12080 for (; itv != setOfInsideVol.end(); ++itv)
12083 int neighborsVtkIds[NBMAXNEIGHBORS];
12084 int downIds[NBMAXNEIGHBORS];
12085 unsigned char downTypes[NBMAXNEIGHBORS];
12086 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12087 for (int n = 0; n < nbNeighbors; n++)
12088 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12089 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12091 setOfVolToCheck = setOfVolToReCheck;
12092 setOfVolToReCheck.clear();
12093 while (!setOfVolToCheck.empty())
12095 std::set<int>::iterator it = setOfVolToCheck.begin();
12097 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12099 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12100 int countInside = 0;
12101 int neighborsVtkIds[NBMAXNEIGHBORS];
12102 int downIds[NBMAXNEIGHBORS];
12103 unsigned char downTypes[NBMAXNEIGHBORS];
12104 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12105 for (int n = 0; n < nbNeighbors; n++)
12106 if (setOfInsideVol.count(neighborsVtkIds[n]))
12108 //MESSAGE("countInside " << countInside);
12109 if (countInside > 1)
12111 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12112 setOfInsideVol.insert(vtkId);
12113 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12114 addedInside = true;
12117 setOfVolToReCheck.insert(vtkId);
12119 setOfVolToCheck.erase(vtkId);
12123 // --- map of Downward faces at the boundary, inside the global volume
12124 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12125 // fill group of SMDS faces inside the volume (when several volume shapes)
12126 // fill group of SMDS faces on the skin of the global volume (if skin)
12128 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12129 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12130 std::set<int>::iterator it = setOfInsideVol.begin();
12131 for (; it != setOfInsideVol.end(); ++it)
12134 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12135 int neighborsVtkIds[NBMAXNEIGHBORS];
12136 int downIds[NBMAXNEIGHBORS];
12137 unsigned char downTypes[NBMAXNEIGHBORS];
12138 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12139 for (int n = 0; n < nbNeighbors; n++)
12141 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12142 if (neighborDim == 3)
12144 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12146 DownIdType face(downIds[n], downTypes[n]);
12147 boundaryFaces[face] = vtkId;
12149 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12150 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12151 if (vtkFaceId >= 0)
12153 sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12154 // find also the smds edges on this face
12155 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12156 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12157 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12158 for (int i = 0; i < nbEdges; i++)
12160 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12161 if (vtkEdgeId >= 0)
12162 sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12166 else if (neighborDim == 2) // skin of the volume
12168 DownIdType face(downIds[n], downTypes[n]);
12169 skinFaces[face] = vtkId;
12170 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12171 if (vtkFaceId >= 0)
12172 sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12177 // --- identify the edges constituting the wire of each subshape on the skin
12178 // define polylines with the nodes of edges, equivalent to wires
12179 // project polylines on subshapes, and partition, to get geom faces
12181 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12182 std::set<int> emptySet;
12184 std::set<int> shapeIds;
12186 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12187 while (itelem->more())
12189 const SMDS_MeshElement *elem = itelem->next();
12190 int shapeId = elem->getshapeId();
12191 int vtkId = elem->GetVtkID();
12192 if (!shapeIdToVtkIdSet.count(shapeId))
12194 shapeIdToVtkIdSet[shapeId] = emptySet;
12195 shapeIds.insert(shapeId);
12197 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12200 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12201 std::set<DownIdType, DownIdCompare> emptyEdges;
12202 emptyEdges.clear();
12204 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12205 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12207 int shapeId = itShape->first;
12208 //MESSAGE(" --- Shape ID --- "<< shapeId);
12209 shapeIdToEdges[shapeId] = emptyEdges;
12211 std::vector<int> nodesEdges;
12213 std::set<int>::iterator its = itShape->second.begin();
12214 for (; its != itShape->second.end(); ++its)
12217 //MESSAGE(" " << vtkId);
12218 int neighborsVtkIds[NBMAXNEIGHBORS];
12219 int downIds[NBMAXNEIGHBORS];
12220 unsigned char downTypes[NBMAXNEIGHBORS];
12221 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12222 for (int n = 0; n < nbNeighbors; n++)
12224 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12226 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12227 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12228 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12230 DownIdType edge(downIds[n], downTypes[n]);
12231 if (!shapeIdToEdges[shapeId].count(edge))
12233 shapeIdToEdges[shapeId].insert(edge);
12235 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12236 nodesEdges.push_back(vtkNodeId[0]);
12237 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12238 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12244 std::list<int> order;
12246 if (nodesEdges.size() > 0)
12248 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12249 nodesEdges[0] = -1;
12250 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12251 nodesEdges[1] = -1; // do not reuse this edge
12255 int nodeTofind = order.back(); // try first to push back
12257 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12258 if (nodesEdges[i] == nodeTofind)
12260 if ( i == (int) nodesEdges.size() )
12261 found = false; // no follower found on back
12264 if (i%2) // odd ==> use the previous one
12265 if (nodesEdges[i-1] < 0)
12269 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12270 nodesEdges[i-1] = -1;
12272 else // even ==> use the next one
12273 if (nodesEdges[i+1] < 0)
12277 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12278 nodesEdges[i+1] = -1;
12283 // try to push front
12285 nodeTofind = order.front(); // try to push front
12286 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12287 if ( nodesEdges[i] == nodeTofind )
12289 if ( i == (int)nodesEdges.size() )
12291 found = false; // no predecessor found on front
12294 if (i%2) // odd ==> use the previous one
12295 if (nodesEdges[i-1] < 0)
12299 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12300 nodesEdges[i-1] = -1;
12302 else // even ==> use the next one
12303 if (nodesEdges[i+1] < 0)
12307 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12308 nodesEdges[i+1] = -1;
12314 std::vector<int> nodes;
12315 nodes.push_back(shapeId);
12316 std::list<int>::iterator itl = order.begin();
12317 for (; itl != order.end(); itl++)
12319 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12320 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12322 listOfListOfNodes.push_back(nodes);
12325 // partition geom faces with blocFissure
12326 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12327 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12333 //================================================================================
12335 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12336 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12337 * \return TRUE if operation has been completed successfully, FALSE otherwise
12339 //================================================================================
12341 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12343 // iterates on volume elements and detect all free faces on them
12344 SMESHDS_Mesh* aMesh = GetMeshDS();
12348 ElemFeatures faceType( SMDSAbs_Face );
12349 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12350 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12353 const SMDS_MeshVolume* volume = vIt->next();
12354 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12355 vTool.SetExternalNormal();
12356 const int iQuad = volume->IsQuadratic();
12357 faceType.SetQuad( iQuad );
12358 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12360 if (!vTool.IsFreeFace(iface))
12363 vector<const SMDS_MeshNode *> nodes;
12364 int nbFaceNodes = vTool.NbFaceNodes(iface);
12365 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12367 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12368 nodes.push_back(faceNodes[inode]);
12370 if (iQuad) // add medium nodes
12372 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12373 nodes.push_back(faceNodes[inode]);
12374 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12375 nodes.push_back(faceNodes[8]);
12377 // add new face based on volume nodes
12378 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12380 nbExisted++; // face already exists
12384 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12389 return ( nbFree == ( nbExisted + nbCreated ));
12394 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12396 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12398 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12401 //================================================================================
12403 * \brief Creates missing boundary elements
12404 * \param elements - elements whose boundary is to be checked
12405 * \param dimension - defines type of boundary elements to create
12406 * \param group - a group to store created boundary elements in
12407 * \param targetMesh - a mesh to store created boundary elements in
12408 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12409 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12410 * boundary elements will be copied into the targetMesh
12411 * \param toAddExistingBondary - if true, not only new but also pre-existing
12412 * boundary elements will be added into the new group
12413 * \param aroundElements - if true, elements will be created on boundary of given
12414 * elements else, on boundary of the whole mesh.
12415 * \return nb of added boundary elements
12417 //================================================================================
12419 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12420 Bnd_Dimension dimension,
12421 SMESH_Group* group/*=0*/,
12422 SMESH_Mesh* targetMesh/*=0*/,
12423 bool toCopyElements/*=false*/,
12424 bool toCopyExistingBoundary/*=false*/,
12425 bool toAddExistingBondary/*= false*/,
12426 bool aroundElements/*= false*/)
12428 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12429 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12430 // hope that all elements are of the same type, do not check them all
12431 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12432 throw SALOME_Exception(LOCALIZED("wrong element type"));
12435 toCopyElements = toCopyExistingBoundary = false;
12437 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12438 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12439 int nbAddedBnd = 0;
12441 // editor adding present bnd elements and optionally holding elements to add to the group
12442 SMESH_MeshEditor* presentEditor;
12443 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12444 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12446 SMESH_MesherHelper helper( *myMesh );
12447 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12448 SMDS_VolumeTool vTool;
12449 TIDSortedElemSet avoidSet;
12450 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12453 typedef vector<const SMDS_MeshNode*> TConnectivity;
12454 TConnectivity tgtNodes;
12455 ElemFeatures elemKind( missType ), elemToCopy;
12457 vector<const SMDS_MeshElement*> presentBndElems;
12458 vector<TConnectivity> missingBndElems;
12459 vector<int> freeFacets;
12460 TConnectivity nodes, elemNodes;
12462 SMDS_ElemIteratorPtr eIt;
12463 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12464 else eIt = SMESHUtils::elemSetIterator( elements );
12466 while ( eIt->more() )
12468 const SMDS_MeshElement* elem = eIt->next();
12469 const int iQuad = elem->IsQuadratic();
12470 elemKind.SetQuad( iQuad );
12472 // ------------------------------------------------------------------------------------
12473 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12474 // ------------------------------------------------------------------------------------
12475 presentBndElems.clear();
12476 missingBndElems.clear();
12477 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12478 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12480 const SMDS_MeshElement* otherVol = 0;
12481 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12483 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12484 ( !aroundElements || elements.count( otherVol )))
12486 freeFacets.push_back( iface );
12488 if ( missType == SMDSAbs_Face )
12489 vTool.SetExternalNormal();
12490 for ( size_t i = 0; i < freeFacets.size(); ++i )
12492 int iface = freeFacets[i];
12493 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12494 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12495 if ( missType == SMDSAbs_Edge ) // boundary edges
12497 nodes.resize( 2+iQuad );
12498 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12500 for ( size_t j = 0; j < nodes.size(); ++j )
12501 nodes[ j ] = nn[ i+j ];
12502 if ( const SMDS_MeshElement* edge =
12503 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12504 presentBndElems.push_back( edge );
12506 missingBndElems.push_back( nodes );
12509 else // boundary face
12512 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12513 nodes.push_back( nn[inode] ); // add corner nodes
12515 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12516 nodes.push_back( nn[inode] ); // add medium nodes
12517 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12519 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12521 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12522 SMDSAbs_Face, /*noMedium=*/false ))
12523 presentBndElems.push_back( f );
12525 missingBndElems.push_back( nodes );
12527 if ( targetMesh != myMesh )
12529 // add 1D elements on face boundary to be added to a new mesh
12530 const SMDS_MeshElement* edge;
12531 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12534 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12536 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12537 if ( edge && avoidSet.insert( edge ).second )
12538 presentBndElems.push_back( edge );
12544 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12546 avoidSet.clear(), avoidSet.insert( elem );
12547 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12548 SMDS_MeshElement::iterator() );
12549 elemNodes.push_back( elemNodes[0] );
12550 nodes.resize( 2 + iQuad );
12551 const int nbLinks = elem->NbCornerNodes();
12552 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12554 nodes[0] = elemNodes[iN];
12555 nodes[1] = elemNodes[iN+1+iQuad];
12556 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12557 continue; // not free link
12559 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12560 if ( const SMDS_MeshElement* edge =
12561 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12562 presentBndElems.push_back( edge );
12564 missingBndElems.push_back( nodes );
12568 // ---------------------------------
12569 // 2. Add missing boundary elements
12570 // ---------------------------------
12571 if ( targetMesh != myMesh )
12572 // instead of making a map of nodes in this mesh and targetMesh,
12573 // we create nodes with same IDs.
12574 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12576 TConnectivity& srcNodes = missingBndElems[i];
12577 tgtNodes.resize( srcNodes.size() );
12578 for ( inode = 0; inode < srcNodes.size(); ++inode )
12579 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12580 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12582 /*noMedium=*/false))
12584 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12588 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12590 TConnectivity& nodes = missingBndElems[ i ];
12591 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12593 /*noMedium=*/false))
12595 SMDS_MeshElement* newElem =
12596 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12597 nbAddedBnd += bool( newElem );
12599 // try to set a new element to a shape
12600 if ( myMesh->HasShapeToMesh() )
12603 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12604 const size_t nbN = nodes.size() / (iQuad+1 );
12605 for ( inode = 0; inode < nbN && ok; ++inode )
12607 pair<int, TopAbs_ShapeEnum> i_stype =
12608 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12609 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12610 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12612 if ( ok && mediumShapes.size() > 1 )
12614 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12615 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12616 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12618 if (( ok = ( stype_i->first != stype_i_0.first )))
12619 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12620 aMesh->IndexToShape( stype_i_0.second ));
12623 if ( ok && mediumShapes.begin()->first == missShapeType )
12624 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12628 // ----------------------------------
12629 // 3. Copy present boundary elements
12630 // ----------------------------------
12631 if ( toCopyExistingBoundary )
12632 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12634 const SMDS_MeshElement* e = presentBndElems[i];
12635 tgtNodes.resize( e->NbNodes() );
12636 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12637 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12638 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12640 else // store present elements to add them to a group
12641 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12643 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12646 } // loop on given elements
12648 // ---------------------------------------------
12649 // 4. Fill group with boundary elements
12650 // ---------------------------------------------
12653 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12654 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12655 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12657 tgtEditor.myLastCreatedElems.clear();
12658 tgtEditor2.myLastCreatedElems.clear();
12660 // -----------------------
12661 // 5. Copy given elements
12662 // -----------------------
12663 if ( toCopyElements && targetMesh != myMesh )
12665 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12666 else eIt = SMESHUtils::elemSetIterator( elements );
12667 while (eIt->more())
12669 const SMDS_MeshElement* elem = eIt->next();
12670 tgtNodes.resize( elem->NbNodes() );
12671 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12672 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12673 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12675 tgtEditor.myLastCreatedElems.clear();
12681 //================================================================================
12683 * \brief Copy node position and set \a to node on the same geometry
12685 //================================================================================
12687 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12688 const SMDS_MeshNode* to )
12690 if ( !from || !to ) return;
12692 SMDS_PositionPtr pos = from->GetPosition();
12693 if ( !pos || from->getshapeId() < 1 ) return;
12695 switch ( pos->GetTypeOfPosition() )
12697 case SMDS_TOP_3DSPACE: break;
12699 case SMDS_TOP_FACE:
12701 SMDS_FacePositionPtr fPos = pos;
12702 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12703 fPos->GetUParameter(), fPos->GetVParameter() );
12706 case SMDS_TOP_EDGE:
12708 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12709 SMDS_EdgePositionPtr ePos = pos;
12710 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12713 case SMDS_TOP_VERTEX:
12715 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12718 case SMDS_TOP_UNSPEC: