1 // Copyright (C) 2007-2020 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
49 #include "utilities.h"
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_ListOfInteger.hxx>
66 #include <TopAbs_State.hxx>
68 #include <TopExp_Explorer.hxx>
69 #include <TopTools_ListIteratorOfListOfShape.hxx>
70 #include <TopTools_ListOfShape.hxx>
71 #include <TopTools_SequenceOfShape.hxx>
73 #include <TopoDS_Edge.hxx>
74 #include <TopoDS_Face.hxx>
75 #include <TopoDS_Solid.hxx>
81 #include <gp_Trsf.hxx>
95 #include <boost/tuple/tuple.hpp>
96 #include <boost/container/flat_set.hpp>
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
101 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
103 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
106 using namespace SMESH::Controls;
108 //=======================================================================
109 //function : SMESH_MeshEditor
111 //=======================================================================
113 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
114 :myMesh( theMesh ) // theMesh may be NULL
118 //================================================================================
120 * \brief Return mesh DS
122 //================================================================================
124 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
126 return myMesh->GetMeshDS();
130 //================================================================================
132 * \brief Clears myLastCreatedNodes and myLastCreatedElems
134 //================================================================================
136 void SMESH_MeshEditor::ClearLastCreated()
138 SMESHUtils::FreeVector( myLastCreatedElems );
139 SMESHUtils::FreeVector( myLastCreatedNodes );
142 //================================================================================
144 * \brief Initializes members by an existing element
145 * \param [in] elem - the source element
146 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
148 //================================================================================
150 SMESH_MeshEditor::ElemFeatures&
151 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
155 myType = elem->GetType();
156 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
158 myIsPoly = elem->IsPoly();
161 myIsQuad = elem->IsQuadratic();
162 if ( myType == SMDSAbs_Volume && !basicOnly )
164 myPolyhedQuantities = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
168 else if ( myType == SMDSAbs_Ball && !basicOnly )
170 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
176 //=======================================================================
180 //=======================================================================
183 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
184 const ElemFeatures& features)
186 SMDS_MeshElement* e = 0;
187 int nbnode = node.size();
188 SMESHDS_Mesh* mesh = GetMeshDS();
189 const int ID = features.myID;
191 switch ( features.myType ) {
193 if ( !features.myIsPoly ) {
195 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
196 else e = mesh->AddFace (node[0], node[1], node[2] );
198 else if (nbnode == 4) {
199 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
200 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
202 else if (nbnode == 6) {
203 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
204 node[4], node[5], ID);
205 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
208 else if (nbnode == 7) {
209 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
210 node[4], node[5], node[6], ID);
211 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
212 node[4], node[5], node[6] );
214 else if (nbnode == 8) {
215 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
216 node[4], node[5], node[6], node[7], ID);
217 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
218 node[4], node[5], node[6], node[7] );
220 else if (nbnode == 9) {
221 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
222 node[4], node[5], node[6], node[7], node[8], ID);
223 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
224 node[4], node[5], node[6], node[7], node[8] );
227 else if ( !features.myIsQuad )
229 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
230 else e = mesh->AddPolygonalFace (node );
232 else if ( nbnode % 2 == 0 ) // just a protection
234 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
235 else e = mesh->AddQuadPolygonalFace (node );
240 if ( !features.myIsPoly ) {
242 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
243 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
245 else if (nbnode == 5) {
246 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
248 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
251 else if (nbnode == 6) {
252 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
253 node[4], node[5], ID);
254 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
257 else if (nbnode == 8) {
258 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
259 node[4], node[5], node[6], node[7], ID);
260 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
261 node[4], node[5], node[6], node[7] );
263 else if (nbnode == 10) {
264 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
265 node[4], node[5], node[6], node[7],
266 node[8], node[9], ID);
267 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
268 node[4], node[5], node[6], node[7],
271 else if (nbnode == 12) {
272 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
273 node[4], node[5], node[6], node[7],
274 node[8], node[9], node[10], node[11], ID);
275 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
276 node[4], node[5], node[6], node[7],
277 node[8], node[9], node[10], node[11] );
279 else if (nbnode == 13) {
280 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
281 node[4], node[5], node[6], node[7],
282 node[8], node[9], node[10],node[11],
284 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
285 node[4], node[5], node[6], node[7],
286 node[8], node[9], node[10],node[11],
289 else if (nbnode == 15) {
290 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
291 node[4], node[5], node[6], node[7],
292 node[8], node[9], node[10],node[11],
293 node[12],node[13],node[14],ID);
294 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
295 node[4], node[5], node[6], node[7],
296 node[8], node[9], node[10],node[11],
297 node[12],node[13],node[14] );
299 else if (nbnode == 18) {
300 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
301 node[4], node[5], node[6], node[7],
302 node[8], node[9], node[10],node[11],
303 node[12],node[13],node[14],
304 node[15],node[16],node[17],ID );
305 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
306 node[4], node[5], node[6], node[7],
307 node[8], node[9], node[10],node[11],
308 node[12],node[13],node[14],
309 node[15],node[16],node[17] );
311 else if (nbnode == 20) {
312 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
313 node[4], node[5], node[6], node[7],
314 node[8], node[9], node[10],node[11],
315 node[12],node[13],node[14],node[15],
316 node[16],node[17],node[18],node[19],ID);
317 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
318 node[4], node[5], node[6], node[7],
319 node[8], node[9], node[10],node[11],
320 node[12],node[13],node[14],node[15],
321 node[16],node[17],node[18],node[19] );
323 else if (nbnode == 27) {
324 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
325 node[4], node[5], node[6], node[7],
326 node[8], node[9], node[10],node[11],
327 node[12],node[13],node[14],node[15],
328 node[16],node[17],node[18],node[19],
329 node[20],node[21],node[22],node[23],
330 node[24],node[25],node[26], ID);
331 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
332 node[4], node[5], node[6], node[7],
333 node[8], node[9], node[10],node[11],
334 node[12],node[13],node[14],node[15],
335 node[16],node[17],node[18],node[19],
336 node[20],node[21],node[22],node[23],
337 node[24],node[25],node[26] );
340 else if ( !features.myIsQuad )
342 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
343 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
347 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
348 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
354 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
355 else e = mesh->AddEdge (node[0], node[1] );
357 else if ( nbnode == 3 ) {
358 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
359 else e = mesh->AddEdge (node[0], node[1], node[2] );
363 case SMDSAbs_0DElement:
365 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
366 else e = mesh->Add0DElement (node[0] );
371 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
372 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
376 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
377 else e = mesh->AddBall (node[0], features.myBallDiameter );
382 if ( e ) myLastCreatedElems.push_back( e );
386 //=======================================================================
390 //=======================================================================
392 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
393 const ElemFeatures& features)
395 vector<const SMDS_MeshNode*> nodes;
396 nodes.reserve( nodeIDs.size() );
397 vector<int>::const_iterator id = nodeIDs.begin();
398 while ( id != nodeIDs.end() ) {
399 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
400 nodes.push_back( node );
404 return AddElement( nodes, features );
407 //=======================================================================
409 //purpose : Remove a node or an element.
410 // Modify a compute state of sub-meshes which become empty
411 //=======================================================================
413 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
418 SMESHDS_Mesh* aMesh = GetMeshDS();
419 set< SMESH_subMesh *> smmap;
422 list<int>::const_iterator it = theIDs.begin();
423 for ( ; it != theIDs.end(); it++ ) {
424 const SMDS_MeshElement * elem;
426 elem = aMesh->FindNode( *it );
428 elem = aMesh->FindElement( *it );
432 // Notify VERTEX sub-meshes about modification
434 const SMDS_MeshNode* node = cast2Node( elem );
435 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
436 if ( int aShapeID = node->getshapeId() )
437 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
440 // Find sub-meshes to notify about modification
441 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
442 // while ( nodeIt->more() ) {
443 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
444 // const SMDS_PositionPtr& aPosition = node->GetPosition();
445 // if ( aPosition.get() ) {
446 // if ( int aShapeID = aPosition->GetShapeId() ) {
447 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
448 // smmap.insert( sm );
455 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
457 aMesh->RemoveElement( elem );
461 // Notify sub-meshes about modification
462 if ( !smmap.empty() ) {
463 set< SMESH_subMesh *>::iterator smIt;
464 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
465 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
468 // // Check if the whole mesh becomes empty
469 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
470 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
475 //================================================================================
477 * \brief Create 0D elements on all nodes of the given object.
478 * \param elements - Elements on whose nodes to create 0D elements; if empty,
479 * the all mesh is treated
480 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
481 * \param duplicateElements - to add one more 0D element to a node or not
483 //================================================================================
485 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
486 TIDSortedElemSet& all0DElems,
487 const bool duplicateElements )
489 SMDS_ElemIteratorPtr elemIt;
490 if ( elements.empty() )
492 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
496 elemIt = SMESHUtils::elemSetIterator( elements );
499 while ( elemIt->more() )
501 const SMDS_MeshElement* e = elemIt->next();
502 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
503 while ( nodeIt->more() )
505 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
506 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
507 if ( duplicateElements || !it0D->more() )
509 myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
510 all0DElems.insert( myLastCreatedElems.back() );
512 while ( it0D->more() )
513 all0DElems.insert( it0D->next() );
518 //=======================================================================
519 //function : FindShape
520 //purpose : Return an index of the shape theElem is on
521 // or zero if a shape not found
522 //=======================================================================
524 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
528 SMESHDS_Mesh * aMesh = GetMeshDS();
529 if ( aMesh->ShapeToMesh().IsNull() )
532 int aShapeID = theElem->getshapeId();
536 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
537 if ( sm->Contains( theElem ))
540 if ( theElem->GetType() == SMDSAbs_Node ) {
541 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
544 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
547 TopoDS_Shape aShape; // the shape a node of theElem is on
548 if ( theElem->GetType() != SMDSAbs_Node )
550 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
551 while ( nodeIt->more() ) {
552 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
553 if ((aShapeID = node->getshapeId()) > 0) {
554 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
555 if ( sm->Contains( theElem ))
557 if ( aShape.IsNull() )
558 aShape = aMesh->IndexToShape( aShapeID );
564 // None of nodes is on a proper shape,
565 // find the shape among ancestors of aShape on which a node is
566 if ( !aShape.IsNull() ) {
567 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
568 for ( ; ancIt.More(); ancIt.Next() ) {
569 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
570 if ( sm && sm->Contains( theElem ))
571 return aMesh->ShapeToIndex( ancIt.Value() );
576 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
577 while ( const SMESHDS_SubMesh* sm = smIt->next() )
578 if ( sm->Contains( theElem ))
585 //=======================================================================
586 //function : IsMedium
588 //=======================================================================
590 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
591 const SMDSAbs_ElementType typeToCheck)
593 bool isMedium = false;
594 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
595 while (it->more() && !isMedium ) {
596 const SMDS_MeshElement* elem = it->next();
597 isMedium = elem->IsMediumNode(node);
602 //=======================================================================
603 //function : shiftNodesQuadTria
604 //purpose : Shift nodes in the array corresponded to quadratic triangle
605 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
606 //=======================================================================
608 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
610 const SMDS_MeshNode* nd1 = aNodes[0];
611 aNodes[0] = aNodes[1];
612 aNodes[1] = aNodes[2];
614 const SMDS_MeshNode* nd2 = aNodes[3];
615 aNodes[3] = aNodes[4];
616 aNodes[4] = aNodes[5];
620 //=======================================================================
621 //function : getNodesFromTwoTria
623 //=======================================================================
625 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
626 const SMDS_MeshElement * theTria2,
627 vector< const SMDS_MeshNode*>& N1,
628 vector< const SMDS_MeshNode*>& N2)
630 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
631 if ( N1.size() < 6 ) return false;
632 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
633 if ( N2.size() < 6 ) return false;
635 int sames[3] = {-1,-1,-1};
647 if(nbsames!=2) return false;
649 shiftNodesQuadTria(N1);
651 shiftNodesQuadTria(N1);
654 i = sames[0] + sames[1] + sames[2];
656 shiftNodesQuadTria(N2);
658 // now we receive following N1 and N2 (using numeration as in the image below)
659 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
660 // i.e. first nodes from both arrays form a new diagonal
664 //=======================================================================
665 //function : InverseDiag
666 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
667 // but having other common link.
668 // Return False if args are improper
669 //=======================================================================
671 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
672 const SMDS_MeshElement * theTria2 )
676 if ( !theTria1 || !theTria2 ||
677 !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
678 !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
679 theTria1->GetType() != SMDSAbs_Face ||
680 theTria2->GetType() != SMDSAbs_Face )
683 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
684 (theTria2->GetEntityType() == SMDSEntity_Triangle))
686 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
687 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
691 // put nodes in array and find out indices of the same ones
692 const SMDS_MeshNode* aNodes [6];
693 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
695 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
696 while ( it->more() ) {
697 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
699 if ( i > 2 ) // theTria2
700 // find same node of theTria1
701 for ( int j = 0; j < 3; j++ )
702 if ( aNodes[ i ] == aNodes[ j ]) {
711 return false; // theTria1 is not a triangle
712 it = theTria2->nodesIterator();
714 if ( i == 6 && it->more() )
715 return false; // theTria2 is not a triangle
718 // find indices of 1,2 and of A,B in theTria1
719 int iA = -1, iB = 0, i1 = 0, i2 = 0;
720 for ( i = 0; i < 6; i++ ) {
721 if ( sameInd [ i ] == -1 ) {
726 if ( iA >= 0) iB = i;
730 // nodes 1 and 2 should not be the same
731 if ( aNodes[ i1 ] == aNodes[ i2 ] )
735 aNodes[ iA ] = aNodes[ i2 ];
737 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
739 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
740 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
744 } // end if(F1 && F2)
746 // check case of quadratic faces
747 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
748 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
750 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
751 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
755 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
756 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
764 vector< const SMDS_MeshNode* > N1;
765 vector< const SMDS_MeshNode* > N2;
766 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
768 // now we receive following N1 and N2 (using numeration as above image)
769 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
770 // i.e. first nodes from both arrays determ new diagonal
772 vector< const SMDS_MeshNode*> N1new( N1.size() );
773 vector< const SMDS_MeshNode*> N2new( N2.size() );
774 N1new.back() = N1.back(); // central node of biquadratic
775 N2new.back() = N2.back();
776 N1new[0] = N1[0]; N2new[0] = N1[0];
777 N1new[1] = N2[0]; N2new[1] = N1[1];
778 N1new[2] = N2[1]; N2new[2] = N2[0];
779 N1new[3] = N1[4]; N2new[3] = N1[3];
780 N1new[4] = N2[3]; N2new[4] = N2[5];
781 N1new[5] = N1[5]; N2new[5] = N1[4];
782 // change nodes in faces
783 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
784 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
786 // move the central node of biquadratic triangle
787 SMESH_MesherHelper helper( *GetMesh() );
788 for ( int is2nd = 0; is2nd < 2; ++is2nd )
790 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
791 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
792 if ( nodes.size() < 7 )
794 helper.SetSubShape( tria->getshapeId() );
795 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
799 xyz = ( SMESH_NodeXYZ( nodes[3] ) +
800 SMESH_NodeXYZ( nodes[4] ) +
801 SMESH_NodeXYZ( nodes[5] )) / 3.;
806 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
807 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
808 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
810 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
811 xyz = S->Value( uv.X(), uv.Y() );
812 xyz.Transform( loc );
813 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
814 nodes[6]->getshapeId() > 0 )
815 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
817 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
822 //=======================================================================
823 //function : findTriangles
824 //purpose : find triangles sharing theNode1-theNode2 link
825 //=======================================================================
827 static bool findTriangles(const SMDS_MeshNode * theNode1,
828 const SMDS_MeshNode * theNode2,
829 const SMDS_MeshElement*& theTria1,
830 const SMDS_MeshElement*& theTria2)
832 if ( !theNode1 || !theNode2 ) return false;
834 theTria1 = theTria2 = 0;
836 set< const SMDS_MeshElement* > emap;
837 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
839 const SMDS_MeshElement* elem = it->next();
840 if ( elem->NbCornerNodes() == 3 )
843 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
845 const SMDS_MeshElement* elem = it->next();
846 if ( emap.count( elem )) {
854 // theTria1 must be element with minimum ID
855 if ( theTria2->GetID() < theTria1->GetID() )
856 std::swap( theTria2, theTria1 );
864 //=======================================================================
865 //function : InverseDiag
866 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
867 // with ones built on the same 4 nodes but having other common link.
868 // Return false if proper faces not found
869 //=======================================================================
871 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
872 const SMDS_MeshNode * theNode2)
876 const SMDS_MeshElement *tr1, *tr2;
877 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
880 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
881 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
884 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
885 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
887 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
888 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
892 // put nodes in array
893 // and find indices of 1,2 and of A in tr1 and of B in tr2
894 int i, iA1 = 0, i1 = 0;
895 const SMDS_MeshNode* aNodes1 [3];
896 SMDS_ElemIteratorPtr it;
897 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
898 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
899 if ( aNodes1[ i ] == theNode1 )
900 iA1 = i; // node A in tr1
901 else if ( aNodes1[ i ] != theNode2 )
905 const SMDS_MeshNode* aNodes2 [3];
906 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
907 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
908 if ( aNodes2[ i ] == theNode2 )
909 iB2 = i; // node B in tr2
910 else if ( aNodes2[ i ] != theNode1 )
914 // nodes 1 and 2 should not be the same
915 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
919 aNodes1[ iA1 ] = aNodes2[ i2 ];
921 aNodes2[ iB2 ] = aNodes1[ i1 ];
923 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
924 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
929 // check case of quadratic faces
930 return InverseDiag(tr1,tr2);
933 //=======================================================================
934 //function : getQuadrangleNodes
935 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
936 // fusion of triangles tr1 and tr2 having shared link on
937 // theNode1 and theNode2
938 //=======================================================================
940 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
941 const SMDS_MeshNode * theNode1,
942 const SMDS_MeshNode * theNode2,
943 const SMDS_MeshElement * tr1,
944 const SMDS_MeshElement * tr2 )
946 if( tr1->NbNodes() != tr2->NbNodes() )
948 // find the 4-th node to insert into tr1
949 const SMDS_MeshNode* n4 = 0;
950 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
952 while ( !n4 && i<3 ) {
953 const SMDS_MeshNode * n = cast2Node( it->next() );
955 bool isDiag = ( n == theNode1 || n == theNode2 );
959 // Make an array of nodes to be in a quadrangle
960 int iNode = 0, iFirstDiag = -1;
961 it = tr1->nodesIterator();
964 const SMDS_MeshNode * n = cast2Node( it->next() );
966 bool isDiag = ( n == theNode1 || n == theNode2 );
968 if ( iFirstDiag < 0 )
970 else if ( iNode - iFirstDiag == 1 )
971 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
973 else if ( n == n4 ) {
974 return false; // tr1 and tr2 should not have all the same nodes
976 theQuadNodes[ iNode++ ] = n;
978 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
979 theQuadNodes[ iNode ] = n4;
984 //=======================================================================
985 //function : DeleteDiag
986 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
987 // with a quadrangle built on the same 4 nodes.
988 // Return false if proper faces not found
989 //=======================================================================
991 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
992 const SMDS_MeshNode * theNode2)
996 const SMDS_MeshElement *tr1, *tr2;
997 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1000 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
1001 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
1004 SMESHDS_Mesh * aMesh = GetMeshDS();
1006 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1007 (tr2->GetEntityType() == SMDSEntity_Triangle))
1009 const SMDS_MeshNode* aNodes [ 4 ];
1010 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1013 const SMDS_MeshElement* newElem = 0;
1014 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1015 myLastCreatedElems.push_back(newElem);
1016 AddToSameGroups( newElem, tr1, aMesh );
1017 int aShapeId = tr1->getshapeId();
1019 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1021 aMesh->RemoveElement( tr1 );
1022 aMesh->RemoveElement( tr2 );
1027 // check case of quadratic faces
1028 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1030 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1034 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1035 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1043 vector< const SMDS_MeshNode* > N1;
1044 vector< const SMDS_MeshNode* > N2;
1045 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1047 // now we receive following N1 and N2 (using numeration as above image)
1048 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1049 // i.e. first nodes from both arrays determ new diagonal
1051 const SMDS_MeshNode* aNodes[8];
1061 const SMDS_MeshElement* newElem = 0;
1062 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1063 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1064 myLastCreatedElems.push_back(newElem);
1065 AddToSameGroups( newElem, tr1, aMesh );
1066 int aShapeId = tr1->getshapeId();
1069 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1071 aMesh->RemoveElement( tr1 );
1072 aMesh->RemoveElement( tr2 );
1074 // remove middle node (9)
1075 GetMeshDS()->RemoveNode( N1[4] );
1080 //=======================================================================
1081 //function : Reorient
1082 //purpose : Reverse theElement orientation
1083 //=======================================================================
1085 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1091 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1092 if ( !it || !it->more() )
1095 const SMDSAbs_ElementType type = theElem->GetType();
1096 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1099 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1100 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1102 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1104 MESSAGE("Warning: bad volumic element");
1107 SMDS_VolumeTool vTool( aPolyedre );
1108 const int nbFaces = vTool.NbFaces();
1109 vector<int> quantities( nbFaces );
1110 vector<const SMDS_MeshNode *> poly_nodes;
1112 // check if all facets are oriented equally
1113 bool sameOri = true;
1114 vector<int>& facetOri = quantities; // keep orientation in quantities so far
1115 for (int iface = 0; iface < nbFaces; iface++)
1117 facetOri[ iface ] = vTool.IsFaceExternal( iface );
1118 if ( facetOri[ iface ] != facetOri[ 0 ])
1122 // reverse faces of the polyhedron
1123 int neededOri = sameOri ? 1 - facetOri[0] : 1;
1124 poly_nodes.reserve( vTool.NbNodes() );
1125 for ( int iface = 0; iface < nbFaces; iface++ )
1127 int nbFaceNodes = vTool.NbFaceNodes( iface );
1128 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( iface );
1129 bool toReverse = ( facetOri[ iface ] != neededOri );
1131 quantities[ iface ] = nbFaceNodes;
1134 for ( int inode = nbFaceNodes - 1; inode >= 0; inode-- )
1135 poly_nodes.push_back( nodes[ inode ]);
1137 poly_nodes.insert( poly_nodes.end(), nodes, nodes + nbFaceNodes );
1139 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1141 else // other elements
1143 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1144 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1145 if ( interlace.empty() )
1147 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1151 SMDS_MeshCell::applyInterlace( interlace, nodes );
1153 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1158 //================================================================================
1160 * \brief Reorient faces.
1161 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1162 * \param theDirection - desired direction of normal of \a theFace
1163 * \param theFace - one of \a theFaces that should be oriented according to
1164 * \a theDirection and whose orientation defines orientation of other faces
1165 * \return number of reoriented faces.
1167 //================================================================================
1169 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1170 const gp_Dir& theDirection,
1171 const SMDS_MeshElement * theFace)
1174 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1176 if ( theFaces.empty() )
1178 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
1179 while ( fIt->more() )
1180 theFaces.insert( theFaces.end(), fIt->next() );
1183 // orient theFace according to theDirection
1185 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1186 if ( normal * theDirection.XYZ() < 0 )
1187 nbReori += Reorient( theFace );
1189 // Orient other faces
1191 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1192 TIDSortedElemSet avoidSet;
1193 set< SMESH_TLink > checkedLinks;
1194 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1196 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1197 theFaces.erase( theFace );
1198 startFaces.insert( theFace );
1200 int nodeInd1, nodeInd2;
1201 const SMDS_MeshElement* otherFace;
1202 vector< const SMDS_MeshElement* > facesNearLink;
1203 vector< std::pair< int, int > > nodeIndsOfFace;
1205 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1206 while ( !startFaces.empty() )
1208 startFace = startFaces.begin();
1209 theFace = *startFace;
1210 startFaces.erase( startFace );
1211 if ( !visitedFaces.insert( theFace ).second )
1215 avoidSet.insert(theFace);
1217 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1219 const int nbNodes = theFace->NbCornerNodes();
1220 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1222 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1223 linkIt_isNew = checkedLinks.insert( link );
1224 if ( !linkIt_isNew.second )
1226 // link has already been checked and won't be encountered more
1227 // if the group (theFaces) is manifold
1228 //checkedLinks.erase( linkIt_isNew.first );
1232 facesNearLink.clear();
1233 nodeIndsOfFace.clear();
1234 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1236 &nodeInd1, &nodeInd2 )))
1237 if ( otherFace != theFace)
1239 facesNearLink.push_back( otherFace );
1240 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1241 avoidSet.insert( otherFace );
1243 if ( facesNearLink.size() > 1 )
1245 // NON-MANIFOLD mesh shell !
1246 // select a face most co-directed with theFace,
1247 // other faces won't be visited this time
1249 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1250 double proj, maxProj = -1;
1251 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1252 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1253 if (( proj = Abs( NF * NOF )) > maxProj ) {
1255 otherFace = facesNearLink[i];
1256 nodeInd1 = nodeIndsOfFace[i].first;
1257 nodeInd2 = nodeIndsOfFace[i].second;
1260 // not to visit rejected faces
1261 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1262 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1263 visitedFaces.insert( facesNearLink[i] );
1265 else if ( facesNearLink.size() == 1 )
1267 otherFace = facesNearLink[0];
1268 nodeInd1 = nodeIndsOfFace.back().first;
1269 nodeInd2 = nodeIndsOfFace.back().second;
1271 if ( otherFace && otherFace != theFace)
1273 // link must be reverse in otherFace if orientation to otherFace
1274 // is same as that of theFace
1275 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1277 nbReori += Reorient( otherFace );
1279 startFaces.insert( otherFace );
1282 std::swap( link.first, link.second ); // reverse the link
1288 //================================================================================
1290 * \brief Reorient faces basing on orientation of adjacent volumes.
1291 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1292 * \param theVolumes - reference volumes.
1293 * \param theOutsideNormal - to orient faces to have their normal
1294 * pointing either \a outside or \a inside the adjacent volumes.
1295 * \return number of reoriented faces.
1297 //================================================================================
1299 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1300 TIDSortedElemSet & theVolumes,
1301 const bool theOutsideNormal)
1305 SMDS_ElemIteratorPtr faceIt;
1306 if ( theFaces.empty() )
1307 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1309 faceIt = SMESHUtils::elemSetIterator( theFaces );
1311 vector< const SMDS_MeshNode* > faceNodes;
1312 TIDSortedElemSet checkedVolumes;
1313 set< const SMDS_MeshNode* > faceNodesSet;
1314 SMDS_VolumeTool volumeTool;
1316 while ( faceIt->more() ) // loop on given faces
1318 const SMDS_MeshElement* face = faceIt->next();
1319 if ( face->GetType() != SMDSAbs_Face )
1322 const size_t nbCornersNodes = face->NbCornerNodes();
1323 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1325 checkedVolumes.clear();
1326 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1327 while ( vIt->more() )
1329 const SMDS_MeshElement* volume = vIt->next();
1331 if ( !checkedVolumes.insert( volume ).second )
1333 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1336 // is volume adjacent?
1337 bool allNodesCommon = true;
1338 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1339 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1340 if ( !allNodesCommon )
1343 // get nodes of a corresponding volume facet
1344 faceNodesSet.clear();
1345 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1346 volumeTool.Set( volume );
1347 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1348 if ( facetID < 0 ) continue;
1349 volumeTool.SetExternalNormal();
1350 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1352 // compare order of faceNodes and facetNodes
1353 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1355 for ( int i = 0; i < 2; ++i )
1357 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1358 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1359 if ( faceNodes[ iN ] == n )
1365 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1366 if ( isOutside != theOutsideNormal )
1367 nbReori += Reorient( face );
1369 } // loop on given faces
1374 //=======================================================================
1375 //function : getBadRate
1377 //=======================================================================
1379 static double getBadRate (const SMDS_MeshElement* theElem,
1380 SMESH::Controls::NumericalFunctorPtr& theCrit)
1382 SMESH::Controls::TSequenceOfXYZ P;
1383 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1385 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1386 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1389 //=======================================================================
1390 //function : QuadToTri
1391 //purpose : Cut quadrangles into triangles.
1392 // theCrit is used to select a diagonal to cut
1393 //=======================================================================
1395 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1396 SMESH::Controls::NumericalFunctorPtr theCrit)
1400 if ( !theCrit.get() )
1403 SMESHDS_Mesh * aMesh = GetMeshDS();
1404 Handle(Geom_Surface) surface;
1405 SMESH_MesherHelper helper( *GetMesh() );
1407 myLastCreatedElems.reserve( theElems.size() * 2 );
1409 TIDSortedElemSet::iterator itElem;
1410 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1412 const SMDS_MeshElement* elem = *itElem;
1413 if ( !elem || elem->GetType() != SMDSAbs_Face )
1415 if ( elem->NbCornerNodes() != 4 )
1418 // retrieve element nodes
1419 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1421 // compare two sets of possible triangles
1422 double aBadRate1, aBadRate2; // to what extent a set is bad
1423 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1424 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1425 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1427 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1428 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1429 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1431 const int aShapeId = FindShape( elem );
1432 const SMDS_MeshElement* newElem1 = 0;
1433 const SMDS_MeshElement* newElem2 = 0;
1435 if ( !elem->IsQuadratic() ) // split linear quadrangle
1437 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1438 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1439 if ( aBadRate1 <= aBadRate2 ) {
1440 // tr1 + tr2 is better
1441 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1442 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1445 // tr3 + tr4 is better
1446 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1447 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1450 else // split quadratic quadrangle
1452 helper.SetIsQuadratic( true );
1453 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1455 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1456 if ( aNodes.size() == 9 )
1458 helper.SetIsBiQuadratic( true );
1459 if ( aBadRate1 <= aBadRate2 )
1460 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1462 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1464 // create a new element
1465 if ( aBadRate1 <= aBadRate2 ) {
1466 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1467 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1470 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1471 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1475 // care of a new element
1477 myLastCreatedElems.push_back(newElem1);
1478 myLastCreatedElems.push_back(newElem2);
1479 AddToSameGroups( newElem1, elem, aMesh );
1480 AddToSameGroups( newElem2, elem, aMesh );
1482 // put a new triangle on the same shape
1484 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1485 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1487 aMesh->RemoveElement( elem );
1492 //=======================================================================
1494 * \brief Split each of given quadrangles into 4 triangles.
1495 * \param theElems - The faces to be split. If empty all faces are split.
1497 //=======================================================================
1499 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1502 myLastCreatedElems.reserve( theElems.size() * 4 );
1504 SMESH_MesherHelper helper( *GetMesh() );
1505 helper.SetElementsOnShape( true );
1507 // get standalone groups of faces
1508 vector< SMDS_MeshGroup* > allFaceGroups, faceGroups;
1509 for ( SMESHDS_GroupBase* grBase : GetMeshDS()->GetGroups() )
1510 if ( SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( grBase ))
1511 if ( group->GetType() == SMDSAbs_Face && !group->IsEmpty() )
1512 allFaceGroups.push_back( & group->SMDSGroup() );
1515 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1517 vector< const SMDS_MeshNode* > nodes;
1518 SMESHDS_SubMesh* subMeshDS = 0;
1520 Handle(Geom_Surface) surface;
1521 TopLoc_Location loc;
1523 SMDS_ElemIteratorPtr faceIt;
1524 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1525 else faceIt = SMESHUtils::elemSetIterator( theElems );
1527 while ( faceIt->more() )
1529 const SMDS_MeshElement* quad = faceIt->next();
1530 if ( !quad || quad->NbCornerNodes() != 4 )
1533 // get a surface the quad is on
1535 if ( quad->getshapeId() < 1 )
1538 helper.SetSubShape( 0 );
1541 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1543 helper.SetSubShape( quad->getshapeId() );
1544 if ( !helper.GetSubShape().IsNull() &&
1545 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1547 F = TopoDS::Face( helper.GetSubShape() );
1548 surface = BRep_Tool::Surface( F, loc );
1549 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1553 helper.SetSubShape( 0 );
1558 // create a central node
1560 const SMDS_MeshNode* nCentral;
1561 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1563 if ( nodes.size() == 9 )
1565 nCentral = nodes.back();
1572 for ( ; iN < nodes.size(); ++iN )
1573 xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1575 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1576 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1578 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1579 xyz[0], xyz[1], xyz[2], xyz[3],
1580 xyz[4], xyz[5], xyz[6], xyz[7] );
1584 for ( ; iN < nodes.size(); ++iN )
1585 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1587 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1588 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1590 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1591 uv[0], uv[1], uv[2], uv[3],
1592 uv[4], uv[5], uv[6], uv[7] );
1594 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1598 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1599 uv[8].X(), uv[8].Y() );
1600 myLastCreatedNodes.push_back( nCentral );
1603 helper.SetIsQuadratic ( nodes.size() > 4 );
1604 helper.SetIsBiQuadratic( nodes.size() == 9 );
1605 if ( helper.GetIsQuadratic() )
1606 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1608 // select groups to update
1610 for ( SMDS_MeshGroup* group : allFaceGroups )
1611 if ( group->Remove( quad ))
1612 faceGroups.push_back( group );
1614 // create 4 triangles
1616 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1618 for ( int i = 0; i < 4; ++i )
1620 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1623 myLastCreatedElems.push_back( tria );
1624 for ( SMDS_MeshGroup* group : faceGroups )
1630 //=======================================================================
1631 //function : BestSplit
1632 //purpose : Find better diagonal for cutting.
1633 //=======================================================================
1635 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1636 SMESH::Controls::NumericalFunctorPtr theCrit)
1643 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1646 if( theQuad->NbNodes()==4 ||
1647 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1649 // retrieve element nodes
1650 const SMDS_MeshNode* aNodes [4];
1651 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1653 //while (itN->more())
1655 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1657 // compare two sets of possible triangles
1658 double aBadRate1, aBadRate2; // to what extent a set is bad
1659 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1660 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1661 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1663 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1664 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1665 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1666 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1667 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1668 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1669 return 1; // diagonal 1-3
1671 return 2; // diagonal 2-4
1678 // Methods of splitting volumes into tetra
1680 const int theHexTo5_1[5*4+1] =
1682 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1684 const int theHexTo5_2[5*4+1] =
1686 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1688 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1690 const int theHexTo6_1[6*4+1] =
1692 1, 5, 6, 0, 0, 1, 2, 6, 0, 4, 5, 6, 0, 4, 6, 7, 0, 2, 3, 6, 0, 3, 7, 6, -1
1694 const int theHexTo6_2[6*4+1] =
1696 2, 6, 7, 1, 1, 2, 3, 7, 1, 5, 6, 7, 1, 5, 7, 4, 1, 3, 0, 7, 1, 0, 4, 7, -1
1698 const int theHexTo6_3[6*4+1] =
1700 3, 7, 4, 2, 2, 3, 0, 4, 2, 6, 7, 4, 2, 6, 4, 5, 2, 0, 1, 4, 2, 1, 5, 4, -1
1702 const int theHexTo6_4[6*4+1] =
1704 0, 4, 5, 3, 3, 0, 1, 5, 3, 7, 4, 5, 3, 7, 5, 6, 3, 1, 2, 5, 3, 2, 6, 5, -1
1706 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1708 const int thePyraTo2_1[2*4+1] =
1710 0, 1, 2, 4, 0, 2, 3, 4, -1
1712 const int thePyraTo2_2[2*4+1] =
1714 1, 2, 3, 4, 1, 3, 0, 4, -1
1716 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1718 const int thePentaTo3_1[3*4+1] =
1720 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1722 const int thePentaTo3_2[3*4+1] =
1724 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1726 const int thePentaTo3_3[3*4+1] =
1728 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1730 const int thePentaTo3_4[3*4+1] =
1732 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1734 const int thePentaTo3_5[3*4+1] =
1736 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1738 const int thePentaTo3_6[3*4+1] =
1740 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1742 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1743 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1745 // Methods of splitting hexahedron into prisms
1747 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1749 0, 1, 8, 4, 5, 9, 1, 2, 8, 5, 6, 9, 2, 3, 8, 6, 7, 9, 3, 0, 8, 7, 4, 9, -1
1751 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1753 1, 0, 8, 2, 3, 9, 0, 4, 8, 3, 7, 9, 4, 5, 8, 7, 6, 9, 5, 1, 8, 6, 2, 9, -1
1755 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1757 0, 3, 9, 1, 2, 8, 3, 7, 9, 2, 6, 8, 7, 4, 9, 6, 5, 8, 4, 0, 9, 5, 1, 8, -1
1760 const int theHexTo2Prisms_BT_1[6*2+1] =
1762 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1764 const int theHexTo2Prisms_BT_2[6*2+1] =
1766 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1768 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1770 const int theHexTo2Prisms_LR_1[6*2+1] =
1772 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1774 const int theHexTo2Prisms_LR_2[6*2+1] =
1776 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1778 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1780 const int theHexTo2Prisms_FB_1[6*2+1] =
1782 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1784 const int theHexTo2Prisms_FB_2[6*2+1] =
1786 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1788 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1791 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1794 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1795 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1796 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1797 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1803 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1804 bool _baryNode; //!< additional node is to be created at cell barycenter
1805 bool _ownConn; //!< to delete _connectivity in destructor
1806 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1808 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1809 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1810 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1811 bool hasFacet( const TTriangleFacet& facet ) const
1813 if ( _nbCorners == 4 )
1815 const int* tetConn = _connectivity;
1816 for ( ; tetConn[0] >= 0; tetConn += 4 )
1817 if (( facet.contains( tetConn[0] ) +
1818 facet.contains( tetConn[1] ) +
1819 facet.contains( tetConn[2] ) +
1820 facet.contains( tetConn[3] )) == 3 )
1823 else // prism, _nbCorners == 6
1825 const int* prismConn = _connectivity;
1826 for ( ; prismConn[0] >= 0; prismConn += 6 )
1828 if (( facet.contains( prismConn[0] ) &&
1829 facet.contains( prismConn[1] ) &&
1830 facet.contains( prismConn[2] ))
1832 ( facet.contains( prismConn[3] ) &&
1833 facet.contains( prismConn[4] ) &&
1834 facet.contains( prismConn[5] )))
1842 //=======================================================================
1844 * \brief return TSplitMethod for the given element to split into tetrahedra
1846 //=======================================================================
1848 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1850 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1852 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1853 // an edge and a face barycenter; tertaherdons are based on triangles and
1854 // a volume barycenter
1855 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1857 // Find out how adjacent volumes are split
1859 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1860 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1861 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1863 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1864 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1865 if ( nbNodes < 4 ) continue;
1867 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1868 const int* nInd = vol.GetFaceNodesIndices( iF );
1871 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1872 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1873 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1874 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1878 int iCom = 0; // common node of triangle faces to split into
1879 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1881 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1882 nInd[ iQ * ( (iCom+1)%nbNodes )],
1883 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1884 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1885 nInd[ iQ * ( (iCom+2)%nbNodes )],
1886 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1887 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1889 triaSplits.push_back( t012 );
1890 triaSplits.push_back( t023 );
1895 if ( !triaSplits.empty() )
1896 hasAdjacentSplits = true;
1899 // Among variants of split method select one compliant with adjacent volumes
1901 TSplitMethod method;
1902 if ( !vol.Element()->IsPoly() && !is24TetMode )
1904 int nbVariants = 2, nbTet = 0;
1905 const int** connVariants = 0;
1906 switch ( vol.Element()->GetEntityType() )
1908 case SMDSEntity_Hexa:
1909 case SMDSEntity_Quad_Hexa:
1910 case SMDSEntity_TriQuad_Hexa:
1911 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1912 connVariants = theHexTo5, nbTet = 5;
1914 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1916 case SMDSEntity_Pyramid:
1917 case SMDSEntity_Quad_Pyramid:
1918 connVariants = thePyraTo2; nbTet = 2;
1920 case SMDSEntity_Penta:
1921 case SMDSEntity_Quad_Penta:
1922 case SMDSEntity_BiQuad_Penta:
1923 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1928 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1930 // check method compliancy with adjacent tetras,
1931 // all found splits must be among facets of tetras described by this method
1932 method = TSplitMethod( nbTet, connVariants[variant] );
1933 if ( hasAdjacentSplits && method._nbSplits > 0 )
1935 bool facetCreated = true;
1936 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1938 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1939 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1940 facetCreated = method.hasFacet( *facet );
1942 if ( !facetCreated )
1943 method = TSplitMethod(0); // incompatible method
1947 if ( method._nbSplits < 1 )
1949 // No standard method is applicable, use a generic solution:
1950 // each facet of a volume is split into triangles and
1951 // each of triangles and a volume barycenter form a tetrahedron.
1953 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1955 int* connectivity = new int[ maxTetConnSize + 1 ];
1956 method._connectivity = connectivity;
1957 method._ownConn = true;
1958 method._baryNode = !isHex27; // to create central node or not
1961 int baryCenInd = vol.NbNodes() - int( isHex27 );
1962 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1964 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1965 const int* nInd = vol.GetFaceNodesIndices( iF );
1966 // find common node of triangle facets of tetra to create
1967 int iCommon = 0; // index in linear numeration
1968 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1969 if ( !triaSplits.empty() )
1972 const TTriangleFacet* facet = &triaSplits.front();
1973 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1974 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1975 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1978 else if ( nbNodes > 3 && !is24TetMode )
1980 // find the best method of splitting into triangles by aspect ratio
1981 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1982 map< double, int > badness2iCommon;
1983 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1984 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1985 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1988 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1990 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1991 nodes[ iQ*((iLast-1)%nbNodes)],
1992 nodes[ iQ*((iLast )%nbNodes)]);
1993 badness += getBadRate( &tria, aspectRatio );
1995 badness2iCommon.insert( make_pair( badness, iCommon ));
1997 // use iCommon with lowest badness
1998 iCommon = badness2iCommon.begin()->second;
2000 if ( iCommon >= nbNodes )
2001 iCommon = 0; // something wrong
2003 // fill connectivity of tetrahedra based on a current face
2004 int nbTet = nbNodes - 2;
2005 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2010 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2011 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2015 method._faceBaryNode[ iF ] = 0;
2016 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2019 for ( int i = 0; i < nbTet; ++i )
2021 int i1 = i, i2 = (i+1) % nbNodes;
2022 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2023 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2024 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2025 connectivity[ connSize++ ] = faceBaryCenInd;
2026 connectivity[ connSize++ ] = baryCenInd;
2031 for ( int i = 0; i < nbTet; ++i )
2033 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2034 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2035 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2036 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2037 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2038 connectivity[ connSize++ ] = baryCenInd;
2041 method._nbSplits += nbTet;
2043 } // loop on volume faces
2045 connectivity[ connSize++ ] = -1;
2047 } // end of generic solution
2051 //=======================================================================
2053 * \brief return TSplitMethod to split haxhedron into prisms
2055 //=======================================================================
2057 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2058 const int methodFlags,
2059 const int facetToSplit)
2061 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2063 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2065 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2067 static TSplitMethod to4methods[4]; // order BT, LR, FB
2068 if ( to4methods[iF]._nbSplits == 0 )
2072 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2073 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2074 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2077 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2078 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2079 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2082 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2083 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2084 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2086 default: return to4methods[3];
2088 to4methods[iF]._nbSplits = 4;
2089 to4methods[iF]._nbCorners = 6;
2091 return to4methods[iF];
2093 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2095 TSplitMethod method;
2097 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2099 const int nbVariants = 2, nbSplits = 2;
2100 const int** connVariants = 0;
2102 case 0: connVariants = theHexTo2Prisms_BT; break;
2103 case 1: connVariants = theHexTo2Prisms_LR; break;
2104 case 2: connVariants = theHexTo2Prisms_FB; break;
2105 default: return method;
2108 // look for prisms adjacent via facetToSplit and an opposite one
2109 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2111 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2112 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2113 if ( nbNodes != 4 ) return method;
2115 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2116 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2117 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2119 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2121 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2126 // there are adjacent prism
2127 for ( int variant = 0; variant < nbVariants; ++variant )
2129 // check method compliancy with adjacent prisms,
2130 // the found prism facets must be among facets of prisms described by current method
2131 method._nbSplits = nbSplits;
2132 method._nbCorners = 6;
2133 method._connectivity = connVariants[ variant ];
2134 if ( method.hasFacet( *t ))
2139 // No adjacent prisms. Select a variant with a best aspect ratio.
2141 double badness[2] = { 0., 0. };
2142 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2143 const SMDS_MeshNode** nodes = vol.GetNodes();
2144 for ( int variant = 0; variant < nbVariants; ++variant )
2145 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2147 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2148 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2150 method._connectivity = connVariants[ variant ];
2151 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2152 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2153 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2155 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2158 badness[ variant ] += getBadRate( &tria, aspectRatio );
2160 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2162 method._nbSplits = nbSplits;
2163 method._nbCorners = 6;
2164 method._connectivity = connVariants[ iBetter ];
2169 //================================================================================
2171 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2173 //================================================================================
2175 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2176 const SMDSAbs_GeometryType geom ) const
2178 // find the tetrahedron including the three nodes of facet
2179 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2180 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2181 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2182 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2183 while ( volIt1->more() )
2185 const SMDS_MeshElement* v = volIt1->next();
2186 if ( v->GetGeomType() != geom )
2188 const int lastCornerInd = v->NbCornerNodes() - 1;
2189 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2190 continue; // medium node not allowed
2191 const int ind2 = v->GetNodeIndex( n2 );
2192 if ( ind2 < 0 || lastCornerInd < ind2 )
2194 const int ind3 = v->GetNodeIndex( n3 );
2195 if ( ind3 < 0 || lastCornerInd < ind3 )
2202 //=======================================================================
2204 * \brief A key of a face of volume
2206 //=======================================================================
2208 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2210 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2212 TIDSortedNodeSet sortedNodes;
2213 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2214 int nbNodes = vol.NbFaceNodes( iF );
2215 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2216 for ( int i = 0; i < nbNodes; i += iQ )
2217 sortedNodes.insert( fNodes[i] );
2218 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2219 first.first = (*(n++))->GetID();
2220 first.second = (*(n++))->GetID();
2221 second.first = (*(n++))->GetID();
2222 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2227 //=======================================================================
2228 //function : SplitVolumes
2229 //purpose : Split volume elements into tetrahedra or prisms.
2230 // If facet ID < 0, element is split into tetrahedra,
2231 // else a hexahedron is split into prisms so that the given facet is
2232 // split into triangles
2233 //=======================================================================
2235 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2236 const int theMethodFlags)
2238 SMDS_VolumeTool volTool;
2239 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2240 fHelper.ToFixNodeParameters( true );
2242 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2243 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2245 SMESH_SequenceOfElemPtr newNodes, newElems;
2247 // map face of volume to it's baricenrtic node
2248 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2250 vector<const SMDS_MeshElement* > splitVols;
2252 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2253 for ( ; elem2facet != theElems.end(); ++elem2facet )
2255 const SMDS_MeshElement* elem = elem2facet->first;
2256 const int facetToSplit = elem2facet->second;
2257 if ( elem->GetType() != SMDSAbs_Volume )
2259 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2260 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2263 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2265 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2266 getTetraSplitMethod( volTool, theMethodFlags ) :
2267 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2268 if ( splitMethod._nbSplits < 1 ) continue;
2270 // find submesh to add new tetras to
2271 if ( !subMesh || !subMesh->Contains( elem ))
2273 int shapeID = FindShape( elem );
2274 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2275 subMesh = GetMeshDS()->MeshElements( shapeID );
2278 if ( elem->IsQuadratic() )
2281 // add quadratic links to the helper
2282 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2284 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2285 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2286 for ( int iN = 0; iN < nbN; iN += iQ )
2287 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2289 helper.SetIsQuadratic( true );
2294 helper.SetIsQuadratic( false );
2296 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2297 volTool.GetNodes() + elem->NbNodes() );
2298 helper.SetElementsOnShape( true );
2299 if ( splitMethod._baryNode )
2301 // make a node at barycenter
2302 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2303 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2304 nodes.push_back( gcNode );
2305 newNodes.push_back( gcNode );
2307 if ( !splitMethod._faceBaryNode.empty() )
2309 // make or find baricentric nodes of faces
2310 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2311 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2313 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2314 volFace2BaryNode.insert
2315 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2318 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2319 newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2321 nodes.push_back( iF_n->second = f_n->second );
2326 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2327 const int* volConn = splitMethod._connectivity;
2328 if ( splitMethod._nbCorners == 4 ) // tetra
2329 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2330 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2331 nodes[ volConn[1] ],
2332 nodes[ volConn[2] ],
2333 nodes[ volConn[3] ]));
2335 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2336 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2337 nodes[ volConn[1] ],
2338 nodes[ volConn[2] ],
2339 nodes[ volConn[3] ],
2340 nodes[ volConn[4] ],
2341 nodes[ volConn[5] ]));
2343 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2345 // Split faces on sides of the split volume
2347 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2348 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2350 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2351 if ( nbNodes < 4 ) continue;
2353 // find an existing face
2354 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2355 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2356 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2357 /*noMedium=*/false))
2360 helper.SetElementsOnShape( false );
2361 vector< const SMDS_MeshElement* > triangles;
2363 // find submesh to add new triangles in
2364 if ( !fSubMesh || !fSubMesh->Contains( face ))
2366 int shapeID = FindShape( face );
2367 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2369 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2370 if ( iF_n != splitMethod._faceBaryNode.end() )
2372 const SMDS_MeshNode *baryNode = iF_n->second;
2373 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2375 const SMDS_MeshNode* n1 = fNodes[iN];
2376 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2377 const SMDS_MeshNode *n3 = baryNode;
2378 if ( !volTool.IsFaceExternal( iF ))
2380 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2382 if ( fSubMesh ) // update position of the bary node on geometry
2385 subMesh->RemoveNode( baryNode );
2386 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2387 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2388 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2390 fHelper.SetSubShape( s );
2391 gp_XY uv( 1e100, 1e100 );
2393 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2394 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2397 // node is too far from the surface
2398 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2399 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2400 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2407 // among possible triangles create ones described by split method
2408 const int* nInd = volTool.GetFaceNodesIndices( iF );
2409 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2410 int iCom = 0; // common node of triangle faces to split into
2411 list< TTriangleFacet > facets;
2412 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2414 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2415 nInd[ iQ * ( (iCom+1)%nbNodes )],
2416 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2417 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2418 nInd[ iQ * ( (iCom+2)%nbNodes )],
2419 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2420 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2422 facets.push_back( t012 );
2423 facets.push_back( t023 );
2424 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2425 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2426 nInd[ iQ * ((iLast-1)%nbNodes )],
2427 nInd[ iQ * ((iLast )%nbNodes )]));
2431 list< TTriangleFacet >::iterator facet = facets.begin();
2432 if ( facet == facets.end() )
2434 for ( ; facet != facets.end(); ++facet )
2436 if ( !volTool.IsFaceExternal( iF ))
2437 swap( facet->_n2, facet->_n3 );
2438 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2439 volNodes[ facet->_n2 ],
2440 volNodes[ facet->_n3 ]));
2443 for ( size_t i = 0; i < triangles.size(); ++i )
2445 if ( !triangles[ i ]) continue;
2447 fSubMesh->AddElement( triangles[ i ]);
2448 newElems.push_back( triangles[ i ]);
2450 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2451 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2453 } // while a face based on facet nodes exists
2454 } // loop on volume faces to split them into triangles
2456 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2458 if ( geomType == SMDSEntity_TriQuad_Hexa )
2460 // remove medium nodes that could become free
2461 for ( int i = 20; i < volTool.NbNodes(); ++i )
2462 if ( volNodes[i]->NbInverseElements() == 0 )
2463 GetMeshDS()->RemoveNode( volNodes[i] );
2465 } // loop on volumes to split
2467 myLastCreatedNodes = newNodes;
2468 myLastCreatedElems = newElems;
2471 //=======================================================================
2472 //function : GetHexaFacetsToSplit
2473 //purpose : For hexahedra that will be split into prisms, finds facets to
2474 // split into triangles. Only hexahedra adjacent to the one closest
2475 // to theFacetNormal.Location() are returned.
2476 //param [in,out] theHexas - the hexahedra
2477 //param [in] theFacetNormal - facet normal
2478 //param [out] theFacets - the hexahedra and found facet IDs
2479 //=======================================================================
2481 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2482 const gp_Ax1& theFacetNormal,
2483 TFacetOfElem & theFacets)
2485 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2487 // Find a hexa closest to the location of theFacetNormal
2489 const SMDS_MeshElement* startHex;
2491 // get SMDS_ElemIteratorPtr on theHexas
2492 typedef const SMDS_MeshElement* TValue;
2493 typedef TIDSortedElemSet::iterator TSetIterator;
2494 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2495 typedef SMDS_MeshElement::GeomFilter TFilter;
2496 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2497 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2498 ( new TElemSetIter( theHexas.begin(),
2500 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2502 SMESH_ElementSearcher* searcher =
2503 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2505 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2510 throw SALOME_Exception( THIS_METHOD "startHex not found");
2513 // Select a facet of startHex by theFacetNormal
2515 SMDS_VolumeTool vTool( startHex );
2516 double norm[3], dot, maxDot = 0;
2518 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2519 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2521 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2529 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2531 // Fill theFacets starting from facetID of startHex
2533 // facets used for searching of volumes adjacent to already treated ones
2534 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2535 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2536 TFacetMap facetsToCheck;
2538 set<const SMDS_MeshNode*> facetNodes;
2539 const SMDS_MeshElement* curHex;
2541 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2545 // move in two directions from startHex via facetID
2546 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2549 int curFacet = facetID;
2550 if ( is2nd ) // do not treat startHex twice
2552 vTool.Set( curHex );
2553 if ( vTool.IsFreeFace( curFacet, &curHex ))
2559 vTool.GetFaceNodes( curFacet, facetNodes );
2560 vTool.Set( curHex );
2561 curFacet = vTool.GetFaceIndex( facetNodes );
2566 // store a facet to split
2567 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2569 theFacets.insert( make_pair( curHex, -1 ));
2572 if ( !allHex && !theHexas.count( curHex ))
2575 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2576 theFacets.insert( make_pair( curHex, curFacet ));
2577 if ( !facetIt2isNew.second )
2580 // remember not-to-split facets in facetsToCheck
2581 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2582 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2584 if ( iF == curFacet && iF == oppFacet )
2586 TVolumeFaceKey facetKey ( vTool, iF );
2587 TElemFacets elemFacet( facetIt2isNew.first, iF );
2588 pair< TFacetMap::iterator, bool > it2isnew =
2589 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2590 if ( !it2isnew.second )
2591 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2593 // pass to a volume adjacent via oppFacet
2594 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2600 // get a new curFacet
2601 vTool.GetFaceNodes( oppFacet, facetNodes );
2602 vTool.Set( curHex );
2603 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2606 } // move in two directions from startHex via facetID
2608 // Find a new startHex by facetsToCheck
2612 TFacetMap::iterator fIt = facetsToCheck.begin();
2613 while ( !startHex && fIt != facetsToCheck.end() )
2615 const TElemFacets& elemFacets = fIt->second;
2616 const SMDS_MeshElement* hex = elemFacets.first->first;
2617 int splitFacet = elemFacets.first->second;
2618 int lateralFacet = elemFacets.second;
2619 facetsToCheck.erase( fIt );
2620 fIt = facetsToCheck.begin();
2623 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2624 curHex->GetGeomType() != SMDSGeom_HEXA )
2626 if ( !allHex && !theHexas.count( curHex ))
2631 // find a facet of startHex to split
2633 set<const SMDS_MeshNode*> lateralNodes;
2634 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2635 vTool.GetFaceNodes( splitFacet, facetNodes );
2636 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2637 vTool.Set( startHex );
2638 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2640 // look for a facet of startHex having common nodes with facetNodes
2641 // but not lateralFacet
2642 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2644 if ( iF == lateralFacet )
2646 int nbCommonNodes = 0;
2647 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2648 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2649 nbCommonNodes += facetNodes.count( nn[ iN ]);
2651 if ( nbCommonNodes >= 2 )
2658 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2660 } // while ( startHex )
2667 //================================================================================
2669 * \brief Selects nodes of several elements according to a given interlace
2670 * \param [in] srcNodes - nodes to select from
2671 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2672 * \param [in] interlace - indices of nodes for all elements
2673 * \param [in] nbElems - nb of elements
2674 * \param [in] nbNodes - nb of nodes in each element
2675 * \param [in] mesh - the mesh
2676 * \param [out] elemQueue - a list to push elements found by the selected nodes
2677 * \param [in] type - type of elements to look for
2679 //================================================================================
2681 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2682 vector< const SMDS_MeshNode* >* tgtNodesVec,
2683 const int* interlace,
2686 SMESHDS_Mesh* mesh = 0,
2687 list< const SMDS_MeshElement* >* elemQueue=0,
2688 SMDSAbs_ElementType type=SMDSAbs_All)
2690 for ( int iE = 0; iE < nbElems; ++iE )
2692 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2693 const int* select = & interlace[iE*nbNodes];
2694 elemNodes.resize( nbNodes );
2695 for ( int iN = 0; iN < nbNodes; ++iN )
2696 elemNodes[iN] = srcNodes[ select[ iN ]];
2698 const SMDS_MeshElement* e;
2700 for ( int iE = 0; iE < nbElems; ++iE )
2701 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2702 elemQueue->push_back( e );
2706 //=======================================================================
2708 * Split bi-quadratic elements into linear ones without creation of additional nodes
2709 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2710 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2711 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2712 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2713 * will be split in order to keep the mesh conformal.
2714 * \param elems - elements to split
2716 //=======================================================================
2718 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2720 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2721 vector<const SMDS_MeshElement* > splitElems;
2722 list< const SMDS_MeshElement* > elemQueue;
2723 list< const SMDS_MeshElement* >::iterator elemIt;
2725 SMESHDS_Mesh * mesh = GetMeshDS();
2726 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2727 int nbElems, nbNodes;
2729 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2730 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2733 elemQueue.push_back( *elemSetIt );
2734 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2736 const SMDS_MeshElement* elem = *elemIt;
2737 switch( elem->GetEntityType() )
2739 case SMDSEntity_TriQuad_Hexa: // HEX27
2741 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2742 nbElems = nbNodes = 8;
2743 elemType = & hexaType;
2745 // get nodes for new elements
2746 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2747 { 1,9,20,8, 17,22,26,21 },
2748 { 2,10,20,9, 18,23,26,22 },
2749 { 3,11,20,10, 19,24,26,23 },
2750 { 16,21,26,24, 4,12,25,15 },
2751 { 17,22,26,21, 5,13,25,12 },
2752 { 18,23,26,22, 6,14,25,13 },
2753 { 19,24,26,23, 7,15,25,14 }};
2754 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2756 // add boundary faces to elemQueue
2757 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2758 { 4,5,6,7, 12,13,14,15, 25 },
2759 { 0,1,5,4, 8,17,12,16, 21 },
2760 { 1,2,6,5, 9,18,13,17, 22 },
2761 { 2,3,7,6, 10,19,14,18, 23 },
2762 { 3,0,4,7, 11,16,15,19, 24 }};
2763 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2765 // add boundary segments to elemQueue
2766 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2767 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2768 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2769 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2772 case SMDSEntity_BiQuad_Triangle: // TRIA7
2774 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2777 elemType = & quadType;
2779 // get nodes for new elements
2780 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2781 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2783 // add boundary segments to elemQueue
2784 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2785 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2788 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2790 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2793 elemType = & quadType;
2795 // get nodes for new elements
2796 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2797 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2799 // add boundary segments to elemQueue
2800 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2801 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2804 case SMDSEntity_Quad_Edge:
2806 if ( elemIt == elemQueue.begin() )
2807 continue; // an elem is in theElems
2808 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2811 elemType = & segType;
2813 // get nodes for new elements
2814 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2815 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2819 } // switch( elem->GetEntityType() )
2821 // Create new elements
2823 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2827 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2828 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2829 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2830 //elemType->SetID( -1 );
2832 for ( int iE = 0; iE < nbElems; ++iE )
2833 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2836 ReplaceElemInGroups( elem, splitElems, mesh );
2839 for ( size_t i = 0; i < splitElems.size(); ++i )
2840 subMesh->AddElement( splitElems[i] );
2845 //=======================================================================
2846 //function : AddToSameGroups
2847 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2848 //=======================================================================
2850 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2851 const SMDS_MeshElement* elemInGroups,
2852 SMESHDS_Mesh * aMesh)
2854 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2855 if (!groups.empty()) {
2856 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2857 for ( ; grIt != groups.end(); grIt++ ) {
2858 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2859 if ( group && group->Contains( elemInGroups ))
2860 group->SMDSGroup().Add( elemToAdd );
2866 //=======================================================================
2867 //function : RemoveElemFromGroups
2868 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2869 //=======================================================================
2870 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2871 SMESHDS_Mesh * aMesh)
2873 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2874 if (!groups.empty())
2876 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2877 for (; GrIt != groups.end(); GrIt++)
2879 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2880 if (!grp || grp->IsEmpty()) continue;
2881 grp->SMDSGroup().Remove(removeelem);
2886 //================================================================================
2888 * \brief Replace elemToRm by elemToAdd in the all groups
2890 //================================================================================
2892 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2893 const SMDS_MeshElement* elemToAdd,
2894 SMESHDS_Mesh * aMesh)
2896 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2897 if (!groups.empty()) {
2898 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2899 for ( ; grIt != groups.end(); grIt++ ) {
2900 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2901 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2902 group->SMDSGroup().Add( elemToAdd );
2907 //================================================================================
2909 * \brief Replace elemToRm by elemToAdd in the all groups
2911 //================================================================================
2913 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2914 const vector<const SMDS_MeshElement*>& elemToAdd,
2915 SMESHDS_Mesh * aMesh)
2917 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2918 if (!groups.empty())
2920 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2921 for ( ; grIt != groups.end(); grIt++ ) {
2922 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2923 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2924 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2925 group->SMDSGroup().Add( elemToAdd[ i ] );
2930 //=======================================================================
2931 //function : QuadToTri
2932 //purpose : Cut quadrangles into triangles.
2933 // theCrit is used to select a diagonal to cut
2934 //=======================================================================
2936 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2937 const bool the13Diag)
2940 myLastCreatedElems.reserve( theElems.size() * 2 );
2942 SMESHDS_Mesh * aMesh = GetMeshDS();
2943 Handle(Geom_Surface) surface;
2944 SMESH_MesherHelper helper( *GetMesh() );
2946 TIDSortedElemSet::iterator itElem;
2947 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2949 const SMDS_MeshElement* elem = *itElem;
2950 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2953 if ( elem->NbNodes() == 4 ) {
2954 // retrieve element nodes
2955 const SMDS_MeshNode* aNodes [4];
2956 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2958 while ( itN->more() )
2959 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2961 int aShapeId = FindShape( elem );
2962 const SMDS_MeshElement* newElem1 = 0;
2963 const SMDS_MeshElement* newElem2 = 0;
2965 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2966 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2969 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2970 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2972 myLastCreatedElems.push_back(newElem1);
2973 myLastCreatedElems.push_back(newElem2);
2974 // put a new triangle on the same shape and add to the same groups
2977 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2978 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2980 AddToSameGroups( newElem1, elem, aMesh );
2981 AddToSameGroups( newElem2, elem, aMesh );
2982 aMesh->RemoveElement( elem );
2985 // Quadratic quadrangle
2987 else if ( elem->NbNodes() >= 8 )
2989 // get surface elem is on
2990 int aShapeId = FindShape( elem );
2991 if ( aShapeId != helper.GetSubShapeID() ) {
2995 shape = aMesh->IndexToShape( aShapeId );
2996 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2997 TopoDS_Face face = TopoDS::Face( shape );
2998 surface = BRep_Tool::Surface( face );
2999 if ( !surface.IsNull() )
3000 helper.SetSubShape( shape );
3004 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3005 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3006 for ( int i = 0; itN->more(); ++i )
3007 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3009 const SMDS_MeshNode* centrNode = aNodes[8];
3010 if ( centrNode == 0 )
3012 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3013 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3015 myLastCreatedNodes.push_back(centrNode);
3018 // create a new element
3019 const SMDS_MeshElement* newElem1 = 0;
3020 const SMDS_MeshElement* newElem2 = 0;
3022 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3023 aNodes[6], aNodes[7], centrNode );
3024 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3025 centrNode, aNodes[4], aNodes[5] );
3028 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3029 aNodes[7], aNodes[4], centrNode );
3030 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3031 centrNode, aNodes[5], aNodes[6] );
3033 myLastCreatedElems.push_back(newElem1);
3034 myLastCreatedElems.push_back(newElem2);
3035 // put a new triangle on the same shape and add to the same groups
3038 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3039 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3041 AddToSameGroups( newElem1, elem, aMesh );
3042 AddToSameGroups( newElem2, elem, aMesh );
3043 aMesh->RemoveElement( elem );
3050 //=======================================================================
3051 //function : getAngle
3053 //=======================================================================
3055 double getAngle(const SMDS_MeshElement * tr1,
3056 const SMDS_MeshElement * tr2,
3057 const SMDS_MeshNode * n1,
3058 const SMDS_MeshNode * n2)
3060 double angle = 2. * M_PI; // bad angle
3063 SMESH::Controls::TSequenceOfXYZ P1, P2;
3064 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3065 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3068 if(!tr1->IsQuadratic())
3069 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3071 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3072 if ( N1.SquareMagnitude() <= gp::Resolution() )
3074 if(!tr2->IsQuadratic())
3075 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3077 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3078 if ( N2.SquareMagnitude() <= gp::Resolution() )
3081 // find the first diagonal node n1 in the triangles:
3082 // take in account a diagonal link orientation
3083 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3084 for ( int t = 0; t < 2; t++ ) {
3085 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3086 int i = 0, iDiag = -1;
3087 while ( it->more()) {
3088 const SMDS_MeshElement *n = it->next();
3089 if ( n == n1 || n == n2 ) {
3093 if ( i - iDiag == 1 )
3094 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3103 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3106 angle = N1.Angle( N2 );
3111 // =================================================
3112 // class generating a unique ID for a pair of nodes
3113 // and able to return nodes by that ID
3114 // =================================================
3118 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3119 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3122 long GetLinkID (const SMDS_MeshNode * n1,
3123 const SMDS_MeshNode * n2) const
3125 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3128 bool GetNodes (const long theLinkID,
3129 const SMDS_MeshNode* & theNode1,
3130 const SMDS_MeshNode* & theNode2) const
3132 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3133 if ( !theNode1 ) return false;
3134 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3135 if ( !theNode2 ) return false;
3141 const SMESHDS_Mesh* myMesh;
3146 //=======================================================================
3147 //function : TriToQuad
3148 //purpose : Fuse neighbour triangles into quadrangles.
3149 // theCrit is used to select a neighbour to fuse with.
3150 // theMaxAngle is a max angle between element normals at which
3151 // fusion is still performed.
3152 //=======================================================================
3154 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3155 SMESH::Controls::NumericalFunctorPtr theCrit,
3156 const double theMaxAngle)
3159 myLastCreatedElems.reserve( theElems.size() / 2 );
3161 if ( !theCrit.get() )
3164 SMESHDS_Mesh * aMesh = GetMeshDS();
3166 // Prepare data for algo: build
3167 // 1. map of elements with their linkIDs
3168 // 2. map of linkIDs with their elements
3170 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3171 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3172 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3173 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3175 TIDSortedElemSet::iterator itElem;
3176 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3178 const SMDS_MeshElement* elem = *itElem;
3179 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3180 bool IsTria = ( elem->NbCornerNodes()==3 );
3181 if (!IsTria) continue;
3183 // retrieve element nodes
3184 const SMDS_MeshNode* aNodes [4];
3185 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3188 aNodes[ i++ ] = itN->next();
3189 aNodes[ 3 ] = aNodes[ 0 ];
3192 for ( i = 0; i < 3; i++ ) {
3193 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3194 // check if elements sharing a link can be fused
3195 itLE = mapLi_listEl.find( link );
3196 if ( itLE != mapLi_listEl.end() ) {
3197 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3199 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3200 //if ( FindShape( elem ) != FindShape( elem2 ))
3201 // continue; // do not fuse triangles laying on different shapes
3202 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3203 continue; // avoid making badly shaped quads
3204 (*itLE).second.push_back( elem );
3207 mapLi_listEl[ link ].push_back( elem );
3209 mapEl_setLi [ elem ].insert( link );
3212 // Clean the maps from the links shared by a sole element, ie
3213 // links to which only one element is bound in mapLi_listEl
3215 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3216 int nbElems = (*itLE).second.size();
3217 if ( nbElems < 2 ) {
3218 const SMDS_MeshElement* elem = (*itLE).second.front();
3219 SMESH_TLink link = (*itLE).first;
3220 mapEl_setLi[ elem ].erase( link );
3221 if ( mapEl_setLi[ elem ].empty() )
3222 mapEl_setLi.erase( elem );
3226 // Algo: fuse triangles into quadrangles
3228 while ( ! mapEl_setLi.empty() ) {
3229 // Look for the start element:
3230 // the element having the least nb of shared links
3231 const SMDS_MeshElement* startElem = 0;
3233 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3234 int nbLinks = (*itEL).second.size();
3235 if ( nbLinks < minNbLinks ) {
3236 startElem = (*itEL).first;
3237 minNbLinks = nbLinks;
3238 if ( minNbLinks == 1 )
3243 // search elements to fuse starting from startElem or links of elements
3244 // fused earlyer - startLinks
3245 list< SMESH_TLink > startLinks;
3246 while ( startElem || !startLinks.empty() ) {
3247 while ( !startElem && !startLinks.empty() ) {
3248 // Get an element to start, by a link
3249 SMESH_TLink linkId = startLinks.front();
3250 startLinks.pop_front();
3251 itLE = mapLi_listEl.find( linkId );
3252 if ( itLE != mapLi_listEl.end() ) {
3253 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3254 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3255 for ( ; itE != listElem.end() ; itE++ )
3256 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3258 mapLi_listEl.erase( itLE );
3263 // Get candidates to be fused
3264 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3265 const SMESH_TLink *link12 = 0, *link13 = 0;
3267 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3268 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3269 ASSERT( !setLi.empty() );
3270 set< SMESH_TLink >::iterator itLi;
3271 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3273 const SMESH_TLink & link = (*itLi);
3274 itLE = mapLi_listEl.find( link );
3275 if ( itLE == mapLi_listEl.end() )
3278 const SMDS_MeshElement* elem = (*itLE).second.front();
3280 elem = (*itLE).second.back();
3281 mapLi_listEl.erase( itLE );
3282 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3293 // add other links of elem to list of links to re-start from
3294 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3295 set< SMESH_TLink >::iterator it;
3296 for ( it = links.begin(); it != links.end(); it++ ) {
3297 const SMESH_TLink& link2 = (*it);
3298 if ( link2 != link )
3299 startLinks.push_back( link2 );
3303 // Get nodes of possible quadrangles
3304 const SMDS_MeshNode *n12 [4], *n13 [4];
3305 bool Ok12 = false, Ok13 = false;
3306 const SMDS_MeshNode *linkNode1, *linkNode2;
3308 linkNode1 = link12->first;
3309 linkNode2 = link12->second;
3310 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3314 linkNode1 = link13->first;
3315 linkNode2 = link13->second;
3316 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3320 // Choose a pair to fuse
3321 if ( Ok12 && Ok13 ) {
3322 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3323 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3324 double aBadRate12 = getBadRate( &quad12, theCrit );
3325 double aBadRate13 = getBadRate( &quad13, theCrit );
3326 if ( aBadRate13 < aBadRate12 )
3333 // and remove fused elems and remove links from the maps
3334 mapEl_setLi.erase( tr1 );
3337 mapEl_setLi.erase( tr2 );
3338 mapLi_listEl.erase( *link12 );
3339 if ( tr1->NbNodes() == 3 )
3341 const SMDS_MeshElement* newElem = 0;
3342 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3343 myLastCreatedElems.push_back(newElem);
3344 AddToSameGroups( newElem, tr1, aMesh );
3345 int aShapeId = tr1->getshapeId();
3347 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3348 aMesh->RemoveElement( tr1 );
3349 aMesh->RemoveElement( tr2 );
3352 vector< const SMDS_MeshNode* > N1;
3353 vector< const SMDS_MeshNode* > N2;
3354 getNodesFromTwoTria(tr1,tr2,N1,N2);
3355 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3356 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3357 // i.e. first nodes from both arrays form a new diagonal
3358 const SMDS_MeshNode* aNodes[8];
3367 const SMDS_MeshElement* newElem = 0;
3368 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3369 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3370 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3372 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3373 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3374 myLastCreatedElems.push_back(newElem);
3375 AddToSameGroups( newElem, tr1, aMesh );
3376 int aShapeId = tr1->getshapeId();
3378 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3379 aMesh->RemoveElement( tr1 );
3380 aMesh->RemoveElement( tr2 );
3381 // remove middle node (9)
3382 if ( N1[4]->NbInverseElements() == 0 )
3383 aMesh->RemoveNode( N1[4] );
3384 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3385 aMesh->RemoveNode( N1[6] );
3386 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3387 aMesh->RemoveNode( N2[6] );
3392 mapEl_setLi.erase( tr3 );
3393 mapLi_listEl.erase( *link13 );
3394 if ( tr1->NbNodes() == 3 ) {
3395 const SMDS_MeshElement* newElem = 0;
3396 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3397 myLastCreatedElems.push_back(newElem);
3398 AddToSameGroups( newElem, tr1, aMesh );
3399 int aShapeId = tr1->getshapeId();
3401 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3402 aMesh->RemoveElement( tr1 );
3403 aMesh->RemoveElement( tr3 );
3406 vector< const SMDS_MeshNode* > N1;
3407 vector< const SMDS_MeshNode* > N2;
3408 getNodesFromTwoTria(tr1,tr3,N1,N2);
3409 // now we receive following N1 and N2 (using numeration as above image)
3410 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3411 // i.e. first nodes from both arrays form a new diagonal
3412 const SMDS_MeshNode* aNodes[8];
3421 const SMDS_MeshElement* newElem = 0;
3422 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3423 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3424 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3426 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3427 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3428 myLastCreatedElems.push_back(newElem);
3429 AddToSameGroups( newElem, tr1, aMesh );
3430 int aShapeId = tr1->getshapeId();
3432 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3433 aMesh->RemoveElement( tr1 );
3434 aMesh->RemoveElement( tr3 );
3435 // remove middle node (9)
3436 if ( N1[4]->NbInverseElements() == 0 )
3437 aMesh->RemoveNode( N1[4] );
3438 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3439 aMesh->RemoveNode( N1[6] );
3440 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3441 aMesh->RemoveNode( N2[6] );
3445 // Next element to fuse: the rejected one
3447 startElem = Ok12 ? tr3 : tr2;
3449 } // if ( startElem )
3450 } // while ( startElem || !startLinks.empty() )
3451 } // while ( ! mapEl_setLi.empty() )
3456 //================================================================================
3458 * \brief Return nodes linked to the given one
3459 * \param theNode - the node
3460 * \param linkedNodes - the found nodes
3461 * \param type - the type of elements to check
3463 * Medium nodes are ignored
3465 //================================================================================
3467 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3468 TIDSortedElemSet & linkedNodes,
3469 SMDSAbs_ElementType type )
3471 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3472 while ( elemIt->more() )
3474 const SMDS_MeshElement* elem = elemIt->next();
3475 if(elem->GetType() == SMDSAbs_0DElement)
3478 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3479 if ( elem->GetType() == SMDSAbs_Volume )
3481 SMDS_VolumeTool vol( elem );
3482 while ( nodeIt->more() ) {
3483 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3484 if ( theNode != n && vol.IsLinked( theNode, n ))
3485 linkedNodes.insert( n );
3490 for ( int i = 0; nodeIt->more(); ++i ) {
3491 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3492 if ( n == theNode ) {
3493 int iBefore = i - 1;
3495 if ( elem->IsQuadratic() ) {
3496 int nb = elem->NbNodes() / 2;
3497 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3498 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3500 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3501 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3508 //=======================================================================
3509 //function : laplacianSmooth
3510 //purpose : pulls theNode toward the center of surrounding nodes directly
3511 // connected to that node along an element edge
3512 //=======================================================================
3514 void laplacianSmooth(const SMDS_MeshNode* theNode,
3515 const Handle(Geom_Surface)& theSurface,
3516 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3518 // find surrounding nodes
3520 TIDSortedElemSet nodeSet;
3521 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3523 // compute new coodrs
3525 double coord[] = { 0., 0., 0. };
3526 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3527 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3528 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3529 if ( theSurface.IsNull() ) { // smooth in 3D
3530 coord[0] += node->X();
3531 coord[1] += node->Y();
3532 coord[2] += node->Z();
3534 else { // smooth in 2D
3535 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3536 gp_XY* uv = theUVMap[ node ];
3537 coord[0] += uv->X();
3538 coord[1] += uv->Y();
3541 int nbNodes = nodeSet.size();
3544 coord[0] /= nbNodes;
3545 coord[1] /= nbNodes;
3547 if ( !theSurface.IsNull() ) {
3548 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3549 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3550 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3556 coord[2] /= nbNodes;
3560 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3563 //=======================================================================
3564 //function : centroidalSmooth
3565 //purpose : pulls theNode toward the element-area-weighted centroid of the
3566 // surrounding elements
3567 //=======================================================================
3569 void centroidalSmooth(const SMDS_MeshNode* theNode,
3570 const Handle(Geom_Surface)& theSurface,
3571 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3573 gp_XYZ aNewXYZ(0.,0.,0.);
3574 SMESH::Controls::Area anAreaFunc;
3575 double totalArea = 0.;
3580 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3581 while ( elemIt->more() )
3583 const SMDS_MeshElement* elem = elemIt->next();
3586 gp_XYZ elemCenter(0.,0.,0.);
3587 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3588 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3589 int nn = elem->NbNodes();
3590 if(elem->IsQuadratic()) nn = nn/2;
3592 //while ( itN->more() ) {
3594 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3596 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3597 aNodePoints.push_back( aP );
3598 if ( !theSurface.IsNull() ) { // smooth in 2D
3599 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3600 gp_XY* uv = theUVMap[ aNode ];
3601 aP.SetCoord( uv->X(), uv->Y(), 0. );
3605 double elemArea = anAreaFunc.GetValue( aNodePoints );
3606 totalArea += elemArea;
3608 aNewXYZ += elemCenter * elemArea;
3610 aNewXYZ /= totalArea;
3611 if ( !theSurface.IsNull() ) {
3612 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3613 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3618 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3621 //=======================================================================
3622 //function : getClosestUV
3623 //purpose : return UV of closest projection
3624 //=======================================================================
3626 static bool getClosestUV (Extrema_GenExtPS& projector,
3627 const gp_Pnt& point,
3630 projector.Perform( point );
3631 if ( projector.IsDone() ) {
3632 double u = 0, v = 0, minVal = DBL_MAX;
3633 for ( int i = projector.NbExt(); i > 0; i-- )
3634 if ( projector.SquareDistance( i ) < minVal ) {
3635 minVal = projector.SquareDistance( i );
3636 projector.Point( i ).Parameter( u, v );
3638 result.SetCoord( u, v );
3644 //=======================================================================
3646 //purpose : Smooth theElements during theNbIterations or until a worst
3647 // element has aspect ratio <= theTgtAspectRatio.
3648 // Aspect Ratio varies in range [1.0, inf].
3649 // If theElements is empty, the whole mesh is smoothed.
3650 // theFixedNodes contains additionally fixed nodes. Nodes built
3651 // on edges and boundary nodes are always fixed.
3652 //=======================================================================
3654 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3655 set<const SMDS_MeshNode*> & theFixedNodes,
3656 const SmoothMethod theSmoothMethod,
3657 const int theNbIterations,
3658 double theTgtAspectRatio,
3663 if ( theTgtAspectRatio < 1.0 )
3664 theTgtAspectRatio = 1.0;
3666 const double disttol = 1.e-16;
3668 SMESH::Controls::AspectRatio aQualityFunc;
3670 SMESHDS_Mesh* aMesh = GetMeshDS();
3672 if ( theElems.empty() ) {
3673 // add all faces to theElems
3674 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3675 while ( fIt->more() ) {
3676 const SMDS_MeshElement* face = fIt->next();
3677 theElems.insert( theElems.end(), face );
3680 // get all face ids theElems are on
3681 set< int > faceIdSet;
3682 TIDSortedElemSet::iterator itElem;
3684 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3685 int fId = FindShape( *itElem );
3686 // check that corresponding submesh exists and a shape is face
3688 faceIdSet.find( fId ) == faceIdSet.end() &&
3689 aMesh->MeshElements( fId )) {
3690 TopoDS_Shape F = aMesh->IndexToShape( fId );
3691 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3692 faceIdSet.insert( fId );
3695 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3697 // ===============================================
3698 // smooth elements on each TopoDS_Face separately
3699 // ===============================================
3701 SMESH_MesherHelper helper( *GetMesh() );
3703 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3704 for ( ; fId != faceIdSet.rend(); ++fId )
3706 // get face surface and submesh
3707 Handle(Geom_Surface) surface;
3708 SMESHDS_SubMesh* faceSubMesh = 0;
3711 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3712 bool isUPeriodic = false, isVPeriodic = false;
3715 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3716 surface = BRep_Tool::Surface( face );
3717 faceSubMesh = aMesh->MeshElements( *fId );
3718 fToler2 = BRep_Tool::Tolerance( face );
3719 fToler2 *= fToler2 * 10.;
3720 isUPeriodic = surface->IsUPeriodic();
3721 // if ( isUPeriodic )
3722 // surface->UPeriod();
3723 isVPeriodic = surface->IsVPeriodic();
3724 // if ( isVPeriodic )
3725 // surface->VPeriod();
3726 surface->Bounds( u1, u2, v1, v2 );
3727 helper.SetSubShape( face );
3729 // ---------------------------------------------------------
3730 // for elements on a face, find movable and fixed nodes and
3731 // compute UV for them
3732 // ---------------------------------------------------------
3733 bool checkBoundaryNodes = false;
3734 bool isQuadratic = false;
3735 set<const SMDS_MeshNode*> setMovableNodes;
3736 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3737 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3738 list< const SMDS_MeshElement* > elemsOnFace;
3740 Extrema_GenExtPS projector;
3741 GeomAdaptor_Surface surfAdaptor;
3742 if ( !surface.IsNull() ) {
3743 surfAdaptor.Load( surface );
3744 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3746 int nbElemOnFace = 0;
3747 itElem = theElems.begin();
3748 // loop on not yet smoothed elements: look for elems on a face
3749 while ( itElem != theElems.end() )
3751 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3752 break; // all elements found
3754 const SMDS_MeshElement* elem = *itElem;
3755 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3756 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3760 elemsOnFace.push_back( elem );
3761 theElems.erase( itElem++ );
3765 isQuadratic = elem->IsQuadratic();
3767 // get movable nodes of elem
3768 const SMDS_MeshNode* node;
3769 SMDS_TypeOfPosition posType;
3770 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3771 int nn = 0, nbn = elem->NbNodes();
3772 if(elem->IsQuadratic())
3774 while ( nn++ < nbn ) {
3775 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3776 const SMDS_PositionPtr& pos = node->GetPosition();
3777 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3778 if (posType != SMDS_TOP_EDGE &&
3779 posType != SMDS_TOP_VERTEX &&
3780 theFixedNodes.find( node ) == theFixedNodes.end())
3782 // check if all faces around the node are on faceSubMesh
3783 // because a node on edge may be bound to face
3785 if ( faceSubMesh ) {
3786 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3787 while ( eIt->more() && all ) {
3788 const SMDS_MeshElement* e = eIt->next();
3789 all = faceSubMesh->Contains( e );
3793 setMovableNodes.insert( node );
3795 checkBoundaryNodes = true;
3797 if ( posType == SMDS_TOP_3DSPACE )
3798 checkBoundaryNodes = true;
3801 if ( surface.IsNull() )
3804 // get nodes to check UV
3805 list< const SMDS_MeshNode* > uvCheckNodes;
3806 const SMDS_MeshNode* nodeInFace = 0;
3807 itN = elem->nodesIterator();
3808 nn = 0; nbn = elem->NbNodes();
3809 if(elem->IsQuadratic())
3811 while ( nn++ < nbn ) {
3812 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3813 if ( node->GetPosition()->GetDim() == 2 )
3815 if ( uvMap.find( node ) == uvMap.end() )
3816 uvCheckNodes.push_back( node );
3817 // add nodes of elems sharing node
3818 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3819 // while ( eIt->more() ) {
3820 // const SMDS_MeshElement* e = eIt->next();
3821 // if ( e != elem ) {
3822 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3823 // while ( nIt->more() ) {
3824 // const SMDS_MeshNode* n =
3825 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3826 // if ( uvMap.find( n ) == uvMap.end() )
3827 // uvCheckNodes.push_back( n );
3833 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3834 for ( ; n != uvCheckNodes.end(); ++n ) {
3837 const SMDS_PositionPtr& pos = node->GetPosition();
3838 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3842 bool toCheck = true;
3843 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3845 // compute not existing UV
3846 bool project = ( posType == SMDS_TOP_3DSPACE );
3847 // double dist1 = DBL_MAX, dist2 = 0;
3848 // if ( posType != SMDS_TOP_3DSPACE ) {
3849 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3850 // project = dist1 > fToler2;
3852 if ( project ) { // compute new UV
3854 gp_Pnt pNode = SMESH_NodeXYZ( node );
3855 if ( !getClosestUV( projector, pNode, newUV )) {
3856 MESSAGE("Node Projection Failed " << node);
3860 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3862 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3864 // if ( posType != SMDS_TOP_3DSPACE )
3865 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3866 // if ( dist2 < dist1 )
3870 // store UV in the map
3871 listUV.push_back( uv );
3872 uvMap.insert( make_pair( node, &listUV.back() ));
3874 } // loop on not yet smoothed elements
3876 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3877 checkBoundaryNodes = true;
3879 // fix nodes on mesh boundary
3881 if ( checkBoundaryNodes ) {
3882 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3883 map< SMESH_TLink, int >::iterator link_nb;
3884 // put all elements links to linkNbMap
3885 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3886 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3887 const SMDS_MeshElement* elem = (*elemIt);
3888 int nbn = elem->NbCornerNodes();
3889 // loop on elem links: insert them in linkNbMap
3890 for ( int iN = 0; iN < nbn; ++iN ) {
3891 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3892 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3893 SMESH_TLink link( n1, n2 );
3894 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3898 // remove nodes that are in links encountered only once from setMovableNodes
3899 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3900 if ( link_nb->second == 1 ) {
3901 setMovableNodes.erase( link_nb->first.node1() );
3902 setMovableNodes.erase( link_nb->first.node2() );
3907 // -----------------------------------------------------
3908 // for nodes on seam edge, compute one more UV ( uvMap2 );
3909 // find movable nodes linked to nodes on seam and which
3910 // are to be smoothed using the second UV ( uvMap2 )
3911 // -----------------------------------------------------
3913 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3914 if ( !surface.IsNull() ) {
3915 TopExp_Explorer eExp( face, TopAbs_EDGE );
3916 for ( ; eExp.More(); eExp.Next() ) {
3917 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3918 if ( !BRep_Tool::IsClosed( edge, face ))
3920 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3921 if ( !sm ) continue;
3922 // find out which parameter varies for a node on seam
3925 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3926 if ( pcurve.IsNull() ) continue;
3927 uv1 = pcurve->Value( f );
3929 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3930 if ( pcurve.IsNull() ) continue;
3931 uv2 = pcurve->Value( f );
3932 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3934 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3935 std::swap( uv1, uv2 );
3936 // get nodes on seam and its vertices
3937 list< const SMDS_MeshNode* > seamNodes;
3938 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3939 while ( nSeamIt->more() ) {
3940 const SMDS_MeshNode* node = nSeamIt->next();
3941 if ( !isQuadratic || !IsMedium( node ))
3942 seamNodes.push_back( node );
3944 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3945 for ( ; vExp.More(); vExp.Next() ) {
3946 sm = aMesh->MeshElements( vExp.Current() );
3948 nSeamIt = sm->GetNodes();
3949 while ( nSeamIt->more() )
3950 seamNodes.push_back( nSeamIt->next() );
3953 // loop on nodes on seam
3954 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3955 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3956 const SMDS_MeshNode* nSeam = *noSeIt;
3957 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3958 if ( n_uv == uvMap.end() )
3961 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3962 // set the second UV
3963 listUV.push_back( *n_uv->second );
3964 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3965 if ( uvMap2.empty() )
3966 uvMap2 = uvMap; // copy the uvMap contents
3967 uvMap2[ nSeam ] = &listUV.back();
3969 // collect movable nodes linked to ones on seam in nodesNearSeam
3970 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3971 while ( eIt->more() ) {
3972 const SMDS_MeshElement* e = eIt->next();
3973 int nbUseMap1 = 0, nbUseMap2 = 0;
3974 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3975 int nn = 0, nbn = e->NbNodes();
3976 if(e->IsQuadratic()) nbn = nbn/2;
3977 while ( nn++ < nbn )
3979 const SMDS_MeshNode* n =
3980 static_cast<const SMDS_MeshNode*>( nIt->next() );
3982 setMovableNodes.find( n ) == setMovableNodes.end() )
3984 // add only nodes being closer to uv2 than to uv1
3985 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3986 // 0.5 * ( n->Y() + nSeam->Y() ),
3987 // 0.5 * ( n->Z() + nSeam->Z() ));
3989 // getClosestUV( projector, pMid, uv );
3990 double x = uvMap[ n ]->Coord( iPar );
3991 if ( Abs( uv1.Coord( iPar ) - x ) >
3992 Abs( uv2.Coord( iPar ) - x )) {
3993 nodesNearSeam.insert( n );
3999 // for centroidalSmooth all element nodes must
4000 // be on one side of a seam
4001 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4002 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4004 while ( nn++ < nbn ) {
4005 const SMDS_MeshNode* n =
4006 static_cast<const SMDS_MeshNode*>( nIt->next() );
4007 setMovableNodes.erase( n );
4011 } // loop on nodes on seam
4012 } // loop on edge of a face
4013 } // if ( !face.IsNull() )
4015 if ( setMovableNodes.empty() ) {
4016 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4017 continue; // goto next face
4025 double maxRatio = -1., maxDisplacement = -1.;
4026 set<const SMDS_MeshNode*>::iterator nodeToMove;
4027 for ( it = 0; it < theNbIterations; it++ ) {
4028 maxDisplacement = 0.;
4029 nodeToMove = setMovableNodes.begin();
4030 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4031 const SMDS_MeshNode* node = (*nodeToMove);
4032 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4035 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4036 if ( theSmoothMethod == LAPLACIAN )
4037 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4039 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4041 // node displacement
4042 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4043 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4044 if ( aDispl > maxDisplacement )
4045 maxDisplacement = aDispl;
4047 // no node movement => exit
4048 //if ( maxDisplacement < 1.e-16 ) {
4049 if ( maxDisplacement < disttol ) {
4050 MESSAGE("-- no node movement --");
4054 // check elements quality
4056 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4057 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4058 const SMDS_MeshElement* elem = (*elemIt);
4059 if ( !elem || elem->GetType() != SMDSAbs_Face )
4061 SMESH::Controls::TSequenceOfXYZ aPoints;
4062 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4063 double aValue = aQualityFunc.GetValue( aPoints );
4064 if ( aValue > maxRatio )
4068 if ( maxRatio <= theTgtAspectRatio ) {
4069 //MESSAGE("-- quality achieved --");
4072 if (it+1 == theNbIterations) {
4073 //MESSAGE("-- Iteration limit exceeded --");
4075 } // smoothing iterations
4077 // MESSAGE(" Face id: " << *fId <<
4078 // " Nb iterstions: " << it <<
4079 // " Displacement: " << maxDisplacement <<
4080 // " Aspect Ratio " << maxRatio);
4082 // ---------------------------------------
4083 // new nodes positions are computed,
4084 // record movement in DS and set new UV
4085 // ---------------------------------------
4086 nodeToMove = setMovableNodes.begin();
4087 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4088 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4089 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4090 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4091 if ( node_uv != uvMap.end() ) {
4092 gp_XY* uv = node_uv->second;
4094 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4098 // move medium nodes of quadratic elements
4101 vector<const SMDS_MeshNode*> nodes;
4103 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4104 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4106 const SMDS_MeshElement* QF = *elemIt;
4107 if ( QF->IsQuadratic() )
4109 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4110 SMDS_MeshElement::iterator() );
4111 nodes.push_back( nodes[0] );
4113 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4115 if ( !surface.IsNull() )
4117 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4118 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4119 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4120 xyz = surface->Value( uv.X(), uv.Y() );
4123 xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4125 if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4126 // we have to move a medium node
4127 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4133 } // loop on face ids
4139 //=======================================================================
4140 //function : isReverse
4141 //purpose : Return true if normal of prevNodes is not co-directied with
4142 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4143 // iNotSame is where prevNodes and nextNodes are different.
4144 // If result is true then future volume orientation is OK
4145 //=======================================================================
4147 bool isReverse(const SMDS_MeshElement* face,
4148 const vector<const SMDS_MeshNode*>& prevNodes,
4149 const vector<const SMDS_MeshNode*>& nextNodes,
4153 SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4154 SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4155 gp_XYZ extrDir( pN - pP ), faceNorm;
4156 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4158 return faceNorm * extrDir < 0.0;
4161 //================================================================================
4163 * \brief Assure that theElemSets[0] holds elements, not nodes
4165 //================================================================================
4167 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4169 if ( !theElemSets[0].empty() &&
4170 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4172 std::swap( theElemSets[0], theElemSets[1] );
4174 else if ( !theElemSets[1].empty() &&
4175 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4177 std::swap( theElemSets[0], theElemSets[1] );
4182 //=======================================================================
4184 * \brief Create elements by sweeping an element
4185 * \param elem - element to sweep
4186 * \param newNodesItVec - nodes generated from each node of the element
4187 * \param newElems - generated elements
4188 * \param nbSteps - number of sweeping steps
4189 * \param srcElements - to append elem for each generated element
4191 //=======================================================================
4193 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4194 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4195 list<const SMDS_MeshElement*>& newElems,
4196 const size_t nbSteps,
4197 SMESH_SequenceOfElemPtr& srcElements)
4199 SMESHDS_Mesh* aMesh = GetMeshDS();
4201 const int nbNodes = elem->NbNodes();
4202 const int nbCorners = elem->NbCornerNodes();
4203 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4204 polyhedron creation !!! */
4205 // Loop on elem nodes:
4206 // find new nodes and detect same nodes indices
4207 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4208 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4209 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4210 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4212 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4213 vector<int> sames(nbNodes);
4214 vector<bool> isSingleNode(nbNodes);
4216 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4217 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4218 const SMDS_MeshNode* node = nnIt->first;
4219 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4220 if ( listNewNodes.empty() )
4223 itNN [ iNode ] = listNewNodes.begin();
4224 prevNod[ iNode ] = node;
4225 nextNod[ iNode ] = listNewNodes.front();
4227 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4228 corner node of linear */
4229 if ( prevNod[ iNode ] != nextNod [ iNode ])
4230 nbDouble += !isSingleNode[iNode];
4232 if( iNode < nbCorners ) { // check corners only
4233 if ( prevNod[ iNode ] == nextNod [ iNode ])
4234 sames[nbSame++] = iNode;
4236 iNotSameNode = iNode;
4240 if ( nbSame == nbNodes || nbSame > 2) {
4241 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4245 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4247 // fix nodes order to have bottom normal external
4248 if ( baseType == SMDSEntity_Polygon )
4250 std::reverse( itNN.begin(), itNN.end() );
4251 std::reverse( prevNod.begin(), prevNod.end() );
4252 std::reverse( midlNod.begin(), midlNod.end() );
4253 std::reverse( nextNod.begin(), nextNod.end() );
4254 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4258 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4259 SMDS_MeshCell::applyInterlace( ind, itNN );
4260 SMDS_MeshCell::applyInterlace( ind, prevNod );
4261 SMDS_MeshCell::applyInterlace( ind, nextNod );
4262 SMDS_MeshCell::applyInterlace( ind, midlNod );
4263 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4266 sames[nbSame] = iNotSameNode;
4267 for ( int j = 0; j <= nbSame; ++j )
4268 for ( size_t i = 0; i < ind.size(); ++i )
4269 if ( ind[i] == sames[j] )
4274 iNotSameNode = sames[nbSame];
4278 else if ( elem->GetType() == SMDSAbs_Edge )
4280 // orient a new face same as adjacent one
4282 const SMDS_MeshElement* e;
4283 TIDSortedElemSet dummy;
4284 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4285 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4286 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4288 // there is an adjacent face, check order of nodes in it
4289 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4292 std::swap( itNN[0], itNN[1] );
4293 std::swap( prevNod[0], prevNod[1] );
4294 std::swap( nextNod[0], nextNod[1] );
4295 std::swap( isSingleNode[0], isSingleNode[1] );
4297 sames[0] = 1 - sames[0];
4298 iNotSameNode = 1 - iNotSameNode;
4303 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4305 iSameNode = sames[ nbSame-1 ];
4306 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4307 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4308 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4311 if ( baseType == SMDSEntity_Polygon )
4313 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4314 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4316 else if ( baseType == SMDSEntity_Quad_Polygon )
4318 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4319 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4322 // make new elements
4323 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4326 for ( iNode = 0; iNode < nbNodes; iNode++ )
4328 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4329 nextNod[ iNode ] = *itNN[ iNode ]++;
4332 SMDS_MeshElement* aNewElem = 0;
4333 /*if(!elem->IsPoly())*/ {
4334 switch ( baseType ) {
4336 case SMDSEntity_Node: { // sweep NODE
4337 if ( nbSame == 0 ) {
4338 if ( isSingleNode[0] )
4339 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4341 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4347 case SMDSEntity_Edge: { // sweep EDGE
4348 if ( nbDouble == 0 )
4350 if ( nbSame == 0 ) // ---> quadrangle
4351 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4352 nextNod[ 1 ], nextNod[ 0 ] );
4353 else // ---> triangle
4354 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4355 nextNod[ iNotSameNode ] );
4357 else // ---> polygon
4359 vector<const SMDS_MeshNode*> poly_nodes;
4360 poly_nodes.push_back( prevNod[0] );
4361 poly_nodes.push_back( prevNod[1] );
4362 if ( prevNod[1] != nextNod[1] )
4364 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4365 poly_nodes.push_back( nextNod[1] );
4367 if ( prevNod[0] != nextNod[0] )
4369 poly_nodes.push_back( nextNod[0] );
4370 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4372 switch ( poly_nodes.size() ) {
4374 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4377 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4378 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4381 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4386 case SMDSEntity_Triangle: // TRIANGLE --->
4388 if ( nbDouble > 0 ) break;
4389 if ( nbSame == 0 ) // ---> pentahedron
4390 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4391 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4393 else if ( nbSame == 1 ) // ---> pyramid
4394 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4395 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4396 nextNod[ iSameNode ]);
4398 else // 2 same nodes: ---> tetrahedron
4399 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4400 nextNod[ iNotSameNode ]);
4403 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4407 if ( nbDouble+nbSame == 2 )
4409 if(nbSame==0) { // ---> quadratic quadrangle
4410 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4411 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4413 else { //(nbSame==1) // ---> quadratic triangle
4415 return; // medium node on axis
4417 else if(sames[0]==0)
4418 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4419 prevNod[2], midlNod[1], nextNod[2] );
4421 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4422 prevNod[2], nextNod[2], midlNod[0]);
4425 else if ( nbDouble == 3 )
4427 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4428 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4429 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4436 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4437 if ( nbDouble > 0 ) break;
4439 if ( nbSame == 0 ) // ---> hexahedron
4440 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4441 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4443 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4444 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4445 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4446 nextNod[ iSameNode ]);
4447 newElems.push_back( aNewElem );
4448 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4449 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4450 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4452 else if ( nbSame == 2 ) { // ---> pentahedron
4453 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4454 // iBeforeSame is same too
4455 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4456 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4457 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4459 // iAfterSame is same too
4460 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4461 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4462 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4466 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4467 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4468 if ( nbDouble+nbSame != 3 ) break;
4470 // ---> pentahedron with 15 nodes
4471 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4472 nextNod[0], nextNod[1], nextNod[2],
4473 prevNod[3], prevNod[4], prevNod[5],
4474 nextNod[3], nextNod[4], nextNod[5],
4475 midlNod[0], midlNod[1], midlNod[2]);
4477 else if(nbSame==1) {
4478 // ---> 2d order pyramid of 13 nodes
4479 int apex = iSameNode;
4480 int i0 = ( apex + 1 ) % nbCorners;
4481 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4485 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4486 nextNod[i0], nextNod[i1], prevNod[apex],
4487 prevNod[i01], midlNod[i0],
4488 nextNod[i01], midlNod[i1],
4489 prevNod[i1a], prevNod[i0a],
4490 nextNod[i0a], nextNod[i1a]);
4492 else if(nbSame==2) {
4493 // ---> 2d order tetrahedron of 10 nodes
4494 int n1 = iNotSameNode;
4495 int n2 = ( n1 + 1 ) % nbCorners;
4496 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4500 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4501 prevNod[n12], prevNod[n23], prevNod[n31],
4502 midlNod[n1], nextNod[n12], nextNod[n31]);
4506 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4508 if ( nbDouble != 4 ) break;
4509 // ---> hexahedron with 20 nodes
4510 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4511 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4512 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4513 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4514 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4516 else if(nbSame==1) {
4517 // ---> pyramid + pentahedron - can not be created since it is needed
4518 // additional middle node at the center of face
4519 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4522 else if( nbSame == 2 ) {
4523 if ( nbDouble != 2 ) break;
4524 // ---> 2d order Pentahedron with 15 nodes
4526 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4527 // iBeforeSame is same too
4534 // iAfterSame is same too
4544 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4545 prevNod[n4], prevNod[n5], nextNod[n5],
4546 prevNod[n12], midlNod[n2], nextNod[n12],
4547 prevNod[n45], midlNod[n5], nextNod[n45],
4548 prevNod[n14], prevNod[n25], nextNod[n25]);
4552 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4554 if( nbSame == 0 && nbDouble == 9 ) {
4555 // ---> tri-quadratic hexahedron with 27 nodes
4556 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4557 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4558 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4559 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4560 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4561 prevNod[8], // bottom center
4562 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4563 nextNod[8], // top center
4564 midlNod[8]);// elem center
4572 case SMDSEntity_Polygon: { // sweep POLYGON
4574 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4575 // ---> hexagonal prism
4576 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4577 prevNod[3], prevNod[4], prevNod[5],
4578 nextNod[0], nextNod[1], nextNod[2],
4579 nextNod[3], nextNod[4], nextNod[5]);
4583 case SMDSEntity_Ball:
4588 } // switch ( baseType )
4591 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4593 if ( baseType != SMDSEntity_Polygon )
4595 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4596 SMDS_MeshCell::applyInterlace( ind, prevNod );
4597 SMDS_MeshCell::applyInterlace( ind, nextNod );
4598 SMDS_MeshCell::applyInterlace( ind, midlNod );
4599 SMDS_MeshCell::applyInterlace( ind, itNN );
4600 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4601 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4603 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4604 vector<int> quantities (nbNodes + 2);
4605 polyedre_nodes.clear();
4609 for (int inode = 0; inode < nbNodes; inode++)
4610 polyedre_nodes.push_back( prevNod[inode] );
4611 quantities.push_back( nbNodes );
4614 polyedre_nodes.push_back( nextNod[0] );
4615 for (int inode = nbNodes; inode-1; --inode )
4616 polyedre_nodes.push_back( nextNod[inode-1] );
4617 quantities.push_back( nbNodes );
4625 const int iQuad = elem->IsQuadratic();
4626 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4628 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4629 int inextface = (iface+1+iQuad) % nbNodes;
4630 int imid = (iface+1) % nbNodes;
4631 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4632 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4633 polyedre_nodes.push_back( prevNod[iface] ); // 1
4634 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4636 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4637 polyedre_nodes.push_back( nextNod[iface] ); // 2
4639 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4640 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4642 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4643 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4645 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4646 if ( nbFaceNodes > 2 )
4647 quantities.push_back( nbFaceNodes );
4648 else // degenerated face
4649 polyedre_nodes.resize( prevNbNodes );
4651 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4653 } // try to create a polyherdal prism
4656 newElems.push_back( aNewElem );
4657 myLastCreatedElems.push_back(aNewElem);
4658 srcElements.push_back( elem );
4661 // set new prev nodes
4662 for ( iNode = 0; iNode < nbNodes; iNode++ )
4663 prevNod[ iNode ] = nextNod[ iNode ];
4668 //=======================================================================
4670 * \brief Create 1D and 2D elements around swept elements
4671 * \param mapNewNodes - source nodes and ones generated from them
4672 * \param newElemsMap - source elements and ones generated from them
4673 * \param elemNewNodesMap - nodes generated from each node of each element
4674 * \param elemSet - all swept elements
4675 * \param nbSteps - number of sweeping steps
4676 * \param srcElements - to append elem for each generated element
4678 //=======================================================================
4680 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4681 TTElemOfElemListMap & newElemsMap,
4682 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4683 TIDSortedElemSet& elemSet,
4685 SMESH_SequenceOfElemPtr& srcElements)
4687 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4688 SMESHDS_Mesh* aMesh = GetMeshDS();
4690 // Find nodes belonging to only one initial element - sweep them into edges.
4692 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4693 for ( ; nList != mapNewNodes.end(); nList++ )
4695 const SMDS_MeshNode* node =
4696 static_cast<const SMDS_MeshNode*>( nList->first );
4697 if ( newElemsMap.count( node ))
4698 continue; // node was extruded into edge
4699 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4700 int nbInitElems = 0;
4701 const SMDS_MeshElement* el = 0;
4702 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4703 while ( eIt->more() && nbInitElems < 2 ) {
4704 const SMDS_MeshElement* e = eIt->next();
4705 SMDSAbs_ElementType type = e->GetType();
4706 if ( type == SMDSAbs_Volume ||
4710 if ( type > highType ) {
4717 if ( nbInitElems == 1 ) {
4718 bool NotCreateEdge = el && el->IsMediumNode(node);
4719 if(!NotCreateEdge) {
4720 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4721 list<const SMDS_MeshElement*> newEdges;
4722 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4727 // Make a ceiling for each element ie an equal element of last new nodes.
4728 // Find free links of faces - make edges and sweep them into faces.
4730 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4732 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4733 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4734 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4736 const SMDS_MeshElement* elem = itElem->first;
4737 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4739 if(itElem->second.size()==0) continue;
4741 const bool isQuadratic = elem->IsQuadratic();
4743 if ( elem->GetType() == SMDSAbs_Edge ) {
4744 // create a ceiling edge
4745 if ( !isQuadratic ) {
4746 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4747 vecNewNodes[ 1 ]->second.back())) {
4748 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4749 vecNewNodes[ 1 ]->second.back()));
4750 srcElements.push_back( elem );
4754 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4755 vecNewNodes[ 1 ]->second.back(),
4756 vecNewNodes[ 2 ]->second.back())) {
4757 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4758 vecNewNodes[ 1 ]->second.back(),
4759 vecNewNodes[ 2 ]->second.back()));
4760 srcElements.push_back( elem );
4764 if ( elem->GetType() != SMDSAbs_Face )
4767 bool hasFreeLinks = false;
4769 TIDSortedElemSet avoidSet;
4770 avoidSet.insert( elem );
4772 set<const SMDS_MeshNode*> aFaceLastNodes;
4773 int iNode, nbNodes = vecNewNodes.size();
4774 if ( !isQuadratic ) {
4775 // loop on the face nodes
4776 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4777 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4778 // look for free links of the face
4779 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4780 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4781 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4782 // check if a link n1-n2 is free
4783 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4784 hasFreeLinks = true;
4785 // make a new edge and a ceiling for a new edge
4786 const SMDS_MeshElement* edge;
4787 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4788 myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4789 srcElements.push_back( myLastCreatedElems.back() );
4791 n1 = vecNewNodes[ iNode ]->second.back();
4792 n2 = vecNewNodes[ iNext ]->second.back();
4793 if ( !aMesh->FindEdge( n1, n2 )) {
4794 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4795 srcElements.push_back( edge );
4800 else { // elem is quadratic face
4801 int nbn = nbNodes/2;
4802 for ( iNode = 0; iNode < nbn; iNode++ ) {
4803 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4804 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4805 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4806 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4807 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4808 // check if a link is free
4809 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4810 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4811 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4812 hasFreeLinks = true;
4813 // make an edge and a ceiling for a new edge
4815 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4816 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4817 srcElements.push_back( elem );
4819 n1 = vecNewNodes[ iNode ]->second.back();
4820 n2 = vecNewNodes[ iNext ]->second.back();
4821 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4822 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4823 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4824 srcElements.push_back( elem );
4828 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4829 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4833 // sweep free links into faces
4835 if ( hasFreeLinks ) {
4836 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4837 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4839 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4840 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4841 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4842 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4843 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4845 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4846 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4847 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4849 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4850 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4851 std::advance( v, volNb );
4852 // find indices of free faces of a volume and their source edges
4853 list< int > freeInd;
4854 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4855 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4856 int iF, nbF = vTool.NbFaces();
4857 for ( iF = 0; iF < nbF; iF ++ ) {
4858 if ( vTool.IsFreeFace( iF ) &&
4859 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4860 initNodeSet != faceNodeSet) // except an initial face
4862 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4864 if ( faceNodeSet == initNodeSetNoCenter )
4866 freeInd.push_back( iF );
4867 // find source edge of a free face iF
4868 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4869 vector<const SMDS_MeshNode*>::iterator lastCommom;
4870 commonNodes.resize( nbNodes, 0 );
4871 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4872 initNodeSet.begin(), initNodeSet.end(),
4873 commonNodes.begin());
4874 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4875 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4877 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4879 if ( !srcEdges.back() )
4881 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4882 << iF << " of volume #" << vTool.ID() << endl;
4887 if ( freeInd.empty() )
4890 // create wall faces for all steps;
4891 // if such a face has been already created by sweep of edge,
4892 // assure that its orientation is OK
4893 for ( int iStep = 0; iStep < nbSteps; iStep++ )
4895 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4896 vTool.SetExternalNormal();
4897 const int nextShift = vTool.IsForward() ? +1 : -1;
4898 list< int >::iterator ind = freeInd.begin();
4899 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4900 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4902 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4903 int nbn = vTool.NbFaceNodes( *ind );
4904 const SMDS_MeshElement * f = 0;
4905 if ( nbn == 3 ) ///// triangle
4907 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4909 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4911 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4913 nodes[ 1 + nextShift ] };
4915 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4917 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4921 else if ( nbn == 4 ) ///// quadrangle
4923 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4925 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4927 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4928 nodes[ 2 ], nodes[ 2+nextShift ] };
4930 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4932 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4933 newOrder[ 2 ], newOrder[ 3 ]));
4936 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4938 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4940 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4942 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4944 nodes[2 + 2*nextShift],
4945 nodes[3 - 2*nextShift],
4947 nodes[3 + 2*nextShift]};
4949 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4951 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4959 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4961 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4962 nodes[1], nodes[3], nodes[5], nodes[7] );
4964 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4966 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4967 nodes[4 - 2*nextShift],
4969 nodes[4 + 2*nextShift],
4971 nodes[5 - 2*nextShift],
4973 nodes[5 + 2*nextShift] };
4975 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4977 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4978 newOrder[ 2 ], newOrder[ 3 ],
4979 newOrder[ 4 ], newOrder[ 5 ],
4980 newOrder[ 6 ], newOrder[ 7 ]));
4983 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4985 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4986 SMDSAbs_Face, /*noMedium=*/false);
4988 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4990 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4991 nodes[4 - 2*nextShift],
4993 nodes[4 + 2*nextShift],
4995 nodes[5 - 2*nextShift],
4997 nodes[5 + 2*nextShift],
5000 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5002 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5003 newOrder[ 2 ], newOrder[ 3 ],
5004 newOrder[ 4 ], newOrder[ 5 ],
5005 newOrder[ 6 ], newOrder[ 7 ],
5009 else //////// polygon
5011 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5012 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5014 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5016 if ( !vTool.IsForward() )
5017 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5019 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5021 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5025 while ( srcElements.size() < myLastCreatedElems.size() )
5026 srcElements.push_back( *srcEdge );
5028 } // loop on free faces
5030 // go to the next volume
5032 while ( iVol++ < nbVolumesByStep ) v++;
5035 } // loop on volumes of one step
5036 } // sweep free links into faces
5038 // Make a ceiling face with a normal external to a volume
5040 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5041 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5042 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5044 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5045 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5046 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5050 lastVol.SetExternalNormal();
5051 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5052 const int nbn = lastVol.NbFaceNodes( iF );
5053 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5054 if ( !hasFreeLinks ||
5055 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5057 const vector<int>& interlace =
5058 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5059 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5061 AddElement( nodeVec, anyFace.Init( elem ));
5063 while ( srcElements.size() < myLastCreatedElems.size() )
5064 srcElements.push_back( elem );
5067 } // loop on swept elements
5070 //=======================================================================
5071 //function : RotationSweep
5073 //=======================================================================
5075 SMESH_MeshEditor::PGroupIDs
5076 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5077 const gp_Ax1& theAxis,
5078 const double theAngle,
5079 const int theNbSteps,
5080 const double theTol,
5081 const bool theMakeGroups,
5082 const bool theMakeWalls)
5086 setElemsFirst( theElemSets );
5087 myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5088 myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5090 // source elements for each generated one
5091 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5092 srcElems.reserve( theElemSets[0].size() );
5093 srcNodes.reserve( theElemSets[1].size() );
5096 aTrsf.SetRotation( theAxis, theAngle );
5098 aTrsf2.SetRotation( theAxis, theAngle/2. );
5100 gp_Lin aLine( theAxis );
5101 double aSqTol = theTol * theTol;
5103 SMESHDS_Mesh* aMesh = GetMeshDS();
5105 TNodeOfNodeListMap mapNewNodes;
5106 TElemOfVecOfNnlmiMap mapElemNewNodes;
5107 TTElemOfElemListMap newElemsMap;
5109 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5110 myMesh->NbFaces(ORDER_QUADRATIC) +
5111 myMesh->NbVolumes(ORDER_QUADRATIC) );
5112 // loop on theElemSets
5113 TIDSortedElemSet::iterator itElem;
5114 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5116 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5117 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5118 const SMDS_MeshElement* elem = *itElem;
5119 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5121 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5122 newNodesItVec.reserve( elem->NbNodes() );
5124 // loop on elem nodes
5125 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5126 while ( itN->more() )
5128 const SMDS_MeshNode* node = cast2Node( itN->next() );
5130 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5132 aXYZ.Coord( coord[0], coord[1], coord[2] );
5133 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5135 // check if a node has been already sweeped
5136 TNodeOfNodeListMapItr nIt =
5137 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5138 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5139 if ( listNewNodes.empty() )
5141 // check if we are to create medium nodes between corner ones
5142 bool needMediumNodes = false;
5143 if ( isQuadraticMesh )
5145 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5146 while (it->more() && !needMediumNodes )
5148 const SMDS_MeshElement* invElem = it->next();
5149 if ( invElem != elem && !theElems.count( invElem )) continue;
5150 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5151 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5152 needMediumNodes = true;
5157 const SMDS_MeshNode * newNode = node;
5158 for ( int i = 0; i < theNbSteps; i++ ) {
5160 if ( needMediumNodes ) // create a medium node
5162 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5163 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5164 myLastCreatedNodes.push_back(newNode);
5165 srcNodes.push_back( node );
5166 listNewNodes.push_back( newNode );
5167 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5170 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5172 // create a corner node
5173 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5174 myLastCreatedNodes.push_back(newNode);
5175 srcNodes.push_back( node );
5176 listNewNodes.push_back( newNode );
5179 listNewNodes.push_back( newNode );
5180 // if ( needMediumNodes )
5181 // listNewNodes.push_back( newNode );
5185 newNodesItVec.push_back( nIt );
5187 // make new elements
5188 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5193 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5195 PGroupIDs newGroupIDs;
5196 if ( theMakeGroups )
5197 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5202 //=======================================================================
5203 //function : ExtrusParam
5204 //purpose : standard construction
5205 //=======================================================================
5207 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5208 const int theNbSteps,
5209 const std::list<double>& theScales,
5210 const std::list<double>& theAngles,
5211 const gp_XYZ* theBasePoint,
5213 const double theTolerance):
5215 myBaseP( Precision::Infinite(), 0, 0 ),
5216 myFlags( theFlags ),
5217 myTolerance( theTolerance ),
5218 myElemsToUse( NULL )
5220 mySteps = new TColStd_HSequenceOfReal;
5221 const double stepSize = theStep.Magnitude();
5222 for (int i=1; i<=theNbSteps; i++ )
5223 mySteps->Append( stepSize );
5225 if ( !theScales.empty() )
5227 if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5228 linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5230 // add medium scales
5231 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5232 myScales.reserve( theNbSteps * 2 );
5233 myScales.push_back( 0.5 * ( *s1 + 1. ));
5234 myScales.push_back( *s1 );
5235 for ( ; s2 != theScales.end(); s1 = s2++ )
5237 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5238 myScales.push_back( *s2 );
5242 if ( !theAngles.empty() )
5244 std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5245 if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5246 linearAngleVariation( theNbSteps, angles );
5248 // accumulate angles
5251 std::list<double>::iterator a1 = angles.begin(), a2;
5252 for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5257 while ( nbAngles++ < theNbSteps )
5258 angles.push_back( angles.back() );
5260 // add medium angles
5261 a2 = angles.begin(), a1 = a2++;
5262 myAngles.push_back( 0.5 * *a1 );
5263 myAngles.push_back( *a1 );
5264 for ( ; a2 != angles.end(); a1 = a2++ )
5266 myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5267 myAngles.push_back( *a2 );
5273 myBaseP = *theBasePoint;
5276 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5277 ( theTolerance > 0 ))
5279 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5283 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5287 //=======================================================================
5288 //function : ExtrusParam
5289 //purpose : steps are given explicitly
5290 //=======================================================================
5292 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5293 Handle(TColStd_HSequenceOfReal) theSteps,
5295 const double theTolerance):
5297 mySteps( theSteps ),
5298 myFlags( theFlags ),
5299 myTolerance( theTolerance ),
5300 myElemsToUse( NULL )
5302 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5303 ( theTolerance > 0 ))
5305 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5309 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5313 //=======================================================================
5314 //function : ExtrusParam
5315 //purpose : for extrusion by normal
5316 //=======================================================================
5318 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5319 const int theNbSteps,
5323 mySteps( new TColStd_HSequenceOfReal ),
5324 myFlags( theFlags ),
5326 myElemsToUse( NULL )
5328 for (int i = 0; i < theNbSteps; i++ )
5329 mySteps->Append( theStepSize );
5333 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5337 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5341 //=======================================================================
5342 //function : ExtrusParam
5343 //purpose : for extrusion along path
5344 //=======================================================================
5346 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5347 const gp_Pnt* theBasePoint,
5348 const std::list<double>& theScales,
5349 const bool theMakeGroups )
5350 : myBaseP( Precision::Infinite(), 0, 0 ),
5351 myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5352 myPathPoints( thePoints )
5356 myBaseP = theBasePoint->XYZ();
5359 if ( !theScales.empty() )
5361 // add medium scales
5362 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5363 myScales.reserve( thePoints.size() * 2 );
5364 myScales.push_back( 0.5 * ( 1. + *s1 ));
5365 myScales.push_back( *s1 );
5366 for ( ; s2 != theScales.end(); s1 = s2++ )
5368 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5369 myScales.push_back( *s2 );
5373 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5376 //=======================================================================
5377 //function : ExtrusParam::SetElementsToUse
5378 //purpose : stores elements to use for extrusion by normal, depending on
5379 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5380 // define myBaseP for scaling
5381 //=======================================================================
5383 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5384 const TIDSortedElemSet& nodes )
5386 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5388 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5390 myBaseP.SetCoord( 0.,0.,0. );
5391 TIDSortedElemSet newNodes;
5393 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5394 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5396 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5397 TIDSortedElemSet::const_iterator itElem = elements.begin();
5398 for ( ; itElem != elements.end(); itElem++ )
5400 const SMDS_MeshElement* elem = *itElem;
5401 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5402 while ( itN->more() ) {
5403 const SMDS_MeshElement* node = itN->next();
5404 if ( newNodes.insert( node ).second )
5405 myBaseP += SMESH_NodeXYZ( node );
5409 myBaseP /= newNodes.size();
5413 //=======================================================================
5414 //function : ExtrusParam::beginStepIter
5415 //purpose : prepare iteration on steps
5416 //=======================================================================
5418 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5420 myWithMediumNodes = withMediumNodes;
5424 //=======================================================================
5425 //function : ExtrusParam::moreSteps
5426 //purpose : are there more steps?
5427 //=======================================================================
5429 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5431 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5433 //=======================================================================
5434 //function : ExtrusParam::nextStep
5435 //purpose : returns the next step
5436 //=======================================================================
5438 double SMESH_MeshEditor::ExtrusParam::nextStep()
5441 if ( !myCurSteps.empty() )
5443 res = myCurSteps.back();
5444 myCurSteps.pop_back();
5446 else if ( myNextStep <= mySteps->Length() )
5448 myCurSteps.push_back( mySteps->Value( myNextStep ));
5450 if ( myWithMediumNodes )
5452 myCurSteps.back() /= 2.;
5453 myCurSteps.push_back( myCurSteps.back() );
5460 //=======================================================================
5461 //function : ExtrusParam::makeNodesByDir
5462 //purpose : create nodes for standard extrusion
5463 //=======================================================================
5465 int SMESH_MeshEditor::ExtrusParam::
5466 makeNodesByDir( SMESHDS_Mesh* mesh,
5467 const SMDS_MeshNode* srcNode,
5468 std::list<const SMDS_MeshNode*> & newNodes,
5469 const bool makeMediumNodes)
5471 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5474 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5476 p += myDir.XYZ() * nextStep();
5477 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5478 newNodes.push_back( newNode );
5481 if ( !myScales.empty() || !myAngles.empty() )
5483 gp_XYZ center = myBaseP;
5484 gp_Ax1 ratationAxis( center, myDir );
5487 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5488 size_t i = !makeMediumNodes;
5489 for ( beginStepIter( makeMediumNodes );
5491 ++nIt, i += 1 + !makeMediumNodes )
5493 center += myDir.XYZ() * nextStep();
5495 gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5497 if ( i < myScales.size() )
5499 xyz = ( myScales[i] * ( xyz - center )) + center;
5502 if ( !myAngles.empty() )
5504 rotation.SetRotation( ratationAxis, myAngles[i] );
5505 rotation.Transforms( xyz );
5509 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5517 //=======================================================================
5518 //function : ExtrusParam::makeNodesByDirAndSew
5519 //purpose : create nodes for standard extrusion with sewing
5520 //=======================================================================
5522 int SMESH_MeshEditor::ExtrusParam::
5523 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5524 const SMDS_MeshNode* srcNode,
5525 std::list<const SMDS_MeshNode*> & newNodes,
5526 const bool makeMediumNodes)
5528 gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5531 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5533 P1 += myDir.XYZ() * nextStep();
5535 // try to search in sequence of existing nodes
5536 // if myNodes.size()>0 we 'nave to use given sequence
5537 // else - use all nodes of mesh
5538 const SMDS_MeshNode * node = 0;
5539 if ( myNodes.Length() > 0 )
5541 for ( int i = 1; i <= myNodes.Length(); i++ )
5543 SMESH_NodeXYZ P2 = myNodes.Value(i);
5544 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5546 node = myNodes.Value(i);
5553 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5556 SMESH_NodeXYZ P2 = itn->next();
5557 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5566 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5568 newNodes.push_back( node );
5575 //=======================================================================
5576 //function : ExtrusParam::makeNodesByNormal2D
5577 //purpose : create nodes for extrusion using normals of faces
5578 //=======================================================================
5580 int SMESH_MeshEditor::ExtrusParam::
5581 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5582 const SMDS_MeshNode* srcNode,
5583 std::list<const SMDS_MeshNode*> & newNodes,
5584 const bool makeMediumNodes)
5586 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5588 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5590 // get normals to faces sharing srcNode
5591 vector< gp_XYZ > norms, baryCenters;
5592 gp_XYZ norm, avgNorm( 0,0,0 );
5593 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5594 while ( faceIt->more() )
5596 const SMDS_MeshElement* face = faceIt->next();
5597 if ( myElemsToUse && !myElemsToUse->count( face ))
5599 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5601 norms.push_back( norm );
5603 if ( !alongAvgNorm )
5607 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5608 bc += SMESH_NodeXYZ( nIt->next() );
5609 baryCenters.push_back( bc / nbN );
5614 if ( norms.empty() ) return 0;
5616 double normSize = avgNorm.Modulus();
5617 if ( normSize < std::numeric_limits<double>::min() )
5620 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5623 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5626 avgNorm /= normSize;
5629 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5632 double stepSize = nextStep();
5634 if ( norms.size() > 1 )
5636 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5638 // translate plane of a face
5639 baryCenters[ iF ] += norms[ iF ] * stepSize;
5641 // find point of intersection of the face plane located at baryCenters[ iF ]
5642 // and avgNorm located at pNew
5643 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5644 double dot = ( norms[ iF ] * avgNorm );
5645 if ( dot < std::numeric_limits<double>::min() )
5646 dot = stepSize * 1e-3;
5647 double step = -( norms[ iF ] * pNew + d ) / dot;
5648 pNew += step * avgNorm;
5653 pNew += stepSize * avgNorm;
5657 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5658 newNodes.push_back( newNode );
5663 //=======================================================================
5664 //function : ExtrusParam::makeNodesByNormal1D
5665 //purpose : create nodes for extrusion using normals of edges
5666 //=======================================================================
5668 int SMESH_MeshEditor::ExtrusParam::
5669 makeNodesByNormal1D( SMESHDS_Mesh* /*mesh*/,
5670 const SMDS_MeshNode* /*srcNode*/,
5671 std::list<const SMDS_MeshNode*> & /*newNodes*/,
5672 const bool /*makeMediumNodes*/)
5674 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5678 //=======================================================================
5679 //function : ExtrusParam::makeNodesAlongTrack
5680 //purpose : create nodes for extrusion along path
5681 //=======================================================================
5683 int SMESH_MeshEditor::ExtrusParam::
5684 makeNodesAlongTrack( SMESHDS_Mesh* mesh,
5685 const SMDS_MeshNode* srcNode,
5686 std::list<const SMDS_MeshNode*> & newNodes,
5687 const bool makeMediumNodes)
5689 const Standard_Real aTolAng=1.e-4;
5691 gp_Pnt aV0x = myBaseP;
5692 gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
5694 const PathPoint& aPP0 = myPathPoints[0];
5695 gp_Pnt aP0x = aPP0.myPnt;
5696 gp_Dir aDT0x= aPP0.myTgt;
5698 std::vector< gp_Pnt > centers;
5699 centers.reserve( NbSteps() * 2 );
5701 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5703 for ( size_t j = 1; j < myPathPoints.size(); ++j )
5705 const PathPoint& aPP = myPathPoints[j];
5706 const gp_Pnt& aP1x = aPP.myPnt;
5707 const gp_Dir& aDT1x = aPP.myTgt;
5710 gp_Vec aV01x( aP0x, aP1x );
5711 aTrsf.SetTranslation( aV01x );
5712 gp_Pnt aV1x = aV0x.Transformed( aTrsf );
5713 gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
5715 // rotation 1 [ T1,T0 ]
5716 Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
5717 if ( fabs( aAngleT1T0 ) > aTolAng )
5719 gp_Dir aDT1T0 = aDT1x ^ aDT0x;
5720 aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
5722 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5726 if ( aPP.myAngle != 0. )
5728 aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
5729 aPN1 = aPN1.Transformed( aTrsfRot );
5733 if ( makeMediumNodes )
5735 // create additional node
5736 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
5737 const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
5738 newNodes.push_back( newNode );
5741 const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5742 newNodes.push_back( newNode );
5744 centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
5745 centers.push_back( aV1x );
5754 if ( !myScales.empty() )
5757 std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
5758 for ( size_t i = !makeMediumNodes;
5759 i < myScales.size() && node != newNodes.end();
5760 i += ( 1 + !makeMediumNodes ), ++node )
5762 aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
5763 gp_Pnt aN = SMESH_NodeXYZ( *node );
5764 gp_Pnt aP = aN.Transformed( aTrsfScale );
5765 mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
5769 return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
5772 //=======================================================================
5773 //function : ExtrusionSweep
5775 //=======================================================================
5777 SMESH_MeshEditor::PGroupIDs
5778 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5779 const gp_Vec& theStep,
5780 const int theNbSteps,
5781 TTElemOfElemListMap& newElemsMap,
5783 const double theTolerance)
5785 std::list<double> dummy;
5786 ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
5787 theFlags, theTolerance );
5788 return ExtrusionSweep( theElems, aParams, newElemsMap );
5794 //=======================================================================
5795 //function : getOriFactor
5796 //purpose : Return -1 or 1 depending on if order of given nodes corresponds to
5797 // edge curve orientation
5798 //=======================================================================
5800 double getOriFactor( const TopoDS_Edge& edge,
5801 const SMDS_MeshNode* n1,
5802 const SMDS_MeshNode* n2,
5803 SMESH_MesherHelper& helper)
5805 double u1 = helper.GetNodeU( edge, n1, n2 );
5806 double u2 = helper.GetNodeU( edge, n2, n1 );
5807 return u1 < u2 ? 1. : -1.;
5811 //=======================================================================
5812 //function : ExtrusionSweep
5814 //=======================================================================
5816 SMESH_MeshEditor::PGroupIDs
5817 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5818 ExtrusParam& theParams,
5819 TTElemOfElemListMap& newElemsMap)
5823 setElemsFirst( theElemSets );
5824 myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5825 myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5827 // source elements for each generated one
5828 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5829 srcElems.reserve( theElemSets[0].size() );
5830 srcNodes.reserve( theElemSets[1].size() );
5832 const int nbSteps = theParams.NbSteps();
5833 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5835 TNodeOfNodeListMap mapNewNodes;
5836 TElemOfVecOfNnlmiMap mapElemNewNodes;
5838 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5839 myMesh->NbFaces(ORDER_QUADRATIC) +
5840 myMesh->NbVolumes(ORDER_QUADRATIC) );
5842 TIDSortedElemSet::iterator itElem;
5843 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5845 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5846 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5848 // check element type
5849 const SMDS_MeshElement* elem = *itElem;
5850 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5853 const size_t nbNodes = elem->NbNodes();
5854 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5855 newNodesItVec.reserve( nbNodes );
5857 // loop on elem nodes
5858 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
5859 while ( itN->more() )
5861 // check if a node has been already sweeped
5862 const SMDS_MeshNode* node = itN->next();
5863 TNodeOfNodeListMap::iterator nIt =
5864 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5865 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5866 if ( listNewNodes.empty() )
5870 // check if we are to create medium nodes between corner ones
5871 bool needMediumNodes = false;
5872 if ( isQuadraticMesh )
5874 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5875 while (it->more() && !needMediumNodes )
5877 const SMDS_MeshElement* invElem = it->next();
5878 if ( invElem != elem && !theElems.count( invElem )) continue;
5879 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5880 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5881 needMediumNodes = true;
5884 // create nodes for all steps
5885 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5887 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5888 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5890 myLastCreatedNodes.push_back( *newNodesIt );
5891 srcNodes.push_back( node );
5896 if ( theParams.ToMakeBoundary() )
5898 GetMeshDS()->Modified();
5899 throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
5901 break; // newNodesItVec will be shorter than nbNodes
5904 newNodesItVec.push_back( nIt );
5906 // make new elements
5907 if ( newNodesItVec.size() == nbNodes )
5908 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5912 if ( theParams.ToMakeBoundary() ) {
5913 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5915 PGroupIDs newGroupIDs;
5916 if ( theParams.ToMakeGroups() )
5917 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5922 //=======================================================================
5923 //function : ExtrusionAlongTrack
5925 //=======================================================================
5926 SMESH_MeshEditor::Extrusion_Error
5927 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5928 SMESH_Mesh* theTrackMesh,
5929 SMDS_ElemIteratorPtr theTrackIterator,
5930 const SMDS_MeshNode* theN1,
5931 std::list<double>& theAngles,
5932 const bool theAngleVariation,
5933 std::list<double>& theScales,
5934 const bool theScaleVariation,
5935 const gp_Pnt* theRefPoint,
5936 const bool theMakeGroups)
5941 if ( theElements[0].empty() && theElements[1].empty() )
5942 return EXTR_NO_ELEMENTS;
5944 ASSERT( theTrackMesh );
5945 if ( ! theTrackIterator || !theTrackIterator->more() )
5946 return EXTR_NO_ELEMENTS;
5948 // 2. Get ordered nodes
5949 SMESH_MeshAlgos::TElemGroupVector branchEdges;
5950 SMESH_MeshAlgos::TNodeGroupVector branchNods;
5951 SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
5952 if ( branchEdges.empty() )
5953 return EXTR_PATH_NOT_EDGE;
5955 if ( branchEdges.size() > 1 )
5956 return EXTR_BAD_PATH_SHAPE;
5958 std::vector< const SMDS_MeshNode* >& pathNodes = branchNods[0];
5959 std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
5960 if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
5961 return EXTR_BAD_STARTING_NODE;
5963 if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
5965 // add medium nodes to pathNodes
5966 std::vector< const SMDS_MeshNode* > pathNodes2;
5967 std::vector< const SMDS_MeshElement* > pathEdges2;
5968 pathNodes2.reserve( pathNodes.size() * 2 );
5969 pathEdges2.reserve( pathEdges.size() * 2 );
5970 for ( size_t i = 0; i < pathEdges.size(); ++i )
5972 pathNodes2.push_back( pathNodes[i] );
5973 pathEdges2.push_back( pathEdges[i] );
5974 if ( pathEdges[i]->IsQuadratic() )
5976 pathNodes2.push_back( pathEdges[i]->GetNode(2) );
5977 pathEdges2.push_back( pathEdges[i] );
5980 pathNodes2.push_back( pathNodes.back() );
5981 pathEdges.swap( pathEdges2 );
5982 pathNodes.swap( pathNodes2 );
5985 // 3. Get path data at pathNodes
5987 std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
5989 if ( theAngleVariation )
5990 linearAngleVariation( points.size()-1, theAngles );
5991 if ( theScaleVariation )
5992 linearScaleVariation( points.size()-1, theScales );
5994 theAngles.push_front( 0 ); // for the 1st point that is not transformed
5995 std::list<double>::iterator angle = theAngles.begin();
5997 SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
5999 std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
6000 std::map< int, double >::iterator id2factor;
6001 SMESH_MesherHelper pathHelper( *theTrackMesh );
6002 gp_Pnt p; gp_Vec tangent;
6003 const double tol2 = gp::Resolution() * gp::Resolution();
6005 for ( size_t i = 0; i < pathNodes.size(); ++i )
6007 ExtrusParam::PathPoint & point = points[ i ];
6009 point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
6011 if ( angle != theAngles.end() )
6012 point.myAngle = *angle++;
6014 tangent.SetCoord( 0,0,0 );
6015 const int shapeID = pathNodes[ i ]->GetShapeID();
6016 const TopoDS_Shape& shape = pathMeshDS->IndexToShape( shapeID );
6017 TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
6018 switch ( shapeType )
6022 TopoDS_Edge edge = TopoDS::Edge( shape );
6023 id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
6024 if ( id2factor->second == 0 )
6026 if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6027 else id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6029 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6030 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6031 curve->D1( u, p, tangent );
6032 tangent *= id2factor->second;
6038 PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6039 while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6041 int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6042 for ( int di = -1; di <= 0; ++di )
6045 if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6047 TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6048 id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6049 if ( id2factor->second == 0 )
6052 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6054 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6056 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6057 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6059 curve->D1( u, p, du );
6060 double size2 = du.SquareMagnitude();
6061 if ( du.SquareMagnitude() > tol2 )
6063 tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6076 for ( int di = -1; di <= 1; di += 2 )
6079 if ( j < pathNodes.size() )
6081 gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6082 double size2 = dir.SquareMagnitude();
6084 tangent += dir.Divided( Sqrt( size2 )) * di;
6088 } // switch ( shapeType )
6090 if ( tangent.SquareMagnitude() < tol2 )
6091 return EXTR_CANT_GET_TANGENT;
6093 point.myTgt = tangent;
6095 } // loop on pathNodes
6098 ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6099 TTElemOfElemListMap newElemsMap;
6101 ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6106 //=======================================================================
6107 //function : linearAngleVariation
6108 //purpose : spread values over nbSteps
6109 //=======================================================================
6111 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6112 list<double>& Angles)
6114 int nbAngles = Angles.size();
6115 if( nbSteps > nbAngles && nbAngles > 0 )
6117 vector<double> theAngles(nbAngles);
6118 theAngles.assign( Angles.begin(), Angles.end() );
6121 double rAn2St = double( nbAngles ) / double( nbSteps );
6122 double angPrev = 0, angle;
6123 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6125 double angCur = rAn2St * ( iSt+1 );
6126 double angCurFloor = floor( angCur );
6127 double angPrevFloor = floor( angPrev );
6128 if ( angPrevFloor == angCurFloor )
6129 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6131 int iP = int( angPrevFloor );
6132 double angPrevCeil = ceil(angPrev);
6133 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6135 int iC = int( angCurFloor );
6136 if ( iC < nbAngles )
6137 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6139 iP = int( angPrevCeil );
6141 angle += theAngles[ iC ];
6143 res.push_back(angle);
6150 //=======================================================================
6151 //function : linearScaleVariation
6152 //purpose : spread values over nbSteps
6153 //=======================================================================
6155 void SMESH_MeshEditor::linearScaleVariation(const int theNbSteps,
6156 std::list<double>& theScales)
6158 int nbScales = theScales.size();
6159 std::vector<double> myScales;
6160 myScales.reserve( theNbSteps );
6161 std::list<double>::const_iterator scale = theScales.begin();
6162 double prevScale = 1.0;
6163 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6165 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6166 int stDelta = Max( 1, iStep - myScales.size());
6167 double scDelta = ( *scale - prevScale ) / stDelta;
6168 for ( int iStep = 0; iStep < stDelta; ++iStep )
6170 myScales.push_back( prevScale + scDelta );
6171 prevScale = myScales.back();
6175 theScales.assign( myScales.begin(), myScales.end() );
6178 //================================================================================
6180 * \brief Move or copy theElements applying theTrsf to their nodes
6181 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6182 * \param theTrsf - transformation to apply
6183 * \param theCopy - if true, create translated copies of theElems
6184 * \param theMakeGroups - if true and theCopy, create translated groups
6185 * \param theTargetMesh - mesh to copy translated elements into
6186 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6188 //================================================================================
6190 SMESH_MeshEditor::PGroupIDs
6191 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6192 const gp_Trsf& theTrsf,
6194 const bool theMakeGroups,
6195 SMESH_Mesh* theTargetMesh)
6198 myLastCreatedElems.reserve( theElems.size() );
6200 bool needReverse = false;
6201 string groupPostfix;
6202 switch ( theTrsf.Form() ) {
6205 groupPostfix = "mirrored";
6208 groupPostfix = "mirrored";
6212 groupPostfix = "mirrored";
6215 groupPostfix = "rotated";
6217 case gp_Translation:
6218 groupPostfix = "translated";
6221 groupPostfix = "scaled";
6223 case gp_CompoundTrsf: // different scale by axis
6224 groupPostfix = "scaled";
6227 needReverse = false;
6228 groupPostfix = "transformed";
6231 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6232 SMESHDS_Mesh* aMesh = GetMeshDS();
6234 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6235 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6236 SMESH_MeshEditor::ElemFeatures elemType;
6238 // map old node to new one
6239 TNodeNodeMap nodeMap;
6241 // elements sharing moved nodes; those of them which have all
6242 // nodes mirrored but are not in theElems are to be reversed
6243 TIDSortedElemSet inverseElemSet;
6245 // source elements for each generated one
6246 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6248 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6249 TIDSortedElemSet orphanNode;
6251 if ( theElems.empty() ) // transform the whole mesh
6254 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6255 while ( eIt->more() ) theElems.insert( eIt->next() );
6257 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6258 while ( nIt->more() )
6260 const SMDS_MeshNode* node = nIt->next();
6261 if ( node->NbInverseElements() == 0)
6262 orphanNode.insert( node );
6266 // loop on elements to transform nodes : first orphan nodes then elems
6267 TIDSortedElemSet::iterator itElem;
6268 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6269 for (int i=0; i<2; i++)
6270 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6272 const SMDS_MeshElement* elem = *itElem;
6276 // loop on elem nodes
6278 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6279 while ( itN->more() )
6281 const SMDS_MeshNode* node = cast2Node( itN->next() );
6282 // check if a node has been already transformed
6283 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6284 nodeMap.insert( make_pair ( node, node ));
6285 if ( !n2n_isnew.second )
6288 node->GetXYZ( coord );
6289 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6290 if ( theTargetMesh ) {
6291 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6292 n2n_isnew.first->second = newNode;
6293 myLastCreatedNodes.push_back(newNode);
6294 srcNodes.push_back( node );
6296 else if ( theCopy ) {
6297 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6298 n2n_isnew.first->second = newNode;
6299 myLastCreatedNodes.push_back(newNode);
6300 srcNodes.push_back( node );
6303 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6304 // node position on shape becomes invalid
6305 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6306 ( SMDS_SpacePosition::originSpacePosition() );
6309 // keep inverse elements
6310 if ( !theCopy && !theTargetMesh && needReverse ) {
6311 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6312 while ( invElemIt->more() ) {
6313 const SMDS_MeshElement* iel = invElemIt->next();
6314 inverseElemSet.insert( iel );
6318 } // loop on elems in { &orphanNode, &theElems };
6320 // either create new elements or reverse mirrored ones
6321 if ( !theCopy && !needReverse && !theTargetMesh )
6324 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6326 // Replicate or reverse elements
6328 std::vector<int> iForw;
6329 vector<const SMDS_MeshNode*> nodes;
6330 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6332 const SMDS_MeshElement* elem = *itElem;
6333 if ( !elem ) continue;
6335 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6336 size_t nbNodes = elem->NbNodes();
6337 if ( geomType == SMDSGeom_NONE ) continue; // node
6339 nodes.resize( nbNodes );
6341 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6343 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6347 bool allTransformed = true;
6348 int nbFaces = aPolyedre->NbFaces();
6349 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6351 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6352 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6354 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6355 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6356 if ( nodeMapIt == nodeMap.end() )
6357 allTransformed = false; // not all nodes transformed
6359 nodes.push_back((*nodeMapIt).second);
6361 if ( needReverse && allTransformed )
6362 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6364 if ( !allTransformed )
6365 continue; // not all nodes transformed
6367 else // ----------------------- the rest element types
6369 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6370 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6371 const vector<int>& i = needReverse ? iRev : iForw;
6373 // find transformed nodes
6375 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6376 while ( itN->more() ) {
6377 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6378 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6379 if ( nodeMapIt == nodeMap.end() )
6380 break; // not all nodes transformed
6381 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6383 if ( iNode != nbNodes )
6384 continue; // not all nodes transformed
6388 // copy in this or a new mesh
6389 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6390 srcElems.push_back( elem );
6393 // reverse element as it was reversed by transformation
6395 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6398 } // loop on elements
6400 if ( editor && editor != this )
6401 myLastCreatedElems.swap( editor->myLastCreatedElems );
6403 PGroupIDs newGroupIDs;
6405 if ( ( theMakeGroups && theCopy ) ||
6406 ( theMakeGroups && theTargetMesh ) )
6407 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6412 //================================================================================
6414 * \brief Make an offset mesh from a source 2D mesh
6415 * \param [in] theElements - source faces
6416 * \param [in] theValue - offset value
6417 * \param [out] theTgtMesh - a mesh to add offset elements to
6418 * \param [in] theMakeGroups - to generate groups
6419 * \return PGroupIDs - IDs of created groups. NULL means failure
6421 //================================================================================
6423 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6424 const double theValue,
6425 SMESH_Mesh* theTgtMesh,
6426 const bool theMakeGroups,
6427 const bool theCopyElements,
6428 const bool theFixSelfIntersection)
6430 SMESHDS_Mesh* meshDS = GetMeshDS();
6431 SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6432 SMESH_MeshEditor tgtEditor( theTgtMesh );
6434 SMDS_ElemIteratorPtr eIt;
6435 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6436 else eIt = SMESHUtils::elemSetIterator( theElements );
6438 SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6439 SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6440 std::unique_ptr< SMDS_Mesh > offsetMesh
6441 ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6442 theFixSelfIntersection,
6443 new2OldFaces, new2OldNodes ));
6444 if ( offsetMesh->NbElements() == 0 )
6445 return PGroupIDs(); // MakeOffset() failed
6448 if ( theTgtMesh == myMesh && !theCopyElements )
6450 // clear the source elements
6451 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6452 else eIt = SMESHUtils::elemSetIterator( theElements );
6453 while ( eIt->more() )
6454 meshDS->RemoveFreeElement( eIt->next(), 0 );
6457 // offsetMesh->Modified();
6458 // offsetMesh->CompactMesh(); // make IDs start from 1
6460 // source elements for each generated one
6461 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6462 srcElems.reserve( new2OldFaces.size() );
6463 srcNodes.reserve( new2OldNodes.size() );
6466 myLastCreatedElems.reserve( new2OldFaces.size() );
6467 myLastCreatedNodes.reserve( new2OldNodes.size() );
6469 // copy offsetMesh to theTgtMesh
6471 int idShift = meshDS->MaxNodeID();
6472 for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6473 if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6476 if ( n->NbInverseElements() > 0 )
6479 const SMDS_MeshNode* n2 =
6480 tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6481 myLastCreatedNodes.push_back( n2 );
6482 srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6486 ElemFeatures elemType;
6487 for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6488 if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6491 elemType.myNodes.clear();
6492 for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6494 const SMDS_MeshNode* n2 = nIt->next();
6495 elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6497 tgtEditor.AddElement( elemType.myNodes, elemType );
6498 srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6501 myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6503 PGroupIDs newGroupIDs;
6504 if ( theMakeGroups )
6505 newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6507 newGroupIDs.reset( new std::list< int > );
6512 //=======================================================================
6514 * \brief Create groups of elements made during transformation
6515 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6516 * \param elemGens - elements making corresponding myLastCreatedElems
6517 * \param postfix - to push_back to names of new groups
6518 * \param targetMesh - mesh to create groups in
6519 * \param topPresent - is there are "top" elements that are created by sweeping
6521 //=======================================================================
6523 SMESH_MeshEditor::PGroupIDs
6524 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6525 const SMESH_SequenceOfElemPtr& elemGens,
6526 const std::string& postfix,
6527 SMESH_Mesh* targetMesh,
6528 const bool topPresent)
6530 PGroupIDs newGroupIDs( new list<int> );
6531 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6533 // Sort existing groups by types and collect their names
6535 // containers to store an old group and generated new ones;
6536 // 1st new group is for result elems of different type than a source one;
6537 // 2nd new group is for same type result elems ("top" group at extrusion)
6539 using boost::make_tuple;
6540 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6541 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6542 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6544 set< string > groupNames;
6546 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6547 if ( !groupIt->more() ) return newGroupIDs;
6549 int newGroupID = mesh->GetGroupIds().back()+1;
6550 while ( groupIt->more() )
6552 SMESH_Group * group = groupIt->next();
6553 if ( !group ) continue;
6554 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6555 if ( !groupDS || groupDS->IsEmpty() ) continue;
6556 groupNames.insert ( group->GetName() );
6557 groupDS->SetStoreName( group->GetName() );
6558 const SMDSAbs_ElementType type = groupDS->GetType();
6559 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6560 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6561 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6562 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6565 // Loop on nodes and elements to add them in new groups
6567 vector< const SMDS_MeshElement* > resultElems;
6568 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6570 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6571 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6572 if ( gens.size() != elems.size() )
6573 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6575 // loop on created elements
6576 for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6578 const SMDS_MeshElement* sourceElem = gens[ iElem ];
6579 if ( !sourceElem ) {
6580 MESSAGE("generateGroups(): NULL source element");
6583 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6584 if ( groupsOldNew.empty() ) { // no groups of this type at all
6585 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6586 ++iElem; // skip all elements made by sourceElem
6589 // collect all elements made by the iElem-th sourceElem
6590 resultElems.clear();
6591 if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6592 if ( resElem != sourceElem )
6593 resultElems.push_back( resElem );
6594 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6595 if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6596 if ( resElem != sourceElem )
6597 resultElems.push_back( resElem );
6599 const SMDS_MeshElement* topElem = 0;
6600 if ( isNodes ) // there must be a top element
6602 topElem = resultElems.back();
6603 resultElems.pop_back();
6607 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6608 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6609 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6611 topElem = *resElemIt;
6612 *resElemIt = 0; // erase *resElemIt
6616 // add resultElems to groups originted from ones the sourceElem belongs to
6617 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6618 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6620 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6621 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6623 // fill in a new group
6624 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6625 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6626 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6628 newGroup.Add( *resElemIt );
6630 // fill a "top" group
6633 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6634 newTopGroup.Add( topElem );
6638 } // loop on created elements
6639 }// loop on nodes and elements
6641 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6643 list<int> topGrouIds;
6644 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6646 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6647 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6648 orderedOldNewGroups[i]->get<2>() };
6649 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6651 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6652 if ( newGroupDS->IsEmpty() )
6654 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6659 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6662 const bool isTop = ( topPresent &&
6663 newGroupDS->GetType() == oldGroupDS->GetType() &&
6666 string name = oldGroupDS->GetStoreName();
6667 { // remove trailing whitespaces (issue 22599)
6668 size_t size = name.size();
6669 while ( size > 1 && isspace( name[ size-1 ]))
6671 if ( size != name.size() )
6673 name.resize( size );
6674 oldGroupDS->SetStoreName( name.c_str() );
6677 if ( !targetMesh ) {
6678 string suffix = ( isTop ? "top": postfix.c_str() );
6682 while ( !groupNames.insert( name ).second ) // name exists
6683 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6688 newGroupDS->SetStoreName( name.c_str() );
6690 // make a SMESH_Groups
6691 mesh->AddGroup( newGroupDS );
6693 topGrouIds.push_back( newGroupDS->GetID() );
6695 newGroupIDs->push_back( newGroupDS->GetID() );
6699 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6704 //================================================================================
6706 * * \brief Return list of group of nodes close to each other within theTolerance
6707 * * Search among theNodes or in the whole mesh if theNodes is empty using
6708 * * an Octree algorithm
6709 * \param [in,out] theNodes - the nodes to treat
6710 * \param [in] theTolerance - the tolerance
6711 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
6712 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
6713 * corner and medium nodes in separate groups
6715 //================================================================================
6717 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6718 const double theTolerance,
6719 TListOfListOfNodes & theGroupsOfNodes,
6720 bool theSeparateCornersAndMedium)
6724 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
6725 myMesh->NbFaces ( ORDER_QUADRATIC ) +
6726 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
6727 theSeparateCornersAndMedium = false;
6729 TIDSortedNodeSet& corners = theNodes;
6730 TIDSortedNodeSet medium;
6732 if ( theNodes.empty() ) // get all nodes in the mesh
6734 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
6735 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
6736 if ( theSeparateCornersAndMedium )
6737 while ( nIt->more() )
6739 const SMDS_MeshNode* n = nIt->next();
6740 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
6741 nodeSet->insert( nodeSet->end(), n );
6744 while ( nIt->more() )
6745 theNodes.insert( theNodes.end(), nIt->next() );
6747 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
6749 TIDSortedNodeSet::iterator nIt = corners.begin();
6750 while ( nIt != corners.end() )
6751 if ( SMESH_MesherHelper::IsMedium( *nIt ))
6753 medium.insert( medium.end(), *nIt );
6754 corners.erase( nIt++ );
6762 if ( !corners.empty() )
6763 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
6764 if ( !medium.empty() )
6765 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
6768 //=======================================================================
6769 //function : SimplifyFace
6770 //purpose : split a chain of nodes into several closed chains
6771 //=======================================================================
6773 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6774 vector<const SMDS_MeshNode *>& poly_nodes,
6775 vector<int>& quantities) const
6777 int nbNodes = faceNodes.size();
6778 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
6782 size_t prevNbQuant = quantities.size();
6784 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
6785 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
6786 map< const SMDS_MeshNode*, int >::iterator nInd;
6788 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
6789 simpleNodes.push_back( faceNodes[0] );
6790 for ( int iCur = 1; iCur < nbNodes; iCur++ )
6792 if ( faceNodes[ iCur ] != simpleNodes.back() )
6794 int index = simpleNodes.size();
6795 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
6796 int prevIndex = nInd->second;
6797 if ( prevIndex < index )
6800 int loopLen = index - prevIndex;
6803 // store the sub-loop
6804 quantities.push_back( loopLen );
6805 for ( int i = prevIndex; i < index; i++ )
6806 poly_nodes.push_back( simpleNodes[ i ]);
6808 simpleNodes.resize( prevIndex+1 );
6812 simpleNodes.push_back( faceNodes[ iCur ]);
6817 if ( simpleNodes.size() > 2 )
6819 quantities.push_back( simpleNodes.size() );
6820 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
6823 return quantities.size() - prevNbQuant;
6826 //=======================================================================
6827 //function : MergeNodes
6828 //purpose : In each group, the cdr of nodes are substituted by the first one
6830 //=======================================================================
6832 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
6833 const bool theAvoidMakingHoles)
6837 SMESHDS_Mesh* mesh = GetMeshDS();
6839 TNodeNodeMap nodeNodeMap; // node to replace - new node
6840 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6841 list< int > rmElemIds, rmNodeIds;
6842 vector< ElemFeatures > newElemDefs;
6844 // Fill nodeNodeMap and elems
6846 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6847 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
6849 list<const SMDS_MeshNode*>& nodes = *grIt;
6850 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6851 const SMDS_MeshNode* nToKeep = *nIt;
6852 for ( ++nIt; nIt != nodes.end(); nIt++ )
6854 const SMDS_MeshNode* nToRemove = *nIt;
6855 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
6856 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6857 while ( invElemIt->more() ) {
6858 const SMDS_MeshElement* elem = invElemIt->next();
6864 // Apply recursive replacements (BUG 0020185)
6865 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
6866 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
6868 const SMDS_MeshNode* nToKeep = nnIt->second;
6869 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
6870 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
6872 nToKeep = nnIt_i->second;
6873 nnIt->second = nToKeep;
6874 nnIt_i = nodeNodeMap.find( nToKeep );
6878 if ( theAvoidMakingHoles )
6880 // find elements whose topology changes
6882 vector<const SMDS_MeshElement*> pbElems;
6883 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6884 for ( ; eIt != elems.end(); ++eIt )
6886 const SMDS_MeshElement* elem = *eIt;
6887 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6888 while ( itN->more() )
6890 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6891 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6892 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
6894 // several nodes of elem stick
6895 pbElems.push_back( elem );
6900 // exclude from merge nodes causing spoiling element
6901 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
6903 bool nodesExcluded = false;
6904 for ( size_t i = 0; i < pbElems.size(); ++i )
6906 size_t prevNbMergeNodes = nodeNodeMap.size();
6907 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
6908 prevNbMergeNodes < nodeNodeMap.size() )
6909 nodesExcluded = true;
6911 if ( !nodesExcluded )
6916 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
6918 const SMDS_MeshNode* nToRemove = nnIt->first;
6919 const SMDS_MeshNode* nToKeep = nnIt->second;
6920 if ( nToRemove != nToKeep )
6922 rmNodeIds.push_back( nToRemove->GetID() );
6923 AddToSameGroups( nToKeep, nToRemove, mesh );
6924 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
6925 // w/o creating node in place of merged ones.
6926 SMDS_PositionPtr pos = nToRemove->GetPosition();
6927 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6928 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6929 sm->SetIsAlwaysComputed( true );
6933 // Change element nodes or remove an element
6935 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6936 for ( ; eIt != elems.end(); eIt++ )
6938 const SMDS_MeshElement* elem = *eIt;
6939 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
6941 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
6943 rmElemIds.push_back( elem->GetID() );
6945 for ( size_t i = 0; i < newElemDefs.size(); ++i )
6947 if ( i > 0 || !mesh->ChangeElementNodes( elem,
6948 & newElemDefs[i].myNodes[0],
6949 newElemDefs[i].myNodes.size() ))
6953 newElemDefs[i].SetID( elem->GetID() );
6954 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6955 if ( !keepElem ) rmElemIds.pop_back();
6959 newElemDefs[i].SetID( -1 );
6961 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
6962 if ( sm && newElem )
6963 sm->AddElement( newElem );
6964 if ( elem != newElem )
6965 ReplaceElemInGroups( elem, newElem, mesh );
6970 // Remove bad elements, then equal nodes (order important)
6971 Remove( rmElemIds, /*isNodes=*/false );
6972 Remove( rmNodeIds, /*isNodes=*/true );
6977 //=======================================================================
6978 //function : applyMerge
6979 //purpose : Compute new connectivity of an element after merging nodes
6980 // \param [in] elems - the element
6981 // \param [out] newElemDefs - definition(s) of result element(s)
6982 // \param [inout] nodeNodeMap - nodes to merge
6983 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
6984 // after merging (but not degenerated), removes nodes causing
6985 // the invalidity from \a nodeNodeMap.
6986 // \return bool - true if the element should be removed
6987 //=======================================================================
6989 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
6990 vector< ElemFeatures >& newElemDefs,
6991 TNodeNodeMap& nodeNodeMap,
6992 const bool avoidMakingHoles )
6994 bool toRemove = false; // to remove elem
6995 int nbResElems = 1; // nb new elements
6997 newElemDefs.resize(nbResElems);
6998 newElemDefs[0].Init( elem );
6999 newElemDefs[0].myNodes.clear();
7001 set<const SMDS_MeshNode*> nodeSet;
7002 vector< const SMDS_MeshNode*> curNodes;
7003 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7006 const int nbNodes = elem->NbNodes();
7007 SMDSAbs_EntityType entity = elem->GetEntityType();
7009 curNodes.resize( nbNodes );
7010 uniqueNodes.resize( nbNodes );
7011 iRepl.resize( nbNodes );
7012 int iUnique = 0, iCur = 0, nbRepl = 0;
7014 // Get new seq of nodes
7016 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7017 while ( itN->more() )
7019 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7021 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7022 if ( nnIt != nodeNodeMap.end() ) {
7025 curNodes[ iCur ] = n;
7026 bool isUnique = nodeSet.insert( n ).second;
7028 uniqueNodes[ iUnique++ ] = n;
7030 iRepl[ nbRepl++ ] = iCur;
7034 // Analyse element topology after replacement
7036 int nbUniqueNodes = nodeSet.size();
7037 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7042 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7044 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7045 int nbCorners = nbNodes / 2;
7046 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7048 int iNext = ( iCur + 1 ) % nbCorners;
7049 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7051 int iMedium = iCur + nbCorners;
7052 vector< const SMDS_MeshNode* >::iterator i =
7053 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7055 curNodes[ iMedium ]);
7056 if ( i != uniqueNodes.end() )
7059 for ( ; i+1 != uniqueNodes.end(); ++i )
7068 case SMDSEntity_Polygon:
7069 case SMDSEntity_Quad_Polygon: // Polygon
7071 ElemFeatures* elemType = & newElemDefs[0];
7072 const bool isQuad = elemType->myIsQuad;
7074 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7075 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7077 // a polygon can divide into several elements
7078 vector<const SMDS_MeshNode *> polygons_nodes;
7079 vector<int> quantities;
7080 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7081 newElemDefs.resize( nbResElems );
7082 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7084 ElemFeatures* elemType = & newElemDefs[iface];
7085 if ( iface ) elemType->Init( elem );
7087 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7088 int nbNewNodes = quantities[iface];
7089 face_nodes.assign( polygons_nodes.begin() + inode,
7090 polygons_nodes.begin() + inode + nbNewNodes );
7091 inode += nbNewNodes;
7092 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7094 bool isValid = ( nbNewNodes % 2 == 0 );
7095 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7096 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7097 elemType->SetQuad( isValid );
7098 if ( isValid ) // put medium nodes after corners
7099 SMDS_MeshCell::applyInterlaceRev
7100 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7101 nbNewNodes ), face_nodes );
7103 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7105 nbUniqueNodes = newElemDefs[0].myNodes.size();
7109 case SMDSEntity_Polyhedra: // Polyhedral volume
7111 if ( nbUniqueNodes >= 4 )
7113 // each face has to be analyzed in order to check volume validity
7114 if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7116 int nbFaces = aPolyedre->NbFaces();
7118 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7119 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7120 vector<const SMDS_MeshNode *> faceNodes;
7124 for (int iface = 1; iface <= nbFaces; iface++)
7126 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7127 faceNodes.resize( nbFaceNodes );
7128 for (int inode = 1; inode <= nbFaceNodes; inode++)
7130 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7131 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7132 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7133 faceNode = (*nnIt).second;
7134 faceNodes[inode - 1] = faceNode;
7136 SimplifyFace(faceNodes, poly_nodes, quantities);
7139 if ( quantities.size() > 3 )
7141 // TODO: remove coincident faces
7143 nbUniqueNodes = newElemDefs[0].myNodes.size();
7151 // TODO not all the possible cases are solved. Find something more generic?
7152 case SMDSEntity_Edge: //////// EDGE
7153 case SMDSEntity_Triangle: //// TRIANGLE
7154 case SMDSEntity_Quad_Triangle:
7155 case SMDSEntity_Tetra:
7156 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7160 case SMDSEntity_Quad_Edge:
7164 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7166 if ( nbUniqueNodes < 3 )
7168 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7169 toRemove = true; // opposite nodes stick
7174 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7183 if ( nbUniqueNodes == 6 &&
7185 ( nbRepl == 1 || iRepl[1] >= 4 ))
7191 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7200 if ( nbUniqueNodes == 7 &&
7202 ( nbRepl == 1 || iRepl[1] != 8 ))
7208 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7210 if ( nbUniqueNodes == 4 ) {
7211 // ---------------------------------> tetrahedron
7212 if ( curNodes[3] == curNodes[4] &&
7213 curNodes[3] == curNodes[5] ) {
7217 else if ( curNodes[0] == curNodes[1] &&
7218 curNodes[0] == curNodes[2] ) {
7219 // bottom nodes stick: set a top before
7220 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7221 uniqueNodes[ 0 ] = curNodes [ 5 ];
7222 uniqueNodes[ 1 ] = curNodes [ 4 ];
7223 uniqueNodes[ 2 ] = curNodes [ 3 ];
7226 else if (( curNodes[0] == curNodes[3] ) +
7227 ( curNodes[1] == curNodes[4] ) +
7228 ( curNodes[2] == curNodes[5] ) == 2 ) {
7229 // a lateral face turns into a line
7233 else if ( nbUniqueNodes == 5 ) {
7234 // PENTAHEDRON --------------------> pyramid
7235 if ( curNodes[0] == curNodes[3] )
7237 uniqueNodes[ 0 ] = curNodes[ 1 ];
7238 uniqueNodes[ 1 ] = curNodes[ 4 ];
7239 uniqueNodes[ 2 ] = curNodes[ 5 ];
7240 uniqueNodes[ 3 ] = curNodes[ 2 ];
7241 uniqueNodes[ 4 ] = curNodes[ 0 ];
7244 if ( curNodes[1] == curNodes[4] )
7246 uniqueNodes[ 0 ] = curNodes[ 0 ];
7247 uniqueNodes[ 1 ] = curNodes[ 2 ];
7248 uniqueNodes[ 2 ] = curNodes[ 5 ];
7249 uniqueNodes[ 3 ] = curNodes[ 3 ];
7250 uniqueNodes[ 4 ] = curNodes[ 1 ];
7253 if ( curNodes[2] == curNodes[5] )
7255 uniqueNodes[ 0 ] = curNodes[ 0 ];
7256 uniqueNodes[ 1 ] = curNodes[ 3 ];
7257 uniqueNodes[ 2 ] = curNodes[ 4 ];
7258 uniqueNodes[ 3 ] = curNodes[ 1 ];
7259 uniqueNodes[ 4 ] = curNodes[ 2 ];
7265 case SMDSEntity_Hexa:
7267 //////////////////////////////////// HEXAHEDRON
7268 SMDS_VolumeTool hexa (elem);
7269 hexa.SetExternalNormal();
7270 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7271 //////////////////////// HEX ---> tetrahedron
7272 for ( int iFace = 0; iFace < 6; iFace++ ) {
7273 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7274 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7275 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7276 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7277 // one face turns into a point ...
7278 int pickInd = ind[ 0 ];
7279 int iOppFace = hexa.GetOppFaceIndex( iFace );
7280 ind = hexa.GetFaceNodesIndices( iOppFace );
7282 uniqueNodes.clear();
7283 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7284 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7287 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7289 if ( nbStick == 1 ) {
7290 // ... and the opposite one - into a triangle.
7292 uniqueNodes.push_back( curNodes[ pickInd ]);
7299 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7300 //////////////////////// HEX ---> prism
7301 int nbTria = 0, iTria[3];
7302 const int *ind; // indices of face nodes
7303 // look for triangular faces
7304 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7305 ind = hexa.GetFaceNodesIndices( iFace );
7306 TIDSortedNodeSet faceNodes;
7307 for ( iCur = 0; iCur < 4; iCur++ )
7308 faceNodes.insert( curNodes[ind[iCur]] );
7309 if ( faceNodes.size() == 3 )
7310 iTria[ nbTria++ ] = iFace;
7312 // check if triangles are opposite
7313 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7315 // set nodes of the bottom triangle
7316 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7318 for ( iCur = 0; iCur < 4; iCur++ )
7319 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7320 indB.push_back( ind[iCur] );
7321 if ( !hexa.IsForward() )
7322 std::swap( indB[0], indB[2] );
7323 for ( iCur = 0; iCur < 3; iCur++ )
7324 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7325 // set nodes of the top triangle
7326 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7327 for ( iCur = 0; iCur < 3; ++iCur )
7328 for ( int j = 0; j < 4; ++j )
7329 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7331 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7338 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7339 //////////////////// HEXAHEDRON ---> pyramid
7340 for ( int iFace = 0; iFace < 6; iFace++ ) {
7341 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7342 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7343 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7344 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7345 // one face turns into a point ...
7346 int iOppFace = hexa.GetOppFaceIndex( iFace );
7347 ind = hexa.GetFaceNodesIndices( iOppFace );
7348 uniqueNodes.clear();
7349 for ( iCur = 0; iCur < 4; iCur++ ) {
7350 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7353 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7355 if ( uniqueNodes.size() == 4 ) {
7356 // ... and the opposite one is a quadrangle
7358 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7359 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7367 if ( toRemove && nbUniqueNodes > 4 ) {
7368 ////////////////// HEXAHEDRON ---> polyhedron
7369 hexa.SetExternalNormal();
7370 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7371 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7372 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7373 quantities.reserve( 6 ); quantities.clear();
7374 for ( int iFace = 0; iFace < 6; iFace++ )
7376 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7377 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7378 curNodes[ind[1]] == curNodes[ind[3]] )
7381 break; // opposite nodes stick
7384 for ( iCur = 0; iCur < 4; iCur++ )
7386 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7387 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7389 if ( nodeSet.size() < 3 )
7390 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7392 quantities.push_back( nodeSet.size() );
7394 if ( quantities.size() >= 4 )
7397 nbUniqueNodes = poly_nodes.size();
7398 newElemDefs[0].SetPoly(true);
7402 } // case HEXAHEDRON
7407 } // switch ( entity )
7409 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7411 // erase from nodeNodeMap nodes whose merge spoils elem
7412 vector< const SMDS_MeshNode* > noMergeNodes;
7413 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7414 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7415 nodeNodeMap.erase( noMergeNodes[i] );
7418 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7420 uniqueNodes.resize( nbUniqueNodes );
7422 if ( !toRemove && nbResElems == 0 )
7425 newElemDefs.resize( nbResElems );
7431 // ========================================================
7432 // class : ComparableElement
7433 // purpose : allow comparing elements basing on their nodes
7434 // ========================================================
7436 class ComparableElement : public boost::container::flat_set< int >
7438 typedef boost::container::flat_set< int > int_set;
7440 const SMDS_MeshElement* myElem;
7442 mutable int myGroupID;
7446 ComparableElement( const SMDS_MeshElement* theElem ):
7447 myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7449 this->reserve( theElem->NbNodes() );
7450 for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7452 int id = nodeIt->next()->GetID();
7458 const SMDS_MeshElement* GetElem() const { return myElem; }
7460 int& GroupID() const { return myGroupID; }
7461 //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7463 ComparableElement( const ComparableElement& theSource ) // move copy
7466 ComparableElement& src = const_cast< ComparableElement& >( theSource );
7467 (int_set&) (*this ) = std::move( src );
7468 myElem = src.myElem;
7469 mySumID = src.mySumID;
7470 myGroupID = src.myGroupID;
7473 static int HashCode(const ComparableElement& se, int limit )
7475 return ::HashCode( se.mySumID, limit );
7477 static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7479 return ( se1 == se2 );
7484 //=======================================================================
7485 //function : FindEqualElements
7486 //purpose : Return list of group of elements built on the same nodes.
7487 // Search among theElements or in the whole mesh if theElements is empty
7488 //=======================================================================
7490 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet & theElements,
7491 TListOfListOfElementsID & theGroupsOfElementsID )
7495 SMDS_ElemIteratorPtr elemIt;
7496 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7497 else elemIt = SMESHUtils::elemSetIterator( theElements );
7499 typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7500 typedef std::list<int> TGroupOfElems;
7501 TMapOfElements mapOfElements;
7502 std::vector< TGroupOfElems > arrayOfGroups;
7503 TGroupOfElems groupOfElems;
7505 while ( elemIt->more() )
7507 const SMDS_MeshElement* curElem = elemIt->next();
7508 if ( curElem->IsNull() )
7510 ComparableElement compElem = curElem;
7512 const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7513 if ( elemInSet.GetElem() != curElem ) // coincident elem
7515 int& iG = elemInSet.GroupID();
7518 iG = arrayOfGroups.size();
7519 arrayOfGroups.push_back( groupOfElems );
7520 arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7522 arrayOfGroups[ iG ].push_back( curElem->GetID() );
7526 groupOfElems.clear();
7527 std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7528 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7530 if ( groupIt->size() > 1 ) {
7531 //groupOfElems.sort(); -- theElements are sorted already
7532 theGroupsOfElementsID.emplace_back( *groupIt );
7537 //=======================================================================
7538 //function : MergeElements
7539 //purpose : In each given group, substitute all elements by the first one.
7540 //=======================================================================
7542 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7546 typedef list<int> TListOfIDs;
7547 TListOfIDs rmElemIds; // IDs of elems to remove
7549 SMESHDS_Mesh* aMesh = GetMeshDS();
7551 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7552 while ( groupsIt != theGroupsOfElementsID.end() ) {
7553 TListOfIDs& aGroupOfElemID = *groupsIt;
7554 aGroupOfElemID.sort();
7555 int elemIDToKeep = aGroupOfElemID.front();
7556 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7557 aGroupOfElemID.pop_front();
7558 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7559 while ( idIt != aGroupOfElemID.end() ) {
7560 int elemIDToRemove = *idIt;
7561 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7562 // add the kept element in groups of removed one (PAL15188)
7563 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7564 rmElemIds.push_back( elemIDToRemove );
7570 Remove( rmElemIds, false );
7573 //=======================================================================
7574 //function : MergeEqualElements
7575 //purpose : Remove all but one of elements built on the same nodes.
7576 //=======================================================================
7578 void SMESH_MeshEditor::MergeEqualElements()
7580 TIDSortedElemSet aMeshElements; /* empty input ==
7581 to merge equal elements in the whole mesh */
7582 TListOfListOfElementsID aGroupsOfElementsID;
7583 FindEqualElements( aMeshElements, aGroupsOfElementsID );
7584 MergeElements( aGroupsOfElementsID );
7587 //=======================================================================
7588 //function : findAdjacentFace
7590 //=======================================================================
7592 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7593 const SMDS_MeshNode* n2,
7594 const SMDS_MeshElement* elem)
7596 TIDSortedElemSet elemSet, avoidSet;
7598 avoidSet.insert ( elem );
7599 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7602 //=======================================================================
7603 //function : findSegment
7604 //purpose : Return a mesh segment by two nodes one of which can be medium
7605 //=======================================================================
7607 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7608 const SMDS_MeshNode* n2)
7610 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7611 while ( it->more() )
7613 const SMDS_MeshElement* seg = it->next();
7614 if ( seg->GetNodeIndex( n2 ) >= 0 )
7620 //=======================================================================
7621 //function : FindFreeBorder
7623 //=======================================================================
7625 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7627 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7628 const SMDS_MeshNode* theSecondNode,
7629 const SMDS_MeshNode* theLastNode,
7630 list< const SMDS_MeshNode* > & theNodes,
7631 list< const SMDS_MeshElement* >& theFaces)
7633 if ( !theFirstNode || !theSecondNode )
7635 // find border face between theFirstNode and theSecondNode
7636 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7640 theFaces.push_back( curElem );
7641 theNodes.push_back( theFirstNode );
7642 theNodes.push_back( theSecondNode );
7644 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7645 //TIDSortedElemSet foundElems;
7646 bool needTheLast = ( theLastNode != 0 );
7648 vector<const SMDS_MeshNode*> nodes;
7650 while ( nStart != theLastNode ) {
7651 if ( nStart == theFirstNode )
7652 return !needTheLast;
7654 // find all free border faces sharing nStart
7656 list< const SMDS_MeshElement* > curElemList;
7657 list< const SMDS_MeshNode* > nStartList;
7658 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7659 while ( invElemIt->more() ) {
7660 const SMDS_MeshElement* e = invElemIt->next();
7661 //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
7664 nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7665 SMDS_MeshElement::iterator() );
7666 nodes.push_back( nodes[ 0 ]);
7669 int iNode = 0, nbNodes = nodes.size() - 1;
7670 for ( iNode = 0; iNode < nbNodes; iNode++ )
7671 if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7672 ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7673 ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
7675 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
7676 curElemList.push_back( e );
7680 // analyse the found
7682 int nbNewBorders = curElemList.size();
7683 if ( nbNewBorders == 0 ) {
7684 // no free border furthermore
7685 return !needTheLast;
7687 else if ( nbNewBorders == 1 ) {
7688 // one more element found
7690 nStart = nStartList.front();
7691 curElem = curElemList.front();
7692 theFaces.push_back( curElem );
7693 theNodes.push_back( nStart );
7696 // several continuations found
7697 list< const SMDS_MeshElement* >::iterator curElemIt;
7698 list< const SMDS_MeshNode* >::iterator nStartIt;
7699 // check if one of them reached the last node
7700 if ( needTheLast ) {
7701 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7702 curElemIt!= curElemList.end();
7703 curElemIt++, nStartIt++ )
7704 if ( *nStartIt == theLastNode ) {
7705 theFaces.push_back( *curElemIt );
7706 theNodes.push_back( *nStartIt );
7710 // find the best free border by the continuations
7711 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7712 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7713 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7714 curElemIt!= curElemList.end();
7715 curElemIt++, nStartIt++ )
7717 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7718 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7719 // find one more free border
7720 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7724 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7725 // choice: clear a worse one
7726 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7727 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7728 contNodes[ iWorse ].clear();
7729 contFaces[ iWorse ].clear();
7732 if ( contNodes[0].empty() && contNodes[1].empty() )
7735 // push_back the best free border
7736 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7737 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7738 //theNodes.pop_back(); // remove nIgnore
7739 theNodes.pop_back(); // remove nStart
7740 //theFaces.pop_back(); // remove curElem
7741 theNodes.splice( theNodes.end(), *cNL );
7742 theFaces.splice( theFaces.end(), *cFL );
7745 } // several continuations found
7746 } // while ( nStart != theLastNode )
7751 //=======================================================================
7752 //function : CheckFreeBorderNodes
7753 //purpose : Return true if the tree nodes are on a free border
7754 //=======================================================================
7756 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7757 const SMDS_MeshNode* theNode2,
7758 const SMDS_MeshNode* theNode3)
7760 list< const SMDS_MeshNode* > nodes;
7761 list< const SMDS_MeshElement* > faces;
7762 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7765 //=======================================================================
7766 //function : SewFreeBorder
7768 //warning : for border-to-side sewing theSideSecondNode is considered as
7769 // the last side node and theSideThirdNode is not used
7770 //=======================================================================
7772 SMESH_MeshEditor::Sew_Error
7773 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7774 const SMDS_MeshNode* theBordSecondNode,
7775 const SMDS_MeshNode* theBordLastNode,
7776 const SMDS_MeshNode* theSideFirstNode,
7777 const SMDS_MeshNode* theSideSecondNode,
7778 const SMDS_MeshNode* theSideThirdNode,
7779 const bool theSideIsFreeBorder,
7780 const bool toCreatePolygons,
7781 const bool toCreatePolyedrs)
7785 Sew_Error aResult = SEW_OK;
7787 // ====================================
7788 // find side nodes and elements
7789 // ====================================
7791 list< const SMDS_MeshNode* > nSide[ 2 ];
7792 list< const SMDS_MeshElement* > eSide[ 2 ];
7793 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7794 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7798 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7799 nSide[0], eSide[0])) {
7800 MESSAGE(" Free Border 1 not found " );
7801 aResult = SEW_BORDER1_NOT_FOUND;
7803 if (theSideIsFreeBorder) {
7806 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7807 nSide[1], eSide[1])) {
7808 MESSAGE(" Free Border 2 not found " );
7809 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7812 if ( aResult != SEW_OK )
7815 if (!theSideIsFreeBorder) {
7819 // -------------------------------------------------------------------------
7821 // 1. If nodes to merge are not coincident, move nodes of the free border
7822 // from the coord sys defined by the direction from the first to last
7823 // nodes of the border to the correspondent sys of the side 2
7824 // 2. On the side 2, find the links most co-directed with the correspondent
7825 // links of the free border
7826 // -------------------------------------------------------------------------
7828 // 1. Since sewing may break if there are volumes to split on the side 2,
7829 // we won't move nodes but just compute new coordinates for them
7830 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7831 TNodeXYZMap nBordXYZ;
7832 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7833 list< const SMDS_MeshNode* >::iterator nBordIt;
7835 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7836 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7837 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7838 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7839 double tol2 = 1.e-8;
7840 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7841 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7842 // Need node movement.
7844 // find X and Z axes to create trsf
7845 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7847 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7849 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7852 gp_Ax3 toBordAx( Pb1, Zb, X );
7853 gp_Ax3 fromSideAx( Ps1, Zs, X );
7854 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7856 gp_Trsf toBordSys, fromSide2Sys;
7857 toBordSys.SetTransformation( toBordAx );
7858 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7859 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7862 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7863 const SMDS_MeshNode* n = *nBordIt;
7864 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7865 toBordSys.Transforms( xyz );
7866 fromSide2Sys.Transforms( xyz );
7867 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7871 // just insert nodes XYZ in the nBordXYZ map
7872 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7873 const SMDS_MeshNode* n = *nBordIt;
7874 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7878 // 2. On the side 2, find the links most co-directed with the correspondent
7879 // links of the free border
7881 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7882 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7883 sideNodes.push_back( theSideFirstNode );
7885 bool hasVolumes = false;
7886 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7887 set<long> foundSideLinkIDs, checkedLinkIDs;
7888 SMDS_VolumeTool volume;
7889 //const SMDS_MeshNode* faceNodes[ 4 ];
7891 const SMDS_MeshNode* sideNode;
7892 const SMDS_MeshElement* sideElem = 0;
7893 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7894 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7895 nBordIt = bordNodes.begin();
7897 // border node position and border link direction to compare with
7898 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7899 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7900 // choose next side node by link direction or by closeness to
7901 // the current border node:
7902 bool searchByDir = ( *nBordIt != theBordLastNode );
7904 // find the next node on the Side 2
7906 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7908 checkedLinkIDs.clear();
7909 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7911 // loop on inverse elements of current node (prevSideNode) on the Side 2
7912 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7913 while ( invElemIt->more() )
7915 const SMDS_MeshElement* elem = invElemIt->next();
7916 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7917 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7918 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7919 bool isVolume = volume.Set( elem );
7920 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7921 if ( isVolume ) // --volume
7923 else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7924 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7925 SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
7926 while ( nIt->more() ) {
7927 nodes[ iNode ] = cast2Node( nIt->next() );
7928 if ( nodes[ iNode++ ] == prevSideNode )
7929 iPrevNode = iNode - 1;
7931 // there are 2 links to check
7936 // loop on links, to be precise, on the second node of links
7937 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7938 const SMDS_MeshNode* n = nodes[ iNode ];
7940 if ( !volume.IsLinked( n, prevSideNode ))
7944 if ( iNode ) // a node before prevSideNode
7945 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7946 else // a node after prevSideNode
7947 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7949 // check if this link was already used
7950 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7951 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7952 if (!isJustChecked &&
7953 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7955 // test a link geometrically
7956 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7957 bool linkIsBetter = false;
7958 double dot = 0.0, dist = 0.0;
7959 if ( searchByDir ) { // choose most co-directed link
7960 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7961 linkIsBetter = ( dot > maxDot );
7963 else { // choose link with the node closest to bordPos
7964 dist = ( nextXYZ - bordPos ).SquareModulus();
7965 linkIsBetter = ( dist < minDist );
7967 if ( linkIsBetter ) {
7976 } // loop on inverse elements of prevSideNode
7979 MESSAGE(" Can't find path by links of the Side 2 ");
7980 return SEW_BAD_SIDE_NODES;
7982 sideNodes.push_back( sideNode );
7983 sideElems.push_back( sideElem );
7984 foundSideLinkIDs.insert ( linkID );
7985 prevSideNode = sideNode;
7987 if ( *nBordIt == theBordLastNode )
7988 searchByDir = false;
7990 // find the next border link to compare with
7991 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7992 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7993 // move to next border node if sideNode is before forward border node (bordPos)
7994 while ( *nBordIt != theBordLastNode && !searchByDir ) {
7995 prevBordNode = *nBordIt;
7997 bordPos = nBordXYZ[ *nBordIt ];
7998 bordDir = bordPos - nBordXYZ[ prevBordNode ];
7999 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8003 while ( sideNode != theSideSecondNode );
8005 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8006 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8007 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8009 } // end nodes search on the side 2
8011 // ============================
8012 // sew the border to the side 2
8013 // ============================
8015 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8016 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8018 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8019 if ( toMergeConformal && toCreatePolygons )
8021 // do not merge quadrangles if polygons are OK (IPAL0052824)
8022 eIt[0] = eSide[0].begin();
8023 eIt[1] = eSide[1].begin();
8024 bool allQuads[2] = { true, true };
8025 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8026 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8027 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8029 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8032 TListOfListOfNodes nodeGroupsToMerge;
8033 if (( toMergeConformal ) ||
8034 ( theSideIsFreeBorder && !theSideThirdNode )) {
8036 // all nodes are to be merged
8038 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8039 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8040 nIt[0]++, nIt[1]++ )
8042 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8043 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8044 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8049 // insert new nodes into the border and the side to get equal nb of segments
8051 // get normalized parameters of nodes on the borders
8052 vector< double > param[ 2 ];
8053 param[0].resize( maxNbNodes );
8054 param[1].resize( maxNbNodes );
8056 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8057 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8058 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8059 const SMDS_MeshNode* nPrev = *nIt;
8060 double bordLength = 0;
8061 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8062 const SMDS_MeshNode* nCur = *nIt;
8063 gp_XYZ segment (nCur->X() - nPrev->X(),
8064 nCur->Y() - nPrev->Y(),
8065 nCur->Z() - nPrev->Z());
8066 double segmentLen = segment.Modulus();
8067 bordLength += segmentLen;
8068 param[ iBord ][ iNode ] = bordLength;
8071 // normalize within [0,1]
8072 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8073 param[ iBord ][ iNode ] /= bordLength;
8077 // loop on border segments
8078 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8079 int i[ 2 ] = { 0, 0 };
8080 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8081 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8083 // element can be split while iterating on border if it has two edges in the border
8084 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8085 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8087 TElemOfNodeListMap insertMap;
8088 TElemOfNodeListMap::iterator insertMapIt;
8090 // key: elem to insert nodes into
8091 // value: 2 nodes to insert between + nodes to be inserted
8093 bool next[ 2 ] = { false, false };
8095 // find min adjacent segment length after sewing
8096 double nextParam = 10., prevParam = 0;
8097 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8098 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8099 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8100 if ( i[ iBord ] > 0 )
8101 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8103 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8104 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8105 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8107 // choose to insert or to merge nodes
8108 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8109 if ( Abs( du ) <= minSegLen * 0.2 ) {
8112 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8113 const SMDS_MeshNode* n0 = *nIt[0];
8114 const SMDS_MeshNode* n1 = *nIt[1];
8115 nodeGroupsToMerge.back().push_back( n1 );
8116 nodeGroupsToMerge.back().push_back( n0 );
8117 // position of node of the border changes due to merge
8118 param[ 0 ][ i[0] ] += du;
8119 // move n1 for the sake of elem shape evaluation during insertion.
8120 // n1 will be removed by MergeNodes() anyway
8121 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8122 next[0] = next[1] = true;
8127 int intoBord = ( du < 0 ) ? 0 : 1;
8128 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8129 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8130 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8131 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8132 if ( intoBord == 1 ) {
8133 // move node of the border to be on a link of elem of the side
8134 SMESH_NodeXYZ p1( n1 ), p2( n2 );
8135 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8136 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8137 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8139 elemReplaceMapIt = elemReplaceMap.find( elem );
8140 if ( elemReplaceMapIt != elemReplaceMap.end() )
8141 elem = elemReplaceMapIt->second;
8143 insertMapIt = insertMap.find( elem );
8144 bool notFound = ( insertMapIt == insertMap.end() );
8145 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8147 // insert into another link of the same element:
8148 // 1. perform insertion into the other link of the elem
8149 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8150 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8151 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8152 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8153 // 2. perform insertion into the link of adjacent faces
8154 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8155 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8157 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8158 InsertNodesIntoLink( seg, n12, n22, nodeList );
8160 if (toCreatePolyedrs) {
8161 // perform insertion into the links of adjacent volumes
8162 UpdateVolumes(n12, n22, nodeList);
8164 // 3. find an element appeared on n1 and n2 after the insertion
8165 insertMap.erase( insertMapIt );
8166 const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8167 elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8170 if ( notFound || otherLink ) {
8171 // add element and nodes of the side into the insertMap
8172 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8173 (*insertMapIt).second.push_back( n1 );
8174 (*insertMapIt).second.push_back( n2 );
8176 // add node to be inserted into elem
8177 (*insertMapIt).second.push_back( nIns );
8178 next[ 1 - intoBord ] = true;
8181 // go to the next segment
8182 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8183 if ( next[ iBord ] ) {
8184 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8186 nPrev[ iBord ] = *nIt[ iBord ];
8187 nIt[ iBord ]++; i[ iBord ]++;
8191 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8193 // perform insertion of nodes into elements
8195 for (insertMapIt = insertMap.begin();
8196 insertMapIt != insertMap.end();
8199 const SMDS_MeshElement* elem = (*insertMapIt).first;
8200 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8201 if ( nodeList.size() < 3 ) continue;
8202 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8203 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8205 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8207 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8208 InsertNodesIntoLink( seg, n1, n2, nodeList );
8211 if ( !theSideIsFreeBorder ) {
8212 // look for and insert nodes into the faces adjacent to elem
8213 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8214 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8217 if (toCreatePolyedrs) {
8218 // perform insertion into the links of adjacent volumes
8219 UpdateVolumes(n1, n2, nodeList);
8222 } // end: insert new nodes
8224 MergeNodes ( nodeGroupsToMerge );
8227 // Remove coincident segments
8230 TIDSortedElemSet segments;
8231 SMESH_SequenceOfElemPtr newFaces;
8232 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8234 if ( !myLastCreatedElems[i] ) continue;
8235 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8236 segments.insert( segments.end(), myLastCreatedElems[i] );
8238 newFaces.push_back( myLastCreatedElems[i] );
8240 // get segments adjacent to merged nodes
8241 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8242 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8244 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8245 if ( nodes.front()->IsNull() ) continue;
8246 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8247 while ( segIt->more() )
8248 segments.insert( segIt->next() );
8252 TListOfListOfElementsID equalGroups;
8253 if ( !segments.empty() )
8254 FindEqualElements( segments, equalGroups );
8255 if ( !equalGroups.empty() )
8257 // remove from segments those that will be removed
8258 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8259 for ( ; itGroups != equalGroups.end(); ++itGroups )
8261 list< int >& group = *itGroups;
8262 list< int >::iterator id = group.begin();
8263 for ( ++id; id != group.end(); ++id )
8264 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8265 segments.erase( seg );
8267 // remove equal segments
8268 MergeElements( equalGroups );
8270 // restore myLastCreatedElems
8271 myLastCreatedElems = newFaces;
8272 TIDSortedElemSet::iterator seg = segments.begin();
8273 for ( ; seg != segments.end(); ++seg )
8274 myLastCreatedElems.push_back( *seg );
8280 //=======================================================================
8281 //function : InsertNodesIntoLink
8282 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8283 // and theBetweenNode2 and split theElement
8284 //=======================================================================
8286 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8287 const SMDS_MeshNode* theBetweenNode1,
8288 const SMDS_MeshNode* theBetweenNode2,
8289 list<const SMDS_MeshNode*>& theNodesToInsert,
8290 const bool toCreatePoly)
8292 if ( !theElement ) return;
8294 SMESHDS_Mesh *aMesh = GetMeshDS();
8295 vector<const SMDS_MeshElement*> newElems;
8297 if ( theElement->GetType() == SMDSAbs_Edge )
8299 theNodesToInsert.push_front( theBetweenNode1 );
8300 theNodesToInsert.push_back ( theBetweenNode2 );
8301 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8302 const SMDS_MeshNode* n1 = *n;
8303 for ( ++n; n != theNodesToInsert.end(); ++n )
8305 const SMDS_MeshNode* n2 = *n;
8306 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8307 AddToSameGroups( seg, theElement, aMesh );
8309 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8312 theNodesToInsert.pop_front();
8313 theNodesToInsert.pop_back();
8315 if ( theElement->IsQuadratic() ) // add a not split part
8317 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8318 theElement->end_nodes() );
8319 int iOther = 0, nbN = nodes.size();
8320 for ( ; iOther < nbN; ++iOther )
8321 if ( nodes[iOther] != theBetweenNode1 &&
8322 nodes[iOther] != theBetweenNode2 )
8326 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8327 AddToSameGroups( seg, theElement, aMesh );
8329 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8331 else if ( iOther == 2 )
8333 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8334 AddToSameGroups( seg, theElement, aMesh );
8336 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8339 // treat new elements
8340 for ( size_t i = 0; i < newElems.size(); ++i )
8343 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8344 myLastCreatedElems.push_back( newElems[i] );
8346 ReplaceElemInGroups( theElement, newElems, aMesh );
8347 aMesh->RemoveElement( theElement );
8350 } // if ( theElement->GetType() == SMDSAbs_Edge )
8352 const SMDS_MeshElement* theFace = theElement;
8353 if ( theFace->GetType() != SMDSAbs_Face ) return;
8355 // find indices of 2 link nodes and of the rest nodes
8356 int iNode = 0, il1, il2, i3, i4;
8357 il1 = il2 = i3 = i4 = -1;
8358 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8360 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8361 while ( nodeIt->more() ) {
8362 const SMDS_MeshNode* n = nodeIt->next();
8363 if ( n == theBetweenNode1 )
8365 else if ( n == theBetweenNode2 )
8371 nodes[ iNode++ ] = n;
8373 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8376 // arrange link nodes to go one after another regarding the face orientation
8377 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8378 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8383 aNodesToInsert.reverse();
8385 // check that not link nodes of a quadrangles are in good order
8386 int nbFaceNodes = theFace->NbNodes();
8387 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8393 if (toCreatePoly || theFace->IsPoly()) {
8396 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8398 // add nodes of face up to first node of link
8400 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8401 while ( nodeIt->more() && !isFLN ) {
8402 const SMDS_MeshNode* n = nodeIt->next();
8403 poly_nodes[iNode++] = n;
8404 isFLN = ( n == nodes[il1] );
8406 // add nodes to insert
8407 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8408 for (; nIt != aNodesToInsert.end(); nIt++) {
8409 poly_nodes[iNode++] = *nIt;
8411 // add nodes of face starting from last node of link
8412 while ( nodeIt->more() ) {
8413 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8414 poly_nodes[iNode++] = n;
8418 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8421 else if ( !theFace->IsQuadratic() )
8423 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8424 int nbLinkNodes = 2 + aNodesToInsert.size();
8425 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8426 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8427 linkNodes[ 0 ] = nodes[ il1 ];
8428 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8429 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8430 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8431 linkNodes[ iNode++ ] = *nIt;
8433 // decide how to split a quadrangle: compare possible variants
8434 // and choose which of splits to be a quadrangle
8435 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8436 if ( nbFaceNodes == 3 ) {
8437 iBestQuad = nbSplits;
8440 else if ( nbFaceNodes == 4 ) {
8441 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8442 double aBestRate = DBL_MAX;
8443 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8445 double aBadRate = 0;
8446 // evaluate elements quality
8447 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8448 if ( iSplit == iQuad ) {
8449 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8453 aBadRate += getBadRate( &quad, aCrit );
8456 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8458 nodes[ iSplit < iQuad ? i4 : i3 ]);
8459 aBadRate += getBadRate( &tria, aCrit );
8463 if ( aBadRate < aBestRate ) {
8465 aBestRate = aBadRate;
8470 // create new elements
8472 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8474 if ( iSplit == iBestQuad )
8475 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8480 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8482 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8485 const SMDS_MeshNode* newNodes[ 4 ];
8486 newNodes[ 0 ] = linkNodes[ i1 ];
8487 newNodes[ 1 ] = linkNodes[ i2 ];
8488 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8489 newNodes[ 3 ] = nodes[ i4 ];
8490 if (iSplit == iBestQuad)
8491 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8493 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8495 } // end if(!theFace->IsQuadratic())
8497 else { // theFace is quadratic
8498 // we have to split theFace on simple triangles and one simple quadrangle
8500 int nbshift = tmp*2;
8501 // shift nodes in nodes[] by nbshift
8503 for(i=0; i<nbshift; i++) {
8504 const SMDS_MeshNode* n = nodes[0];
8505 for(j=0; j<nbFaceNodes-1; j++) {
8506 nodes[j] = nodes[j+1];
8508 nodes[nbFaceNodes-1] = n;
8510 il1 = il1 - nbshift;
8511 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8512 // n0 n1 n2 n0 n1 n2
8513 // +-----+-----+ +-----+-----+
8522 // create new elements
8524 if ( nbFaceNodes == 6 ) { // quadratic triangle
8525 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8526 if ( theFace->IsMediumNode(nodes[il1]) ) {
8527 // create quadrangle
8528 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8534 // create quadrangle
8535 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8541 else { // nbFaceNodes==8 - quadratic quadrangle
8542 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8543 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8544 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8545 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8546 // create quadrangle
8547 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8553 // create quadrangle
8554 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8560 // create needed triangles using n1,n2,n3 and inserted nodes
8561 int nbn = 2 + aNodesToInsert.size();
8562 vector<const SMDS_MeshNode*> aNodes(nbn);
8563 aNodes[0 ] = nodes[n1];
8564 aNodes[nbn-1] = nodes[n2];
8565 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8566 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8567 aNodes[iNode++] = *nIt;
8569 for ( i = 1; i < nbn; i++ )
8570 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8573 // remove the old face
8574 for ( size_t i = 0; i < newElems.size(); ++i )
8577 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8578 myLastCreatedElems.push_back( newElems[i] );
8580 ReplaceElemInGroups( theFace, newElems, aMesh );
8581 aMesh->RemoveElement(theFace);
8583 } // InsertNodesIntoLink()
8585 //=======================================================================
8586 //function : UpdateVolumes
8588 //=======================================================================
8590 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8591 const SMDS_MeshNode* theBetweenNode2,
8592 list<const SMDS_MeshNode*>& theNodesToInsert)
8596 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8597 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8598 const SMDS_MeshElement* elem = invElemIt->next();
8600 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8601 SMDS_VolumeTool aVolume (elem);
8602 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8605 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8606 int iface, nbFaces = aVolume.NbFaces();
8607 vector<const SMDS_MeshNode *> poly_nodes;
8608 vector<int> quantities (nbFaces);
8610 for (iface = 0; iface < nbFaces; iface++) {
8611 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8612 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8613 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8615 for (int inode = 0; inode < nbFaceNodes; inode++) {
8616 poly_nodes.push_back(faceNodes[inode]);
8618 if (nbInserted == 0) {
8619 if (faceNodes[inode] == theBetweenNode1) {
8620 if (faceNodes[inode + 1] == theBetweenNode2) {
8621 nbInserted = theNodesToInsert.size();
8623 // add nodes to insert
8624 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8625 for (; nIt != theNodesToInsert.end(); nIt++) {
8626 poly_nodes.push_back(*nIt);
8630 else if (faceNodes[inode] == theBetweenNode2) {
8631 if (faceNodes[inode + 1] == theBetweenNode1) {
8632 nbInserted = theNodesToInsert.size();
8634 // add nodes to insert in reversed order
8635 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8637 for (; nIt != theNodesToInsert.begin(); nIt--) {
8638 poly_nodes.push_back(*nIt);
8640 poly_nodes.push_back(*nIt);
8647 quantities[iface] = nbFaceNodes + nbInserted;
8650 // Replace the volume
8651 SMESHDS_Mesh *aMesh = GetMeshDS();
8653 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8655 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8656 myLastCreatedElems.push_back( newElem );
8657 ReplaceElemInGroups( elem, newElem, aMesh );
8659 aMesh->RemoveElement( elem );
8665 //================================================================================
8667 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8669 //================================================================================
8671 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8672 vector<const SMDS_MeshNode *> & nodes,
8673 vector<int> & nbNodeInFaces )
8676 nbNodeInFaces.clear();
8677 SMDS_VolumeTool vTool ( elem );
8678 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8680 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8681 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8682 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8687 //=======================================================================
8689 * \brief Convert elements contained in a sub-mesh to quadratic
8690 * \return int - nb of checked elements
8692 //=======================================================================
8694 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8695 SMESH_MesherHelper& theHelper,
8696 const bool theForce3d)
8698 //MESSAGE("convertElemToQuadratic");
8700 if( !theSm ) return nbElem;
8702 vector<int> nbNodeInFaces;
8703 vector<const SMDS_MeshNode *> nodes;
8704 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8705 while(ElemItr->more())
8708 const SMDS_MeshElement* elem = ElemItr->next();
8709 if( !elem ) continue;
8711 // analyse a necessity of conversion
8712 const SMDSAbs_ElementType aType = elem->GetType();
8713 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8715 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8716 bool hasCentralNodes = false;
8717 if ( elem->IsQuadratic() )
8720 switch ( aGeomType ) {
8721 case SMDSEntity_Quad_Triangle:
8722 case SMDSEntity_Quad_Quadrangle:
8723 case SMDSEntity_Quad_Hexa:
8724 case SMDSEntity_Quad_Penta:
8725 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8727 case SMDSEntity_BiQuad_Triangle:
8728 case SMDSEntity_BiQuad_Quadrangle:
8729 case SMDSEntity_TriQuad_Hexa:
8730 case SMDSEntity_BiQuad_Penta:
8731 alreadyOK = theHelper.GetIsBiQuadratic();
8732 hasCentralNodes = true;
8737 // take into account already present medium nodes
8739 case SMDSAbs_Volume:
8740 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8742 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8744 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8750 // get elem data needed to re-create it
8752 const int id = elem->GetID();
8753 const int nbNodes = elem->NbCornerNodes();
8754 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8755 if ( aGeomType == SMDSEntity_Polyhedra )
8756 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8757 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8758 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8760 // remove a linear element
8761 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8763 // remove central nodes of biquadratic elements (biquad->quad conversion)
8764 if ( hasCentralNodes )
8765 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8766 if ( nodes[i]->NbInverseElements() == 0 )
8767 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8769 const SMDS_MeshElement* NewElem = 0;
8775 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8783 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8786 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8789 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8793 case SMDSAbs_Volume :
8797 case SMDSEntity_Tetra:
8798 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8800 case SMDSEntity_Pyramid:
8801 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8803 case SMDSEntity_Penta:
8804 case SMDSEntity_Quad_Penta:
8805 case SMDSEntity_BiQuad_Penta:
8806 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8808 case SMDSEntity_Hexa:
8809 case SMDSEntity_Quad_Hexa:
8810 case SMDSEntity_TriQuad_Hexa:
8811 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8812 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8814 case SMDSEntity_Hexagonal_Prism:
8816 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8823 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8824 if( NewElem && NewElem->getshapeId() < 1 )
8825 theSm->AddElement( NewElem );
8829 //=======================================================================
8830 //function : ConvertToQuadratic
8832 //=======================================================================
8834 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8836 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8837 SMESHDS_Mesh* meshDS = GetMeshDS();
8839 SMESH_MesherHelper aHelper(*myMesh);
8841 aHelper.SetIsQuadratic( true );
8842 aHelper.SetIsBiQuadratic( theToBiQuad );
8843 aHelper.SetElementsOnShape(true);
8844 aHelper.ToFixNodeParameters( true );
8846 // convert elements assigned to sub-meshes
8847 int nbCheckedElems = 0;
8848 if ( myMesh->HasShapeToMesh() )
8850 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8852 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8853 while ( smIt->more() ) {
8854 SMESH_subMesh* sm = smIt->next();
8855 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8856 aHelper.SetSubShape( sm->GetSubShape() );
8857 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8863 // convert elements NOT assigned to sub-meshes
8864 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8865 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8867 aHelper.SetElementsOnShape(false);
8868 SMESHDS_SubMesh *smDS = 0;
8871 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8872 while( aEdgeItr->more() )
8874 const SMDS_MeshEdge* edge = aEdgeItr->next();
8875 if ( !edge->IsQuadratic() )
8877 int id = edge->GetID();
8878 const SMDS_MeshNode* n1 = edge->GetNode(0);
8879 const SMDS_MeshNode* n2 = edge->GetNode(1);
8881 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8883 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8884 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8888 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8893 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8894 while( aFaceItr->more() )
8896 const SMDS_MeshFace* face = aFaceItr->next();
8897 if ( !face ) continue;
8899 const SMDSAbs_EntityType type = face->GetEntityType();
8903 case SMDSEntity_Quad_Triangle:
8904 case SMDSEntity_Quad_Quadrangle:
8905 alreadyOK = !theToBiQuad;
8906 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8908 case SMDSEntity_BiQuad_Triangle:
8909 case SMDSEntity_BiQuad_Quadrangle:
8910 alreadyOK = theToBiQuad;
8911 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8913 default: alreadyOK = false;
8918 const int id = face->GetID();
8919 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8921 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8923 SMDS_MeshFace * NewFace = 0;
8926 case SMDSEntity_Triangle:
8927 case SMDSEntity_Quad_Triangle:
8928 case SMDSEntity_BiQuad_Triangle:
8929 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8930 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8931 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8934 case SMDSEntity_Quadrangle:
8935 case SMDSEntity_Quad_Quadrangle:
8936 case SMDSEntity_BiQuad_Quadrangle:
8937 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8938 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8939 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8943 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8945 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8949 vector<int> nbNodeInFaces;
8950 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8951 while(aVolumeItr->more())
8953 const SMDS_MeshVolume* volume = aVolumeItr->next();
8954 if ( !volume ) continue;
8956 const SMDSAbs_EntityType type = volume->GetEntityType();
8957 if ( volume->IsQuadratic() )
8962 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8963 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8964 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
8965 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
8966 default: alreadyOK = true;
8970 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8974 const int id = volume->GetID();
8975 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8976 if ( type == SMDSEntity_Polyhedra )
8977 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
8978 else if ( type == SMDSEntity_Hexagonal_Prism )
8979 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8981 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8983 SMDS_MeshVolume * NewVolume = 0;
8986 case SMDSEntity_Tetra:
8987 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8989 case SMDSEntity_Hexa:
8990 case SMDSEntity_Quad_Hexa:
8991 case SMDSEntity_TriQuad_Hexa:
8992 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8993 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8994 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8995 if ( nodes[i]->NbInverseElements() == 0 )
8996 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8998 case SMDSEntity_Pyramid:
8999 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9000 nodes[3], nodes[4], id, theForce3d);
9002 case SMDSEntity_Penta:
9003 case SMDSEntity_Quad_Penta:
9004 case SMDSEntity_BiQuad_Penta:
9005 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9006 nodes[3], nodes[4], nodes[5], id, theForce3d);
9007 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9008 if ( nodes[i]->NbInverseElements() == 0 )
9009 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9011 case SMDSEntity_Hexagonal_Prism:
9013 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9015 ReplaceElemInGroups(volume, NewVolume, meshDS);
9020 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9021 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9022 // aHelper.FixQuadraticElements(myError);
9023 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9027 //================================================================================
9029 * \brief Makes given elements quadratic
9030 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9031 * \param theElements - elements to make quadratic
9033 //================================================================================
9035 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9036 TIDSortedElemSet& theElements,
9037 const bool theToBiQuad)
9039 if ( theElements.empty() ) return;
9041 // we believe that all theElements are of the same type
9042 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9044 // get all nodes shared by theElements
9045 TIDSortedNodeSet allNodes;
9046 TIDSortedElemSet::iterator eIt = theElements.begin();
9047 for ( ; eIt != theElements.end(); ++eIt )
9048 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9050 // complete theElements with elements of lower dim whose all nodes are in allNodes
9052 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9053 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9054 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9055 for ( ; nIt != allNodes.end(); ++nIt )
9057 const SMDS_MeshNode* n = *nIt;
9058 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9059 while ( invIt->more() )
9061 const SMDS_MeshElement* e = invIt->next();
9062 const SMDSAbs_ElementType type = e->GetType();
9063 if ( e->IsQuadratic() )
9065 quadAdjacentElems[ type ].insert( e );
9068 switch ( e->GetEntityType() ) {
9069 case SMDSEntity_Quad_Triangle:
9070 case SMDSEntity_Quad_Quadrangle:
9071 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9072 case SMDSEntity_BiQuad_Triangle:
9073 case SMDSEntity_BiQuad_Quadrangle:
9074 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9075 default: alreadyOK = true;
9080 if ( type >= elemType )
9081 continue; // same type or more complex linear element
9083 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9084 continue; // e is already checked
9088 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9089 while ( nodeIt->more() && allIn )
9090 allIn = allNodes.count( nodeIt->next() );
9092 theElements.insert(e );
9096 SMESH_MesherHelper helper(*myMesh);
9097 helper.SetIsQuadratic( true );
9098 helper.SetIsBiQuadratic( theToBiQuad );
9100 // add links of quadratic adjacent elements to the helper
9102 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9103 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9104 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9106 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9108 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9109 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9110 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9112 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9114 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9115 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9116 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9118 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9121 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9123 SMESHDS_Mesh* meshDS = GetMeshDS();
9124 SMESHDS_SubMesh* smDS = 0;
9125 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9127 const SMDS_MeshElement* elem = *eIt;
9130 int nbCentralNodes = 0;
9131 switch ( elem->GetEntityType() ) {
9132 // linear convertible
9133 case SMDSEntity_Edge:
9134 case SMDSEntity_Triangle:
9135 case SMDSEntity_Quadrangle:
9136 case SMDSEntity_Tetra:
9137 case SMDSEntity_Pyramid:
9138 case SMDSEntity_Hexa:
9139 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9140 // quadratic that can become bi-quadratic
9141 case SMDSEntity_Quad_Triangle:
9142 case SMDSEntity_Quad_Quadrangle:
9143 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9145 case SMDSEntity_BiQuad_Triangle:
9146 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9147 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9149 default: alreadyOK = true;
9151 if ( alreadyOK ) continue;
9153 const SMDSAbs_ElementType type = elem->GetType();
9154 const int id = elem->GetID();
9155 const int nbNodes = elem->NbCornerNodes();
9156 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9158 helper.SetSubShape( elem->getshapeId() );
9160 if ( !smDS || !smDS->Contains( elem ))
9161 smDS = meshDS->MeshElements( elem->getshapeId() );
9162 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9164 SMDS_MeshElement * newElem = 0;
9167 case 4: // cases for most frequently used element types go first (for optimization)
9168 if ( type == SMDSAbs_Volume )
9169 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9171 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9174 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9175 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9178 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9181 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9184 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9185 nodes[4], id, theForce3d);
9188 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9189 nodes[4], nodes[5], id, theForce3d);
9193 ReplaceElemInGroups( elem, newElem, meshDS);
9194 if( newElem && smDS )
9195 smDS->AddElement( newElem );
9197 // remove central nodes
9198 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9199 if ( nodes[i]->NbInverseElements() == 0 )
9200 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9202 } // loop on theElements
9205 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9206 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9207 // helper.FixQuadraticElements( myError );
9208 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9212 //=======================================================================
9214 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9215 * \return int - nb of checked elements
9217 //=======================================================================
9219 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9220 SMDS_ElemIteratorPtr theItr,
9221 const int /*theShapeID*/)
9224 SMESHDS_Mesh* meshDS = GetMeshDS();
9225 ElemFeatures elemType;
9226 vector<const SMDS_MeshNode *> nodes;
9228 while( theItr->more() )
9230 const SMDS_MeshElement* elem = theItr->next();
9232 if( elem && elem->IsQuadratic())
9235 int nbCornerNodes = elem->NbCornerNodes();
9236 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9238 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9240 //remove a quadratic element
9241 if ( !theSm || !theSm->Contains( elem ))
9242 theSm = meshDS->MeshElements( elem->getshapeId() );
9243 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9245 // remove medium nodes
9246 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9247 if ( nodes[i]->NbInverseElements() == 0 )
9248 meshDS->RemoveFreeNode( nodes[i], theSm );
9250 // add a linear element
9251 nodes.resize( nbCornerNodes );
9252 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9253 ReplaceElemInGroups(elem, newElem, meshDS);
9254 if( theSm && newElem )
9255 theSm->AddElement( newElem );
9261 //=======================================================================
9262 //function : ConvertFromQuadratic
9264 //=======================================================================
9266 bool SMESH_MeshEditor::ConvertFromQuadratic()
9268 int nbCheckedElems = 0;
9269 if ( myMesh->HasShapeToMesh() )
9271 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9273 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9274 while ( smIt->more() ) {
9275 SMESH_subMesh* sm = smIt->next();
9276 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9277 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9283 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9284 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9286 SMESHDS_SubMesh *aSM = 0;
9287 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9295 //================================================================================
9297 * \brief Return true if all medium nodes of the element are in the node set
9299 //================================================================================
9301 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9303 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9304 if ( !nodeSet.count( elem->GetNode(i) ))
9310 //================================================================================
9312 * \brief Makes given elements linear
9314 //================================================================================
9316 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9318 if ( theElements.empty() ) return;
9320 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9321 set<int> mediumNodeIDs;
9322 TIDSortedElemSet::iterator eIt = theElements.begin();
9323 for ( ; eIt != theElements.end(); ++eIt )
9325 const SMDS_MeshElement* e = *eIt;
9326 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9327 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9330 // replace given elements by linear ones
9331 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9332 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9334 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9335 // except those elements sharing medium nodes of quadratic element whose medium nodes
9336 // are not all in mediumNodeIDs
9338 // get remaining medium nodes
9339 TIDSortedNodeSet mediumNodes;
9340 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9341 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9342 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9343 mediumNodes.insert( mediumNodes.end(), n );
9345 // find more quadratic elements to convert
9346 TIDSortedElemSet moreElemsToConvert;
9347 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9348 for ( ; nIt != mediumNodes.end(); ++nIt )
9350 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9351 while ( invIt->more() )
9353 const SMDS_MeshElement* e = invIt->next();
9354 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9356 // find a more complex element including e and
9357 // whose medium nodes are not in mediumNodes
9358 bool complexFound = false;
9359 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9361 SMDS_ElemIteratorPtr invIt2 =
9362 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9363 while ( invIt2->more() )
9365 const SMDS_MeshElement* eComplex = invIt2->next();
9366 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9368 int nbCommonNodes = SMESH_MeshAlgos::NbCommonNodes( e, eComplex );
9369 if ( nbCommonNodes == e->NbNodes())
9371 complexFound = true;
9372 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9378 if ( !complexFound )
9379 moreElemsToConvert.insert( e );
9383 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9384 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9387 //=======================================================================
9388 //function : SewSideElements
9390 //=======================================================================
9392 SMESH_MeshEditor::Sew_Error
9393 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9394 TIDSortedElemSet& theSide2,
9395 const SMDS_MeshNode* theFirstNode1,
9396 const SMDS_MeshNode* theFirstNode2,
9397 const SMDS_MeshNode* theSecondNode1,
9398 const SMDS_MeshNode* theSecondNode2)
9402 if ( theSide1.size() != theSide2.size() )
9403 return SEW_DIFF_NB_OF_ELEMENTS;
9405 Sew_Error aResult = SEW_OK;
9407 // 1. Build set of faces representing each side
9408 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9409 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9411 // =======================================================================
9412 // 1. Build set of faces representing each side:
9413 // =======================================================================
9414 // a. build set of nodes belonging to faces
9415 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9416 // c. create temporary faces representing side of volumes if correspondent
9417 // face does not exist
9419 SMESHDS_Mesh* aMesh = GetMeshDS();
9420 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9421 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9422 TIDSortedElemSet faceSet1, faceSet2;
9423 set<const SMDS_MeshElement*> volSet1, volSet2;
9424 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9425 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9426 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9427 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9428 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9429 int iSide, iFace, iNode;
9431 list<const SMDS_MeshElement* > tempFaceList;
9432 for ( iSide = 0; iSide < 2; iSide++ ) {
9433 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9434 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9435 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9436 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9437 set<const SMDS_MeshElement*>::iterator vIt;
9438 TIDSortedElemSet::iterator eIt;
9439 set<const SMDS_MeshNode*>::iterator nIt;
9441 // check that given nodes belong to given elements
9442 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9443 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9444 int firstIndex = -1, secondIndex = -1;
9445 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9446 const SMDS_MeshElement* elem = *eIt;
9447 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9448 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9449 if ( firstIndex > -1 && secondIndex > -1 ) break;
9451 if ( firstIndex < 0 || secondIndex < 0 ) {
9452 // we can simply return until temporary faces created
9453 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9456 // -----------------------------------------------------------
9457 // 1a. Collect nodes of existing faces
9458 // and build set of face nodes in order to detect missing
9459 // faces corresponding to sides of volumes
9460 // -----------------------------------------------------------
9462 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9464 // loop on the given element of a side
9465 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9466 //const SMDS_MeshElement* elem = *eIt;
9467 const SMDS_MeshElement* elem = *eIt;
9468 if ( elem->GetType() == SMDSAbs_Face ) {
9469 faceSet->insert( elem );
9470 set <const SMDS_MeshNode*> faceNodeSet;
9471 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9472 while ( nodeIt->more() ) {
9473 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9474 nodeSet->insert( n );
9475 faceNodeSet.insert( n );
9477 setOfFaceNodeSet.insert( faceNodeSet );
9479 else if ( elem->GetType() == SMDSAbs_Volume )
9480 volSet->insert( elem );
9482 // ------------------------------------------------------------------------------
9483 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9484 // ------------------------------------------------------------------------------
9486 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9487 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9488 while ( fIt->more() ) { // loop on faces sharing a node
9489 const SMDS_MeshElement* f = fIt->next();
9490 if ( faceSet->find( f ) == faceSet->end() ) {
9491 // check if all nodes are in nodeSet and
9492 // complete setOfFaceNodeSet if they are
9493 set <const SMDS_MeshNode*> faceNodeSet;
9494 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9495 bool allInSet = true;
9496 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9497 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9498 if ( nodeSet->find( n ) == nodeSet->end() )
9501 faceNodeSet.insert( n );
9504 faceSet->insert( f );
9505 setOfFaceNodeSet.insert( faceNodeSet );
9511 // -------------------------------------------------------------------------
9512 // 1c. Create temporary faces representing sides of volumes if correspondent
9513 // face does not exist
9514 // -------------------------------------------------------------------------
9516 if ( !volSet->empty() ) {
9517 //int nodeSetSize = nodeSet->size();
9519 // loop on given volumes
9520 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9521 SMDS_VolumeTool vol (*vIt);
9522 // loop on volume faces: find free faces
9523 // --------------------------------------
9524 list<const SMDS_MeshElement* > freeFaceList;
9525 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9526 if ( !vol.IsFreeFace( iFace ))
9528 // check if there is already a face with same nodes in a face set
9529 const SMDS_MeshElement* aFreeFace = 0;
9530 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9531 int nbNodes = vol.NbFaceNodes( iFace );
9532 set <const SMDS_MeshNode*> faceNodeSet;
9533 vol.GetFaceNodes( iFace, faceNodeSet );
9534 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9536 // no such a face is given but it still can exist, check it
9537 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9538 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9541 // create a temporary face
9542 if ( nbNodes == 3 ) {
9543 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9544 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9546 else if ( nbNodes == 4 ) {
9547 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9548 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9551 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9552 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9553 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9556 tempFaceList.push_back( aFreeFace );
9560 freeFaceList.push_back( aFreeFace );
9562 } // loop on faces of a volume
9564 // choose one of several free faces of a volume
9565 // --------------------------------------------
9566 if ( freeFaceList.size() > 1 ) {
9567 // choose a face having max nb of nodes shared by other elems of a side
9568 int maxNbNodes = -1;
9569 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9570 while ( fIt != freeFaceList.end() ) { // loop on free faces
9571 int nbSharedNodes = 0;
9572 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9573 while ( nodeIt->more() ) { // loop on free face nodes
9574 const SMDS_MeshNode* n =
9575 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9576 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9577 while ( invElemIt->more() ) {
9578 const SMDS_MeshElement* e = invElemIt->next();
9579 nbSharedNodes += faceSet->count( e );
9580 nbSharedNodes += elemSet->count( e );
9583 if ( nbSharedNodes > maxNbNodes ) {
9584 maxNbNodes = nbSharedNodes;
9585 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9587 else if ( nbSharedNodes == maxNbNodes ) {
9591 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9594 if ( freeFaceList.size() > 1 )
9596 // could not choose one face, use another way
9597 // choose a face most close to the bary center of the opposite side
9598 gp_XYZ aBC( 0., 0., 0. );
9599 set <const SMDS_MeshNode*> addedNodes;
9600 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9601 eIt = elemSet2->begin();
9602 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9603 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9604 while ( nodeIt->more() ) { // loop on free face nodes
9605 const SMDS_MeshNode* n =
9606 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9607 if ( addedNodes.insert( n ).second )
9608 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9611 aBC /= addedNodes.size();
9612 double minDist = DBL_MAX;
9613 fIt = freeFaceList.begin();
9614 while ( fIt != freeFaceList.end() ) { // loop on free faces
9616 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9617 while ( nodeIt->more() ) { // loop on free face nodes
9618 const SMDS_MeshNode* n =
9619 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9620 gp_XYZ p( n->X(),n->Y(),n->Z() );
9621 dist += ( aBC - p ).SquareModulus();
9623 if ( dist < minDist ) {
9625 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9628 fIt = freeFaceList.erase( fIt++ );
9631 } // choose one of several free faces of a volume
9633 if ( freeFaceList.size() == 1 ) {
9634 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9635 faceSet->insert( aFreeFace );
9636 // complete a node set with nodes of a found free face
9637 // for ( iNode = 0; iNode < ; iNode++ )
9638 // nodeSet->insert( fNodes[ iNode ] );
9641 } // loop on volumes of a side
9643 // // complete a set of faces if new nodes in a nodeSet appeared
9644 // // ----------------------------------------------------------
9645 // if ( nodeSetSize != nodeSet->size() ) {
9646 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9647 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9648 // while ( fIt->more() ) { // loop on faces sharing a node
9649 // const SMDS_MeshElement* f = fIt->next();
9650 // if ( faceSet->find( f ) == faceSet->end() ) {
9651 // // check if all nodes are in nodeSet and
9652 // // complete setOfFaceNodeSet if they are
9653 // set <const SMDS_MeshNode*> faceNodeSet;
9654 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9655 // bool allInSet = true;
9656 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9657 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9658 // if ( nodeSet->find( n ) == nodeSet->end() )
9659 // allInSet = false;
9661 // faceNodeSet.insert( n );
9663 // if ( allInSet ) {
9664 // faceSet->insert( f );
9665 // setOfFaceNodeSet.insert( faceNodeSet );
9671 } // Create temporary faces, if there are volumes given
9674 if ( faceSet1.size() != faceSet2.size() ) {
9675 // delete temporary faces: they are in reverseElements of actual nodes
9676 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9677 // while ( tmpFaceIt->more() )
9678 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9679 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9680 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9681 // aMesh->RemoveElement(*tmpFaceIt);
9682 MESSAGE("Diff nb of faces");
9683 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9686 // ============================================================
9687 // 2. Find nodes to merge:
9688 // bind a node to remove to a node to put instead
9689 // ============================================================
9691 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9692 if ( theFirstNode1 != theFirstNode2 )
9693 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9694 if ( theSecondNode1 != theSecondNode2 )
9695 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9697 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9698 set< long > linkIdSet; // links to process
9699 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9701 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9702 list< NLink > linkList[2];
9703 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9704 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9705 // loop on links in linkList; find faces by links and append links
9706 // of the found faces to linkList
9707 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9708 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9710 NLink link[] = { *linkIt[0], *linkIt[1] };
9711 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9712 if ( !linkIdSet.count( linkID ) )
9715 // by links, find faces in the face sets,
9716 // and find indices of link nodes in the found faces;
9717 // in a face set, there is only one or no face sharing a link
9718 // ---------------------------------------------------------------
9720 const SMDS_MeshElement* face[] = { 0, 0 };
9721 vector<const SMDS_MeshNode*> fnodes[2];
9722 int iLinkNode[2][2];
9723 TIDSortedElemSet avoidSet;
9724 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9725 const SMDS_MeshNode* n1 = link[iSide].first;
9726 const SMDS_MeshNode* n2 = link[iSide].second;
9727 //cout << "Side " << iSide << " ";
9728 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9729 // find a face by two link nodes
9730 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9731 *faceSetPtr[ iSide ], avoidSet,
9732 &iLinkNode[iSide][0],
9733 &iLinkNode[iSide][1] );
9736 //cout << " F " << face[ iSide]->GetID() <<endl;
9737 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9738 // put face nodes to fnodes
9739 SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9740 fnodes[ iSide ].assign( nIt, nEnd );
9741 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9745 // check similarity of elements of the sides
9746 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9747 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9748 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9749 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9752 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9754 break; // do not return because it's necessary to remove tmp faces
9757 // set nodes to merge
9758 // -------------------
9760 if ( face[0] && face[1] ) {
9761 const int nbNodes = face[0]->NbNodes();
9762 if ( nbNodes != face[1]->NbNodes() ) {
9763 MESSAGE("Diff nb of face nodes");
9764 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9765 break; // do not return because it s necessary to remove tmp faces
9767 bool reverse[] = { false, false }; // order of nodes in the link
9768 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9769 // analyse link orientation in faces
9770 int i1 = iLinkNode[ iSide ][ 0 ];
9771 int i2 = iLinkNode[ iSide ][ 1 ];
9772 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9774 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9775 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9776 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9778 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9779 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9782 // add other links of the faces to linkList
9783 // -----------------------------------------
9785 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9786 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9787 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9788 if ( !iter_isnew.second ) { // already in a set: no need to process
9789 linkIdSet.erase( iter_isnew.first );
9791 else // new in set == encountered for the first time: add
9793 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9794 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9795 linkList[0].push_back ( NLink( n1, n2 ));
9796 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9801 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9804 } // loop on link lists
9806 if ( aResult == SEW_OK &&
9807 ( //linkIt[0] != linkList[0].end() ||
9808 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9809 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9810 " " << (faceSetPtr[1]->empty()));
9811 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9814 // ====================================================================
9815 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9816 // ====================================================================
9818 // delete temporary faces
9819 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9820 // while ( tmpFaceIt->more() )
9821 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9822 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9823 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9824 aMesh->RemoveElement(*tmpFaceIt);
9826 if ( aResult != SEW_OK)
9829 list< int > nodeIDsToRemove;
9830 vector< const SMDS_MeshNode*> nodes;
9831 ElemFeatures elemType;
9833 // loop on nodes replacement map
9834 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9835 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9836 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9838 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9839 nodeIDsToRemove.push_back( nToRemove->GetID() );
9840 // loop on elements sharing nToRemove
9841 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9842 while ( invElemIt->more() ) {
9843 const SMDS_MeshElement* e = invElemIt->next();
9844 // get a new suite of nodes: make replacement
9845 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9846 nodes.resize( nbNodes );
9847 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9848 while ( nIt->more() ) {
9849 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9850 nnIt = nReplaceMap.find( n );
9851 if ( nnIt != nReplaceMap.end() ) {
9857 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9858 // elemIDsToRemove.push_back( e->GetID() );
9862 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9863 aMesh->RemoveElement( e );
9865 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9867 AddToSameGroups( newElem, e, aMesh );
9868 if ( int aShapeId = e->getshapeId() )
9869 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9875 Remove( nodeIDsToRemove, true );
9880 //================================================================================
9882 * \brief Find corresponding nodes in two sets of faces
9883 * \param theSide1 - first face set
9884 * \param theSide2 - second first face
9885 * \param theFirstNode1 - a boundary node of set 1
9886 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9887 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9888 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9889 * \param nReplaceMap - output map of corresponding nodes
9890 * \return bool - is a success or not
9892 //================================================================================
9895 //#define DEBUG_MATCHING_NODES
9898 SMESH_MeshEditor::Sew_Error
9899 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9900 set<const SMDS_MeshElement*>& theSide2,
9901 const SMDS_MeshNode* theFirstNode1,
9902 const SMDS_MeshNode* theFirstNode2,
9903 const SMDS_MeshNode* theSecondNode1,
9904 const SMDS_MeshNode* theSecondNode2,
9905 TNodeNodeMap & nReplaceMap)
9907 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9909 nReplaceMap.clear();
9910 //if ( theFirstNode1 != theFirstNode2 )
9911 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9912 //if ( theSecondNode1 != theSecondNode2 )
9913 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9915 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9916 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9918 list< NLink > linkList[2];
9919 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9920 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9922 // loop on links in linkList; find faces by links and append links
9923 // of the found faces to linkList
9924 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9925 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9926 NLink link[] = { *linkIt[0], *linkIt[1] };
9927 if ( linkSet.find( link[0] ) == linkSet.end() )
9930 // by links, find faces in the face sets,
9931 // and find indices of link nodes in the found faces;
9932 // in a face set, there is only one or no face sharing a link
9933 // ---------------------------------------------------------------
9935 const SMDS_MeshElement* face[] = { 0, 0 };
9936 list<const SMDS_MeshNode*> notLinkNodes[2];
9937 //bool reverse[] = { false, false }; // order of notLinkNodes
9939 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9941 const SMDS_MeshNode* n1 = link[iSide].first;
9942 const SMDS_MeshNode* n2 = link[iSide].second;
9943 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9944 set< const SMDS_MeshElement* > facesOfNode1;
9945 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9947 // during a loop of the first node, we find all faces around n1,
9948 // during a loop of the second node, we find one face sharing both n1 and n2
9949 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9950 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9951 while ( fIt->more() ) { // loop on faces sharing a node
9952 const SMDS_MeshElement* f = fIt->next();
9953 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9954 ! facesOfNode1.insert( f ).second ) // f encounters twice
9956 if ( face[ iSide ] ) {
9957 MESSAGE( "2 faces per link " );
9958 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9961 faceSet->erase( f );
9963 // get not link nodes
9964 int nbN = f->NbNodes();
9965 if ( f->IsQuadratic() )
9967 nbNodes[ iSide ] = nbN;
9968 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9969 int i1 = f->GetNodeIndex( n1 );
9970 int i2 = f->GetNodeIndex( n2 );
9971 int iEnd = nbN, iBeg = -1, iDelta = 1;
9972 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9974 std::swap( iEnd, iBeg ); iDelta = -1;
9979 if ( i == iEnd ) i = iBeg + iDelta;
9980 if ( i == i1 ) break;
9981 nodes.push_back ( f->GetNode( i ) );
9987 // check similarity of elements of the sides
9988 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9989 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9990 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9991 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9994 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9998 // set nodes to merge
9999 // -------------------
10001 if ( face[0] && face[1] ) {
10002 if ( nbNodes[0] != nbNodes[1] ) {
10003 MESSAGE("Diff nb of face nodes");
10004 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10006 #ifdef DEBUG_MATCHING_NODES
10007 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10008 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10009 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10011 int nbN = nbNodes[0];
10013 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10014 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10015 for ( int i = 0 ; i < nbN - 2; ++i ) {
10016 #ifdef DEBUG_MATCHING_NODES
10017 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10019 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10023 // add other links of the face 1 to linkList
10024 // -----------------------------------------
10026 const SMDS_MeshElement* f0 = face[0];
10027 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10028 for ( int i = 0; i < nbN; i++ )
10030 const SMDS_MeshNode* n2 = f0->GetNode( i );
10031 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10032 linkSet.insert( SMESH_TLink( n1, n2 ));
10033 if ( !iter_isnew.second ) { // already in a set: no need to process
10034 linkSet.erase( iter_isnew.first );
10036 else // new in set == encountered for the first time: add
10038 #ifdef DEBUG_MATCHING_NODES
10039 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10040 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10042 linkList[0].push_back ( NLink( n1, n2 ));
10043 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10048 } // loop on link lists
10053 namespace // automatically find theAffectedElems for DoubleNodes()
10055 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10057 //--------------------------------------------------------------------------------
10058 // Nodes shared by adjacent FissureBorder's.
10059 // 1 node if FissureBorder separates faces
10060 // 2 nodes if FissureBorder separates volumes
10063 const SMDS_MeshNode* _nodes[2];
10066 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10070 _nbNodes = bool( n1 ) + bool( n2 );
10071 if ( _nbNodes == 2 && n1 > n2 )
10072 std::swap( _nodes[0], _nodes[1] );
10074 bool operator<( const SubBorder& other ) const
10076 for ( int i = 0; i < _nbNodes; ++i )
10078 if ( _nodes[i] < other._nodes[i] ) return true;
10079 if ( _nodes[i] > other._nodes[i] ) return false;
10085 //--------------------------------------------------------------------------------
10086 // Map a SubBorder to all FissureBorder it bounds
10087 struct FissureBorder;
10088 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10089 typedef TBorderLinks::iterator TMappedSub;
10091 //--------------------------------------------------------------------------------
10093 * \brief Element border (volume facet or face edge) at a fissure
10095 struct FissureBorder
10097 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10098 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10100 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10101 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10103 FissureBorder( FissureBorder && from ) // move constructor
10105 std::swap( _nodes, from._nodes );
10106 std::swap( _sortedNodes, from._sortedNodes );
10107 _elems[0] = from._elems[0];
10108 _elems[1] = from._elems[1];
10111 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10112 std::vector< const SMDS_MeshElement* > & adjElems)
10113 : _nodes( elemToDuplicate->NbCornerNodes() )
10115 for ( size_t i = 0; i < _nodes.size(); ++i )
10116 _nodes[i] = elemToDuplicate->GetNode( i );
10118 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10119 findAdjacent( type, adjElems );
10122 FissureBorder( const SMDS_MeshNode** nodes,
10123 const size_t nbNodes,
10124 const SMDSAbs_ElementType adjElemsType,
10125 std::vector< const SMDS_MeshElement* > & adjElems)
10126 : _nodes( nodes, nodes + nbNodes )
10128 findAdjacent( adjElemsType, adjElems );
10131 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10132 std::vector< const SMDS_MeshElement* > & adjElems)
10134 _elems[0] = _elems[1] = 0;
10136 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10137 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10138 _elems[i] = adjElems[i];
10141 bool operator<( const FissureBorder& other ) const
10143 return GetSortedNodes() < other.GetSortedNodes();
10146 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10148 if ( _sortedNodes.empty() && !_nodes.empty() )
10150 FissureBorder* me = const_cast<FissureBorder*>( this );
10151 me->_sortedNodes = me->_nodes;
10152 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10154 return _sortedNodes;
10157 size_t NbSub() const
10159 return _nodes.size();
10162 SubBorder Sub(size_t i) const
10164 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10167 void AddSelfTo( TBorderLinks& borderLinks )
10169 _mappedSubs.resize( NbSub() );
10170 for ( size_t i = 0; i < NbSub(); ++i )
10172 TBorderLinks::iterator s2b =
10173 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10174 s2b->second.push_back( this );
10175 _mappedSubs[ i ] = s2b;
10184 const SMDS_MeshElement* GetMarkedElem() const
10186 if ( _nodes.empty() ) return 0; // cleared
10187 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10188 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10192 gp_XYZ GetNorm() const // normal to the border
10195 if ( _nodes.size() == 2 )
10197 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10198 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10200 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10203 gp_XYZ bordDir( SMESH_NodeXYZ( this->_nodes[0] ) - SMESH_NodeXYZ( this->_nodes[1] ));
10204 norm = bordDir ^ avgNorm;
10208 SMESH_NodeXYZ p0( _nodes[0] );
10209 SMESH_NodeXYZ p1( _nodes[1] );
10210 SMESH_NodeXYZ p2( _nodes[2] );
10211 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10213 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10219 void ChooseSide() // mark an _elem located at positive side of fissure
10221 _elems[0]->setIsMarked( true );
10222 gp_XYZ norm = GetNorm();
10223 double maxX = norm.Coord(1);
10224 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10225 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10228 _elems[0]->setIsMarked( false );
10229 _elems[1]->setIsMarked( true );
10233 }; // struct FissureBorder
10235 //--------------------------------------------------------------------------------
10237 * \brief Classifier of elements at fissure edge
10239 class FissureNormal
10241 std::vector< gp_XYZ > _normals;
10245 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10248 _normals.reserve(2);
10249 _normals.push_back( bord.GetNorm() );
10250 if ( _normals.size() == 2 )
10251 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10254 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10257 switch ( _normals.size() ) {
10260 isIn = !isOut( n, _normals[0], elem );
10265 bool in1 = !isOut( n, _normals[0], elem );
10266 bool in2 = !isOut( n, _normals[1], elem );
10267 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10274 //================================================================================
10276 * \brief Classify an element by a plane passing through a node
10278 //================================================================================
10280 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10282 SMESH_NodeXYZ p = n;
10284 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10286 SMESH_NodeXYZ pi = elem->GetNode( i );
10287 sumDot += norm * ( pi - p );
10289 return sumDot < -1e-100;
10292 //================================================================================
10294 * \brief Find FissureBorder's by nodes to duplicate
10296 //================================================================================
10298 void findFissureBorders( const TIDSortedElemSet& theNodes,
10299 std::vector< FissureBorder > & theFissureBorders )
10301 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10302 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10304 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10305 if ( n->NbInverseElements( elemType ) == 0 )
10307 elemType = SMDSAbs_Face;
10308 if ( n->NbInverseElements( elemType ) == 0 )
10311 // unmark elements touching the fissure
10312 for ( ; nIt != theNodes.end(); ++nIt )
10313 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10315 // loop on elements touching the fissure to get their borders belonging to the fissure
10316 std::set< FissureBorder > fissureBorders;
10317 std::vector< const SMDS_MeshElement* > adjElems;
10318 std::vector< const SMDS_MeshNode* > nodes;
10319 SMDS_VolumeTool volTool;
10320 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10322 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10323 while ( invIt->more() )
10325 const SMDS_MeshElement* eInv = invIt->next();
10326 if ( eInv->isMarked() ) continue;
10327 eInv->setIsMarked( true );
10329 if ( elemType == SMDSAbs_Volume )
10331 volTool.Set( eInv );
10332 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10333 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10335 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10336 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10338 bool allOnFissure = true;
10339 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10340 if (( allOnFissure = theNodes.count( nn[ iN ])))
10341 nodes.push_back( nn[ iN ]);
10342 if ( allOnFissure )
10343 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10344 elemType, adjElems )));
10347 else // elemType == SMDSAbs_Face
10349 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10350 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10351 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10353 nn[1] = eInv->GetNode( iN );
10354 onFissure1 = theNodes.count( nn[1] );
10355 if ( onFissure0 && onFissure1 )
10356 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10358 onFissure0 = onFissure1;
10364 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10365 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10366 for ( ; bord != fissureBorders.end(); ++bord )
10368 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10371 } // findFissureBorders()
10373 //================================================================================
10375 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10376 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10377 * \param [in] theNodesNot - nodes not to duplicate
10378 * \param [out] theAffectedElems - the found elements
10380 //================================================================================
10382 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10383 TIDSortedElemSet& theAffectedElems)
10385 if ( theElemsOrNodes.empty() ) return;
10387 // find FissureBorder's
10389 std::vector< FissureBorder > fissure;
10390 std::vector< const SMDS_MeshElement* > elemsByFacet;
10392 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10393 if ( (*elIt)->GetType() == SMDSAbs_Node )
10395 findFissureBorders( theElemsOrNodes, fissure );
10399 fissure.reserve( theElemsOrNodes.size() );
10400 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10401 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10403 if ( fissure.empty() )
10406 // fill borderLinks
10408 TBorderLinks borderLinks;
10410 for ( size_t i = 0; i < fissure.size(); ++i )
10412 fissure[i].AddSelfTo( borderLinks );
10415 // get theAffectedElems
10417 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10418 for ( size_t i = 0; i < fissure.size(); ++i )
10419 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10421 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10422 false, /*markElem=*/true );
10425 std::vector<const SMDS_MeshNode *> facetNodes;
10426 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10427 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10429 // choose a side of fissure
10430 fissure[0].ChooseSide();
10431 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10433 size_t nbCheckedBorders = 0;
10434 while ( nbCheckedBorders < fissure.size() )
10436 // find a FissureBorder to treat
10437 FissureBorder* bord = 0;
10438 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10439 if ( fissure[i].GetMarkedElem() )
10440 bord = & fissure[i];
10441 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10442 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10444 bord = & fissure[i];
10445 bord->ChooseSide();
10446 theAffectedElems.insert( bord->GetMarkedElem() );
10448 if ( !bord ) return;
10449 ++nbCheckedBorders;
10451 // treat FissureBorder's linked to bord
10452 fissureNodes.clear();
10453 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10454 for ( size_t i = 0; i < bord->NbSub(); ++i )
10456 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10457 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10458 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10459 const SubBorder& sb = l2b->first;
10460 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10462 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10464 for ( int j = 0; j < sb._nbNodes; ++j )
10465 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10469 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10470 // until an elem adjacent to a neighbour FissureBorder is found
10471 facetNodes.clear();
10472 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10473 facetNodes.resize( sb._nbNodes + 1 );
10477 // check if bordElem is adjacent to a neighbour FissureBorder
10478 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10480 FissureBorder* bord2 = linkedBorders[j];
10481 if ( bord2 == bord ) continue;
10482 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10485 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10490 // find the next bordElem
10491 const SMDS_MeshElement* nextBordElem = 0;
10492 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10494 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10495 if ( fissureNodes.count( n )) continue;
10497 facetNodes[ sb._nbNodes ] = n;
10498 elemsByFacet.clear();
10499 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10501 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10502 if ( elemsByFacet[ iE ] != bordElem &&
10503 !elemsByFacet[ iE ]->isMarked() )
10505 theAffectedElems.insert( elemsByFacet[ iE ]);
10506 elemsByFacet[ iE ]->setIsMarked( true );
10507 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10508 nextBordElem = elemsByFacet[ iE ];
10512 bordElem = nextBordElem;
10514 } // while ( bordElem )
10516 linkedBorders.clear(); // not to treat this link any more
10518 } // loop on SubBorder's of a FissureBorder
10522 } // loop on FissureBorder's
10525 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10527 // mark nodes of theAffectedElems
10528 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10530 // unmark nodes of the fissure
10531 elIt = theElemsOrNodes.begin();
10532 if ( (*elIt)->GetType() == SMDSAbs_Node )
10533 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10535 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10537 std::vector< gp_XYZ > normVec;
10539 // loop on nodes of the fissure, add elements having marked nodes
10540 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10542 const SMDS_MeshElement* e = (*elIt);
10543 if ( e->GetType() != SMDSAbs_Node )
10544 e->setIsMarked( true ); // avoid adding a fissure element
10546 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10548 const SMDS_MeshNode* n = e->GetNode( iN );
10549 if ( fissEdgeNodes2Norm.count( n ))
10552 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10553 while ( invIt->more() )
10555 const SMDS_MeshElement* eInv = invIt->next();
10556 if ( eInv->isMarked() ) continue;
10557 eInv->setIsMarked( true );
10559 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10560 while( nIt->more() )
10561 if ( nIt->next()->isMarked())
10563 theAffectedElems.insert( eInv );
10564 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10565 n->setIsMarked( false );
10572 // add elements on the fissure edge
10573 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10574 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10576 const SMDS_MeshNode* edgeNode = n2N->first;
10577 const FissureNormal & normals = n2N->second;
10579 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10580 while ( invIt->more() )
10582 const SMDS_MeshElement* eInv = invIt->next();
10583 if ( eInv->isMarked() ) continue;
10584 eInv->setIsMarked( true );
10586 // classify eInv using normals
10587 bool toAdd = normals.IsIn( edgeNode, eInv );
10588 if ( toAdd ) // check if all nodes lie on the fissure edge
10590 bool notOnEdge = false;
10591 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
10592 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10597 theAffectedElems.insert( eInv );
10603 } // findAffectedElems()
10606 //================================================================================
10608 * \brief Create elements equal (on same nodes) to given ones
10609 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10610 * elements of the uppest dimension are duplicated.
10612 //================================================================================
10614 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10616 ClearLastCreated();
10617 SMESHDS_Mesh* mesh = GetMeshDS();
10619 // get an element type and an iterator over elements
10621 SMDSAbs_ElementType type = SMDSAbs_All;
10622 SMDS_ElemIteratorPtr elemIt;
10623 if ( theElements.empty() )
10625 if ( mesh->NbNodes() == 0 )
10627 // get most complex type
10628 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10629 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10630 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10632 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10633 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10636 elemIt = mesh->elementsIterator( type );
10642 //type = (*theElements.begin())->GetType();
10643 elemIt = SMESHUtils::elemSetIterator( theElements );
10646 // un-mark all elements to avoid duplicating just created elements
10647 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10649 // duplicate elements
10651 ElemFeatures elemType;
10653 vector< const SMDS_MeshNode* > nodes;
10654 while ( elemIt->more() )
10656 const SMDS_MeshElement* elem = elemIt->next();
10657 if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10658 ( elem->isMarked() ))
10661 elemType.Init( elem, /*basicOnly=*/false );
10662 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10664 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10665 newElem->setIsMarked( true );
10669 //================================================================================
10671 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10672 \param theElems - the list of elements (edges or faces) to be replicated
10673 The nodes for duplication could be found from these elements
10674 \param theNodesNot - list of nodes to NOT replicate
10675 \param theAffectedElems - the list of elements (cells and edges) to which the
10676 replicated nodes should be associated to.
10677 \return TRUE if operation has been completed successfully, FALSE otherwise
10679 //================================================================================
10681 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10682 const TIDSortedElemSet& theNodesNot,
10683 const TIDSortedElemSet& theAffectedElems )
10685 ClearLastCreated();
10687 if ( theElems.size() == 0 )
10690 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10695 TNodeNodeMap anOldNodeToNewNode;
10696 // duplicate elements and nodes
10697 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10698 // replce nodes by duplications
10699 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10703 //================================================================================
10705 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10706 \param theMeshDS - mesh instance
10707 \param theElems - the elements replicated or modified (nodes should be changed)
10708 \param theNodesNot - nodes to NOT replicate
10709 \param theNodeNodeMap - relation of old node to new created node
10710 \param theIsDoubleElem - flag os to replicate element or modify
10711 \return TRUE if operation has been completed successfully, FALSE otherwise
10713 //================================================================================
10715 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10716 const TIDSortedElemSet& theElems,
10717 const TIDSortedElemSet& theNodesNot,
10718 TNodeNodeMap& theNodeNodeMap,
10719 const bool theIsDoubleElem )
10721 // iterate through element and duplicate them (by nodes duplication)
10723 std::vector<const SMDS_MeshNode*> newNodes;
10724 ElemFeatures elemType;
10726 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10727 for ( ; elemItr != theElems.end(); ++elemItr )
10729 const SMDS_MeshElement* anElem = *elemItr;
10733 // duplicate nodes to duplicate element
10734 bool isDuplicate = false;
10735 newNodes.resize( anElem->NbNodes() );
10736 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10738 while ( anIter->more() )
10740 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10741 const SMDS_MeshNode* aNewNode = aCurrNode;
10742 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10743 if ( n2n != theNodeNodeMap.end() )
10745 aNewNode = n2n->second;
10747 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10750 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10751 copyPosition( aCurrNode, aNewNode );
10752 theNodeNodeMap[ aCurrNode ] = aNewNode;
10753 myLastCreatedNodes.push_back( aNewNode );
10755 isDuplicate |= (aCurrNode != aNewNode);
10756 newNodes[ ind++ ] = aNewNode;
10758 if ( !isDuplicate )
10761 if ( theIsDoubleElem )
10762 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10764 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10771 //================================================================================
10773 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10774 \param theNodes - identifiers of nodes to be doubled
10775 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10776 nodes. If list of element identifiers is empty then nodes are doubled but
10777 they not assigned to elements
10778 \return TRUE if operation has been completed successfully, FALSE otherwise
10780 //================================================================================
10782 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10783 const std::list< int >& theListOfModifiedElems )
10785 ClearLastCreated();
10787 if ( theListOfNodes.size() == 0 )
10790 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10794 // iterate through nodes and duplicate them
10796 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10798 std::list< int >::const_iterator aNodeIter;
10799 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10801 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10807 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10810 copyPosition( aNode, aNewNode );
10811 anOldNodeToNewNode[ aNode ] = aNewNode;
10812 myLastCreatedNodes.push_back( aNewNode );
10816 // Change nodes of elements
10818 std::vector<const SMDS_MeshNode*> aNodeArr;
10820 std::list< int >::const_iterator anElemIter;
10821 for ( anElemIter = theListOfModifiedElems.begin();
10822 anElemIter != theListOfModifiedElems.end();
10825 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10829 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10830 for( size_t i = 0; i < aNodeArr.size(); ++i )
10832 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10833 anOldNodeToNewNode.find( aNodeArr[ i ]);
10834 if ( n2n != anOldNodeToNewNode.end() )
10835 aNodeArr[ i ] = n2n->second;
10837 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10845 //================================================================================
10847 \brief Check if element located inside shape
10848 \return TRUE if IN or ON shape, FALSE otherwise
10850 //================================================================================
10852 template<class Classifier>
10853 bool isInside(const SMDS_MeshElement* theElem,
10854 Classifier& theClassifier,
10855 const double theTol)
10857 gp_XYZ centerXYZ (0, 0, 0);
10858 for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10859 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10861 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10862 theClassifier.Perform(aPnt, theTol);
10863 TopAbs_State aState = theClassifier.State();
10864 return (aState == TopAbs_IN || aState == TopAbs_ON );
10867 //================================================================================
10869 * \brief Classifier of the 3D point on the TopoDS_Face
10870 * with interaface suitable for isInside()
10872 //================================================================================
10874 struct _FaceClassifier
10876 Extrema_ExtPS _extremum;
10877 BRepAdaptor_Surface _surface;
10878 TopAbs_State _state;
10880 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10882 _extremum.Initialize( _surface,
10883 _surface.FirstUParameter(), _surface.LastUParameter(),
10884 _surface.FirstVParameter(), _surface.LastVParameter(),
10885 _surface.Tolerance(), _surface.Tolerance() );
10887 void Perform(const gp_Pnt& aPnt, double theTol)
10890 _state = TopAbs_OUT;
10891 _extremum.Perform(aPnt);
10892 if ( _extremum.IsDone() )
10893 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10894 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10896 TopAbs_State State() const
10903 //================================================================================
10905 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10906 This method is the first step of DoubleNodeElemGroupsInRegion.
10907 \param theElems - list of groups of elements (edges or faces) to be replicated
10908 \param theNodesNot - list of groups of nodes not to replicated
10909 \param theShape - shape to detect affected elements (element which geometric center
10910 located on or inside shape). If the shape is null, detection is done on faces orientations
10911 (select elements with a gravity center on the side given by faces normals).
10912 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10913 The replicated nodes should be associated to affected elements.
10915 \sa DoubleNodeElemGroupsInRegion()
10917 //================================================================================
10919 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10920 const TIDSortedElemSet& theNodesNot,
10921 const TopoDS_Shape& theShape,
10922 TIDSortedElemSet& theAffectedElems)
10924 if ( theShape.IsNull() )
10926 findAffectedElems( theElems, theAffectedElems );
10930 const double aTol = Precision::Confusion();
10931 std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10932 std::unique_ptr<_FaceClassifier> aFaceClassifier;
10933 if ( theShape.ShapeType() == TopAbs_SOLID )
10935 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10936 bsc3d->PerformInfinitePoint(aTol);
10938 else if (theShape.ShapeType() == TopAbs_FACE )
10940 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10943 // iterates on indicated elements and get elements by back references from their nodes
10944 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10945 for ( ; elemItr != theElems.end(); ++elemItr )
10947 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10948 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10949 while ( nodeItr->more() )
10951 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10952 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10954 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10955 while ( backElemItr->more() )
10957 const SMDS_MeshElement* curElem = backElemItr->next();
10958 if ( curElem && theElems.find(curElem) == theElems.end() &&
10960 isInside( curElem, *bsc3d, aTol ) :
10961 isInside( curElem, *aFaceClassifier, aTol )))
10962 theAffectedElems.insert( curElem );
10970 //================================================================================
10972 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10973 \param theElems - group of of elements (edges or faces) to be replicated
10974 \param theNodesNot - group of nodes not to replicate
10975 \param theShape - shape to detect affected elements (element which geometric center
10976 located on or inside shape).
10977 The replicated nodes should be associated to affected elements.
10978 \return TRUE if operation has been completed successfully, FALSE otherwise
10980 //================================================================================
10982 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10983 const TIDSortedElemSet& theNodesNot,
10984 const TopoDS_Shape& theShape )
10986 if ( theShape.IsNull() )
10989 const double aTol = Precision::Confusion();
10990 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10991 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
10992 if ( theShape.ShapeType() == TopAbs_SOLID )
10994 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10995 bsc3d->PerformInfinitePoint(aTol);
10997 else if (theShape.ShapeType() == TopAbs_FACE )
10999 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11002 // iterates on indicated elements and get elements by back references from their nodes
11003 TIDSortedElemSet anAffected;
11004 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11005 for ( ; elemItr != theElems.end(); ++elemItr )
11007 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11011 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11012 while ( nodeItr->more() )
11014 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11015 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11017 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11018 while ( backElemItr->more() )
11020 const SMDS_MeshElement* curElem = backElemItr->next();
11021 if ( curElem && theElems.find(curElem) == theElems.end() &&
11023 isInside( curElem, *bsc3d, aTol ) :
11024 isInside( curElem, *aFaceClassifier, aTol )))
11025 anAffected.insert( curElem );
11029 return DoubleNodes( theElems, theNodesNot, anAffected );
11033 * \brief compute an oriented angle between two planes defined by four points.
11034 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11035 * @param p0 base of the rotation axe
11036 * @param p1 extremity of the rotation axe
11037 * @param g1 belongs to the first plane
11038 * @param g2 belongs to the second plane
11040 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11042 gp_Vec vref(p0, p1);
11045 gp_Vec n1 = vref.Crossed(v1);
11046 gp_Vec n2 = vref.Crossed(v2);
11048 return n2.AngleWithRef(n1, vref);
11050 catch ( Standard_Failure& ) {
11052 return Max( v1.Magnitude(), v2.Magnitude() );
11056 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11057 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11058 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11059 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11060 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11061 * 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.
11062 * 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.
11063 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11064 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11065 * \param theElems - list of groups of volumes, where a group of volume is a set of
11066 * SMDS_MeshElements sorted by Id.
11067 * \param createJointElems - if TRUE, create the elements
11068 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11069 * the boundary between \a theDomains and the rest mesh
11070 * \return TRUE if operation has been completed successfully, FALSE otherwise
11072 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11073 bool createJointElems,
11074 bool onAllBoundaries)
11076 // MESSAGE("----------------------------------------------");
11077 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11078 // MESSAGE("----------------------------------------------");
11080 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11081 meshDS->BuildDownWardConnectivity(true);
11083 SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11085 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11086 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11087 // build the list of nodes shared by 2 or more domains, with their domain indexes
11089 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11090 std::map<int,int>celldom; // cell vtkId --> domain
11091 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11092 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11093 faceDomains.clear();
11095 cellDomains.clear();
11096 nodeDomains.clear();
11097 std::map<int,int> emptyMap;
11098 std::set<int> emptySet;
11101 //MESSAGE(".. Number of domains :"<<theElems.size());
11103 TIDSortedElemSet theRestDomElems;
11104 const int iRestDom = -1;
11105 const int idom0 = onAllBoundaries ? iRestDom : 0;
11106 const int nbDomains = theElems.size();
11108 // Check if the domains do not share an element
11109 for (int idom = 0; idom < nbDomains-1; idom++)
11111 // MESSAGE("... Check of domain #" << idom);
11112 const TIDSortedElemSet& domain = theElems[idom];
11113 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11114 for (; elemItr != domain.end(); ++elemItr)
11116 const SMDS_MeshElement* anElem = *elemItr;
11117 int idombisdeb = idom + 1 ;
11118 // check if the element belongs to a domain further in the list
11119 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11121 const TIDSortedElemSet& domainbis = theElems[idombis];
11122 if ( domainbis.count( anElem ))
11124 MESSAGE(".... Domain #" << idom);
11125 MESSAGE(".... Domain #" << idombis);
11126 throw SALOME_Exception("The domains are not disjoint.");
11133 for (int idom = 0; idom < nbDomains; idom++)
11136 // --- build a map (face to duplicate --> volume to modify)
11137 // with all the faces shared by 2 domains (group of elements)
11138 // and corresponding volume of this domain, for each shared face.
11139 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11141 //MESSAGE("... Neighbors of domain #" << idom);
11142 const TIDSortedElemSet& domain = theElems[idom];
11143 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11144 for (; elemItr != domain.end(); ++elemItr)
11146 const SMDS_MeshElement* anElem = *elemItr;
11149 int vtkId = anElem->GetVtkID();
11150 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11151 int neighborsVtkIds[NBMAXNEIGHBORS];
11152 int downIds[NBMAXNEIGHBORS];
11153 unsigned char downTypes[NBMAXNEIGHBORS];
11154 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11155 for (int n = 0; n < nbNeighbors; n++)
11157 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11158 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11159 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11162 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11164 // MESSAGE("Domain " << idombis);
11165 const TIDSortedElemSet& domainbis = theElems[idombis];
11166 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11168 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11170 DownIdType face(downIds[n], downTypes[n]);
11171 if (!faceDomains[face].count(idom))
11173 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11174 celldom[vtkId] = idom;
11175 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11179 theRestDomElems.insert( elem );
11180 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11181 celldom[neighborsVtkIds[n]] = iRestDom;
11189 //MESSAGE("Number of shared faces " << faceDomains.size());
11190 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11192 // --- explore the shared faces domain by domain,
11193 // explore the nodes of the face and see if they belong to a cell in the domain,
11194 // which has only a node or an edge on the border (not a shared face)
11196 for (int idomain = idom0; idomain < nbDomains; idomain++)
11198 //MESSAGE("Domain " << idomain);
11199 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11200 itface = faceDomains.begin();
11201 for (; itface != faceDomains.end(); ++itface)
11203 const std::map<int, int>& domvol = itface->second;
11204 if (!domvol.count(idomain))
11206 DownIdType face = itface->first;
11207 //MESSAGE(" --- face " << face.cellId);
11208 std::set<int> oldNodes;
11210 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11211 std::set<int>::iterator itn = oldNodes.begin();
11212 for (; itn != oldNodes.end(); ++itn)
11215 //MESSAGE(" node " << oldId);
11216 vtkCellLinks::Link l = (static_cast <vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11217 for (int i=0; i<l.ncells; i++)
11219 int vtkId = l.cells[i];
11220 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11221 if (!domain.count(anElem))
11223 int vtkType = grid->GetCellType(vtkId);
11224 int downId = grid->CellIdToDownId(vtkId);
11227 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11228 continue; // not OK at this stage of the algorithm:
11229 //no cells created after BuildDownWardConnectivity
11231 DownIdType aCell(downId, vtkType);
11232 cellDomains[aCell][idomain] = vtkId;
11233 celldom[vtkId] = idomain;
11234 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11240 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11241 // for each shared face, get the nodes
11242 // for each node, for each domain of the face, create a clone of the node
11244 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11245 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11246 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11248 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11249 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11250 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11252 //MESSAGE(".. Duplication of the nodes");
11253 for (int idomain = idom0; idomain < nbDomains; idomain++)
11255 itface = faceDomains.begin();
11256 for (; itface != faceDomains.end(); ++itface)
11258 const std::map<int, int>& domvol = itface->second;
11259 if (!domvol.count(idomain))
11261 DownIdType face = itface->first;
11262 //MESSAGE(" --- face " << face.cellId);
11263 std::set<int> oldNodes;
11265 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11266 std::set<int>::iterator itn = oldNodes.begin();
11267 for (; itn != oldNodes.end(); ++itn)
11270 if (nodeDomains[oldId].empty())
11272 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11273 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11275 std::map<int, int>::const_iterator itdom = domvol.begin();
11276 for (; itdom != domvol.end(); ++itdom)
11278 int idom = itdom->first;
11279 //MESSAGE(" domain " << idom);
11280 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11282 if (nodeDomains[oldId].size() >= 2) // a multiple node
11284 vector<int> orderedDoms;
11285 //MESSAGE("multiple node " << oldId);
11286 if (mutipleNodes.count(oldId))
11287 orderedDoms = mutipleNodes[oldId];
11290 map<int,int>::iterator it = nodeDomains[oldId].begin();
11291 for (; it != nodeDomains[oldId].end(); ++it)
11292 orderedDoms.push_back(it->first);
11294 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11295 //stringstream txt;
11296 //for (int i=0; i<orderedDoms.size(); i++)
11297 // txt << orderedDoms[i] << " ";
11298 //MESSAGE("orderedDoms " << txt.str());
11299 mutipleNodes[oldId] = orderedDoms;
11301 double *coords = grid->GetPoint(oldId);
11302 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11303 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11304 int newId = newNode->GetVtkID();
11305 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11306 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11313 //MESSAGE(".. Creation of elements");
11314 for (int idomain = idom0; idomain < nbDomains; idomain++)
11316 itface = faceDomains.begin();
11317 for (; itface != faceDomains.end(); ++itface)
11319 std::map<int, int> domvol = itface->second;
11320 if (!domvol.count(idomain))
11322 DownIdType face = itface->first;
11323 //MESSAGE(" --- face " << face.cellId);
11324 std::set<int> oldNodes;
11326 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11327 int nbMultipleNodes = 0;
11328 std::set<int>::iterator itn = oldNodes.begin();
11329 for (; itn != oldNodes.end(); ++itn)
11332 if (mutipleNodes.count(oldId))
11335 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11337 //MESSAGE("multiple Nodes detected on a shared face");
11338 int downId = itface->first.cellId;
11339 unsigned char cellType = itface->first.cellType;
11340 // --- shared edge or shared face ?
11341 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11344 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11345 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11346 if (mutipleNodes.count(nodes[i]))
11347 if (!mutipleNodesToFace.count(nodes[i]))
11348 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11350 else // shared face (between two volumes)
11352 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11353 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11354 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11355 for (int ie =0; ie < nbEdges; ie++)
11358 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11359 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11361 vector<int> vn0 = mutipleNodes[nodes[0]];
11362 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11364 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11365 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11366 if ( vn0[i0] == vn1[i1] )
11367 doms.push_back( vn0[ i0 ]);
11368 if ( doms.size() > 2 )
11370 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11371 double *coords = grid->GetPoint(nodes[0]);
11372 gp_Pnt p0(coords[0], coords[1], coords[2]);
11373 coords = grid->GetPoint(nodes[nbNodes - 1]);
11374 gp_Pnt p1(coords[0], coords[1], coords[2]);
11376 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11377 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11378 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11379 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11380 for ( size_t id = 0; id < doms.size(); id++ )
11382 int idom = doms[id];
11383 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11384 for ( int ivol = 0; ivol < nbvol; ivol++ )
11386 int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11387 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11388 if (domain.count(elem))
11390 const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11391 domvol[idom] = (SMDS_MeshVolume*) svol;
11392 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11393 double values[3] = { 0,0,0 };
11394 vtkIdType npts = 0;
11395 vtkIdType const *pts(nullptr);
11396 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11397 for ( vtkIdType i = 0; i < npts; ++i )
11399 double *coords = grid->GetPoint( pts[i] );
11400 for ( int j = 0; j < 3; ++j )
11401 values[j] += coords[j] / npts;
11405 gref.SetCoord( values[0], values[1], values[2] );
11406 angleDom[idom] = 0;
11410 gp_Pnt g( values[0], values[1], values[2] );
11411 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11412 //MESSAGE(" angle=" << angleDom[idom]);
11418 map<double, int> sortedDom; // sort domains by angle
11419 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11420 sortedDom[ia->second] = ia->first;
11421 vector<int> vnodes;
11423 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11425 vdom.push_back(ib->second);
11426 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11428 for (int ino = 0; ino < nbNodes; ino++)
11429 vnodes.push_back(nodes[ino]);
11430 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11439 // --- iterate on shared faces (volumes to modify, face to extrude)
11440 // get node id's of the face (id SMDS = id VTK)
11441 // create flat element with old and new nodes if requested
11443 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11444 // (domain1 X domain2) = domain1 + MAXINT*domain2
11446 std::map<int, std::map<long,int> > nodeQuadDomains;
11447 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11449 //MESSAGE(".. Creation of elements: simple junction");
11450 if (createJointElems)
11452 string joints2DName = "joints2D";
11453 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11454 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11455 string joints3DName = "joints3D";
11456 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11457 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11459 itface = faceDomains.begin();
11460 for (; itface != faceDomains.end(); ++itface)
11462 DownIdType face = itface->first;
11463 std::set<int> oldNodes;
11464 std::set<int>::iterator itn;
11466 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11468 std::map<int, int> domvol = itface->second;
11469 std::map<int, int>::iterator itdom = domvol.begin();
11470 int dom1 = itdom->first;
11471 int vtkVolId = itdom->second;
11473 int dom2 = itdom->first;
11474 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11476 stringstream grpname;
11479 grpname << dom1 << "_" << dom2;
11481 grpname << dom2 << "_" << dom1;
11482 string namegrp = grpname.str();
11483 if (!mapOfJunctionGroups.count(namegrp))
11484 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11485 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11487 sgrp->Add(vol->GetID());
11488 if (vol->GetType() == SMDSAbs_Volume)
11489 joints3DGrp->Add(vol->GetID());
11490 else if (vol->GetType() == SMDSAbs_Face)
11491 joints2DGrp->Add(vol->GetID());
11495 // --- create volumes on multiple domain intersection if requested
11496 // iterate on mutipleNodesToFace
11497 // iterate on edgesMultiDomains
11499 //MESSAGE(".. Creation of elements: multiple junction");
11500 if (createJointElems)
11502 // --- iterate on mutipleNodesToFace
11504 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11505 for (; itn != mutipleNodesToFace.end(); ++itn)
11507 int node = itn->first;
11508 vector<int> orderDom = itn->second;
11509 vector<vtkIdType> orderedNodes;
11510 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11511 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11512 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11514 stringstream grpname;
11516 grpname << 0 << "_" << 0;
11517 string namegrp = grpname.str();
11518 if (!mapOfJunctionGroups.count(namegrp))
11519 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11520 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11522 sgrp->Add(face->GetID());
11525 // --- iterate on edgesMultiDomains
11527 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11528 for (; ite != edgesMultiDomains.end(); ++ite)
11530 vector<int> nodes = ite->first;
11531 vector<int> orderDom = ite->second;
11532 vector<vtkIdType> orderedNodes;
11533 if (nodes.size() == 2)
11535 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11536 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11537 if ( orderDom.size() == 3 )
11538 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11539 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11541 for (int idom = orderDom.size()-1; idom >=0; idom--)
11542 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11543 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11545 string namegrp = "jointsMultiples";
11546 if (!mapOfJunctionGroups.count(namegrp))
11547 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11548 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11550 sgrp->Add(vol->GetID());
11554 //INFOS("Quadratic multiple joints not implemented");
11555 // TODO quadratic nodes
11560 // --- list the explicit faces and edges of the mesh that need to be modified,
11561 // i.e. faces and edges built with one or more duplicated nodes.
11562 // associate these faces or edges to their corresponding domain.
11563 // only the first domain found is kept when a face or edge is shared
11565 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11566 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11567 faceOrEdgeDom.clear();
11570 //MESSAGE(".. Modification of elements");
11571 for (int idomain = idom0; idomain < nbDomains; idomain++)
11573 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11574 for (; itnod != nodeDomains.end(); ++itnod)
11576 int oldId = itnod->first;
11577 //MESSAGE(" node " << oldId);
11578 vtkCellLinks::Link l = (static_cast< vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11579 for (int i = 0; i < l.ncells; i++)
11581 int vtkId = l.cells[i];
11582 int vtkType = grid->GetCellType(vtkId);
11583 int downId = grid->CellIdToDownId(vtkId);
11585 continue; // new cells: not to be modified
11586 DownIdType aCell(downId, vtkType);
11587 int volParents[1000];
11588 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11589 for (int j = 0; j < nbvol; j++)
11590 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11591 if (!feDom.count(vtkId))
11593 feDom[vtkId] = idomain;
11594 faceOrEdgeDom[aCell] = emptyMap;
11595 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11596 //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11597 // << " type " << vtkType << " downId " << downId);
11603 // --- iterate on shared faces (volumes to modify, face to extrude)
11604 // get node id's of the face
11605 // replace old nodes by new nodes in volumes, and update inverse connectivity
11607 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11608 for (int m=0; m<3; m++)
11610 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11611 itface = (*amap).begin();
11612 for (; itface != (*amap).end(); ++itface)
11614 DownIdType face = itface->first;
11615 std::set<int> oldNodes;
11616 std::set<int>::iterator itn;
11618 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11619 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11620 std::map<int, int> localClonedNodeIds;
11622 std::map<int, int> domvol = itface->second;
11623 std::map<int, int>::iterator itdom = domvol.begin();
11624 for (; itdom != domvol.end(); ++itdom)
11626 int idom = itdom->first;
11627 int vtkVolId = itdom->second;
11628 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11629 localClonedNodeIds.clear();
11630 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11633 if (nodeDomains[oldId].count(idom))
11635 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11636 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11639 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11644 // Remove empty groups (issue 0022812)
11645 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11646 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11648 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11649 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11652 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11653 grid->DeleteLinks();
11661 * \brief Double nodes on some external faces and create flat elements.
11662 * Flat elements are mainly used by some types of mechanic calculations.
11664 * Each group of the list must be constituted of faces.
11665 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11666 * @param theElems - list of groups of faces, where a group of faces is a set of
11667 * SMDS_MeshElements sorted by Id.
11668 * @return TRUE if operation has been completed successfully, FALSE otherwise
11670 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11672 // MESSAGE("-------------------------------------------------");
11673 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11674 // MESSAGE("-------------------------------------------------");
11676 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11678 // --- For each group of faces
11679 // duplicate the nodes, create a flat element based on the face
11680 // replace the nodes of the faces by their clones
11682 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11683 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11684 clonedNodes.clear();
11685 intermediateNodes.clear();
11686 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11687 mapOfJunctionGroups.clear();
11689 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11691 const TIDSortedElemSet& domain = theElems[idom];
11692 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11693 for ( ; elemItr != domain.end(); ++elemItr )
11695 const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11698 // MESSAGE("aFace=" << aFace->GetID());
11699 bool isQuad = aFace->IsQuadratic();
11700 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11702 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11704 SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11705 while (nodeIt->more())
11707 const SMDS_MeshNode* node = nodeIt->next();
11708 bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11710 ln2.push_back(node);
11712 ln0.push_back(node);
11714 const SMDS_MeshNode* clone = 0;
11715 if (!clonedNodes.count(node))
11717 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11718 copyPosition( node, clone );
11719 clonedNodes[node] = clone;
11722 clone = clonedNodes[node];
11725 ln3.push_back(clone);
11727 ln1.push_back(clone);
11729 const SMDS_MeshNode* inter = 0;
11730 if (isQuad && (!isMedium))
11732 if (!intermediateNodes.count(node))
11734 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11735 copyPosition( node, inter );
11736 intermediateNodes[node] = inter;
11739 inter = intermediateNodes[node];
11740 ln4.push_back(inter);
11744 // --- extrude the face
11746 vector<const SMDS_MeshNode*> ln;
11747 SMDS_MeshVolume* vol = 0;
11748 vtkIdType aType = aFace->GetVtkType();
11752 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11753 // MESSAGE("vol prism " << vol->GetID());
11754 ln.push_back(ln1[0]);
11755 ln.push_back(ln1[1]);
11756 ln.push_back(ln1[2]);
11759 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11760 // MESSAGE("vol hexa " << vol->GetID());
11761 ln.push_back(ln1[0]);
11762 ln.push_back(ln1[1]);
11763 ln.push_back(ln1[2]);
11764 ln.push_back(ln1[3]);
11766 case VTK_QUADRATIC_TRIANGLE:
11767 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11768 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11769 // MESSAGE("vol quad prism " << vol->GetID());
11770 ln.push_back(ln1[0]);
11771 ln.push_back(ln1[1]);
11772 ln.push_back(ln1[2]);
11773 ln.push_back(ln3[0]);
11774 ln.push_back(ln3[1]);
11775 ln.push_back(ln3[2]);
11777 case VTK_QUADRATIC_QUAD:
11778 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11779 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11780 // ln4[0], ln4[1], ln4[2], ln4[3]);
11781 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11782 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11783 ln4[0], ln4[1], ln4[2], ln4[3]);
11784 // MESSAGE("vol quad hexa " << vol->GetID());
11785 ln.push_back(ln1[0]);
11786 ln.push_back(ln1[1]);
11787 ln.push_back(ln1[2]);
11788 ln.push_back(ln1[3]);
11789 ln.push_back(ln3[0]);
11790 ln.push_back(ln3[1]);
11791 ln.push_back(ln3[2]);
11792 ln.push_back(ln3[3]);
11802 stringstream grpname;
11805 string namegrp = grpname.str();
11806 if (!mapOfJunctionGroups.count(namegrp))
11807 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11808 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11810 sgrp->Add(vol->GetID());
11813 // --- modify the face
11815 const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11822 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11823 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11824 * groups of faces to remove inside the object, (idem edges).
11825 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11827 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11828 const TopoDS_Shape& theShape,
11829 SMESH_NodeSearcher* theNodeSearcher,
11830 const char* groupName,
11831 std::vector<double>& nodesCoords,
11832 std::vector<std::vector<int> >& listOfListOfNodes)
11834 // MESSAGE("--------------------------------");
11835 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11836 // MESSAGE("--------------------------------");
11838 // --- zone of volumes to remove is given :
11839 // 1 either by a geom shape (one or more vertices) and a radius,
11840 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11841 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11842 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11843 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11844 // defined by it's name.
11846 SMESHDS_GroupBase* groupDS = 0;
11847 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11848 while ( groupIt->more() )
11851 SMESH_Group * group = groupIt->next();
11852 if ( !group ) continue;
11853 groupDS = group->GetGroupDS();
11854 if ( !groupDS || groupDS->IsEmpty() ) continue;
11855 std::string grpName = group->GetName();
11856 //MESSAGE("grpName=" << grpName);
11857 if (grpName == groupName)
11863 bool isNodeGroup = false;
11864 bool isNodeCoords = false;
11867 if (groupDS->GetType() != SMDSAbs_Node)
11869 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11872 if (nodesCoords.size() > 0)
11873 isNodeCoords = true; // a list o nodes given by their coordinates
11874 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11876 // --- define groups to build
11878 // --- group of SMDS volumes
11879 string grpvName = groupName;
11880 grpvName += "_vol";
11881 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11884 MESSAGE("group not created " << grpvName);
11887 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11889 // --- group of SMDS faces on the skin
11890 string grpsName = groupName;
11891 grpsName += "_skin";
11892 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11895 MESSAGE("group not created " << grpsName);
11898 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11900 // --- group of SMDS faces internal (several shapes)
11901 string grpiName = groupName;
11902 grpiName += "_internalFaces";
11903 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11906 MESSAGE("group not created " << grpiName);
11909 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11911 // --- group of SMDS faces internal (several shapes)
11912 string grpeiName = groupName;
11913 grpeiName += "_internalEdges";
11914 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11917 MESSAGE("group not created " << grpeiName);
11920 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11922 // --- build downward connectivity
11924 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11925 meshDS->BuildDownWardConnectivity(true);
11926 SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11928 // --- set of volumes detected inside
11930 std::set<int> setOfInsideVol;
11931 std::set<int> setOfVolToCheck;
11933 std::vector<gp_Pnt> gpnts;
11936 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11938 //MESSAGE("group of nodes provided");
11939 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11940 while ( elemIt->more() )
11942 const SMDS_MeshElement* elem = elemIt->next();
11945 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11948 SMDS_MeshElement* vol = 0;
11949 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11950 while (volItr->more())
11952 vol = (SMDS_MeshElement*)volItr->next();
11953 setOfInsideVol.insert(vol->GetVtkID());
11954 sgrp->Add(vol->GetID());
11958 else if (isNodeCoords)
11960 //MESSAGE("list of nodes coordinates provided");
11963 while ( i < nodesCoords.size()-2 )
11965 double x = nodesCoords[i++];
11966 double y = nodesCoords[i++];
11967 double z = nodesCoords[i++];
11968 gp_Pnt p = gp_Pnt(x, y ,z);
11969 gpnts.push_back(p);
11970 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11974 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11976 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11977 TopTools_IndexedMapOfShape vertexMap;
11978 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11979 gp_Pnt p = gp_Pnt(0,0,0);
11980 if (vertexMap.Extent() < 1)
11983 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11985 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11986 p = BRep_Tool::Pnt(vertex);
11987 gpnts.push_back(p);
11988 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11992 if (gpnts.size() > 0)
11994 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11995 //MESSAGE("startNode->nodeId " << nodeId);
11997 double radius2 = radius*radius;
11998 //MESSAGE("radius2 " << radius2);
12000 // --- volumes on start node
12002 setOfVolToCheck.clear();
12003 SMDS_MeshElement* startVol = 0;
12004 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12005 while (volItr->more())
12007 startVol = (SMDS_MeshElement*)volItr->next();
12008 setOfVolToCheck.insert(startVol->GetVtkID());
12010 if (setOfVolToCheck.empty())
12012 MESSAGE("No volumes found");
12016 // --- starting with central volumes then their neighbors, check if they are inside
12017 // or outside the domain, until no more new neighbor volume is inside.
12018 // Fill the group of inside volumes
12020 std::map<int, double> mapOfNodeDistance2;
12021 mapOfNodeDistance2.clear();
12022 std::set<int> setOfOutsideVol;
12023 while (!setOfVolToCheck.empty())
12025 std::set<int>::iterator it = setOfVolToCheck.begin();
12027 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12028 bool volInside = false;
12029 vtkIdType npts = 0;
12030 vtkIdType const *pts(nullptr);
12031 grid->GetCellPoints(vtkId, npts, pts);
12032 for (int i=0; i<npts; i++)
12034 double distance2 = 0;
12035 if (mapOfNodeDistance2.count(pts[i]))
12037 distance2 = mapOfNodeDistance2[pts[i]];
12038 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12042 double *coords = grid->GetPoint(pts[i]);
12043 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12045 for ( size_t j = 0; j < gpnts.size(); j++ )
12047 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12048 if (d2 < distance2)
12051 if (distance2 < radius2)
12055 mapOfNodeDistance2[pts[i]] = distance2;
12056 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12058 if (distance2 < radius2)
12060 volInside = true; // one or more nodes inside the domain
12061 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12067 setOfInsideVol.insert(vtkId);
12068 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12069 int neighborsVtkIds[NBMAXNEIGHBORS];
12070 int downIds[NBMAXNEIGHBORS];
12071 unsigned char downTypes[NBMAXNEIGHBORS];
12072 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12073 for (int n = 0; n < nbNeighbors; n++)
12074 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12075 setOfVolToCheck.insert(neighborsVtkIds[n]);
12079 setOfOutsideVol.insert(vtkId);
12080 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12082 setOfVolToCheck.erase(vtkId);
12086 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12087 // If yes, add the volume to the inside set
12089 bool addedInside = true;
12090 std::set<int> setOfVolToReCheck;
12091 while (addedInside)
12093 //MESSAGE(" --------------------------- re check");
12094 addedInside = false;
12095 std::set<int>::iterator itv = setOfInsideVol.begin();
12096 for (; itv != setOfInsideVol.end(); ++itv)
12099 int neighborsVtkIds[NBMAXNEIGHBORS];
12100 int downIds[NBMAXNEIGHBORS];
12101 unsigned char downTypes[NBMAXNEIGHBORS];
12102 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12103 for (int n = 0; n < nbNeighbors; n++)
12104 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12105 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12107 setOfVolToCheck = setOfVolToReCheck;
12108 setOfVolToReCheck.clear();
12109 while (!setOfVolToCheck.empty())
12111 std::set<int>::iterator it = setOfVolToCheck.begin();
12113 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12115 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12116 int countInside = 0;
12117 int neighborsVtkIds[NBMAXNEIGHBORS];
12118 int downIds[NBMAXNEIGHBORS];
12119 unsigned char downTypes[NBMAXNEIGHBORS];
12120 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12121 for (int n = 0; n < nbNeighbors; n++)
12122 if (setOfInsideVol.count(neighborsVtkIds[n]))
12124 //MESSAGE("countInside " << countInside);
12125 if (countInside > 1)
12127 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12128 setOfInsideVol.insert(vtkId);
12129 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12130 addedInside = true;
12133 setOfVolToReCheck.insert(vtkId);
12135 setOfVolToCheck.erase(vtkId);
12139 // --- map of Downward faces at the boundary, inside the global volume
12140 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12141 // fill group of SMDS faces inside the volume (when several volume shapes)
12142 // fill group of SMDS faces on the skin of the global volume (if skin)
12144 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12145 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12146 std::set<int>::iterator it = setOfInsideVol.begin();
12147 for (; it != setOfInsideVol.end(); ++it)
12150 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12151 int neighborsVtkIds[NBMAXNEIGHBORS];
12152 int downIds[NBMAXNEIGHBORS];
12153 unsigned char downTypes[NBMAXNEIGHBORS];
12154 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12155 for (int n = 0; n < nbNeighbors; n++)
12157 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12158 if (neighborDim == 3)
12160 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12162 DownIdType face(downIds[n], downTypes[n]);
12163 boundaryFaces[face] = vtkId;
12165 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12166 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12167 if (vtkFaceId >= 0)
12169 sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12170 // find also the smds edges on this face
12171 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12172 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12173 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12174 for (int i = 0; i < nbEdges; i++)
12176 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12177 if (vtkEdgeId >= 0)
12178 sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12182 else if (neighborDim == 2) // skin of the volume
12184 DownIdType face(downIds[n], downTypes[n]);
12185 skinFaces[face] = vtkId;
12186 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12187 if (vtkFaceId >= 0)
12188 sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12193 // --- identify the edges constituting the wire of each subshape on the skin
12194 // define polylines with the nodes of edges, equivalent to wires
12195 // project polylines on subshapes, and partition, to get geom faces
12197 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12198 std::set<int> emptySet;
12200 std::set<int> shapeIds;
12202 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12203 while (itelem->more())
12205 const SMDS_MeshElement *elem = itelem->next();
12206 int shapeId = elem->getshapeId();
12207 int vtkId = elem->GetVtkID();
12208 if (!shapeIdToVtkIdSet.count(shapeId))
12210 shapeIdToVtkIdSet[shapeId] = emptySet;
12211 shapeIds.insert(shapeId);
12213 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12216 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12217 std::set<DownIdType, DownIdCompare> emptyEdges;
12218 emptyEdges.clear();
12220 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12221 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12223 int shapeId = itShape->first;
12224 //MESSAGE(" --- Shape ID --- "<< shapeId);
12225 shapeIdToEdges[shapeId] = emptyEdges;
12227 std::vector<int> nodesEdges;
12229 std::set<int>::iterator its = itShape->second.begin();
12230 for (; its != itShape->second.end(); ++its)
12233 //MESSAGE(" " << vtkId);
12234 int neighborsVtkIds[NBMAXNEIGHBORS];
12235 int downIds[NBMAXNEIGHBORS];
12236 unsigned char downTypes[NBMAXNEIGHBORS];
12237 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12238 for (int n = 0; n < nbNeighbors; n++)
12240 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12242 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12243 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12244 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12246 DownIdType edge(downIds[n], downTypes[n]);
12247 if (!shapeIdToEdges[shapeId].count(edge))
12249 shapeIdToEdges[shapeId].insert(edge);
12251 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12252 nodesEdges.push_back(vtkNodeId[0]);
12253 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12254 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12260 std::list<int> order;
12262 if (nodesEdges.size() > 0)
12264 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12265 nodesEdges[0] = -1;
12266 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12267 nodesEdges[1] = -1; // do not reuse this edge
12271 int nodeTofind = order.back(); // try first to push back
12273 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12274 if (nodesEdges[i] == nodeTofind)
12276 if ( i == (int) nodesEdges.size() )
12277 found = false; // no follower found on back
12280 if (i%2) // odd ==> use the previous one
12281 if (nodesEdges[i-1] < 0)
12285 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12286 nodesEdges[i-1] = -1;
12288 else // even ==> use the next one
12289 if (nodesEdges[i+1] < 0)
12293 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12294 nodesEdges[i+1] = -1;
12299 // try to push front
12301 nodeTofind = order.front(); // try to push front
12302 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12303 if ( nodesEdges[i] == nodeTofind )
12305 if ( i == (int)nodesEdges.size() )
12307 found = false; // no predecessor found on front
12310 if (i%2) // odd ==> use the previous one
12311 if (nodesEdges[i-1] < 0)
12315 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12316 nodesEdges[i-1] = -1;
12318 else // even ==> use the next one
12319 if (nodesEdges[i+1] < 0)
12323 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12324 nodesEdges[i+1] = -1;
12330 std::vector<int> nodes;
12331 nodes.push_back(shapeId);
12332 std::list<int>::iterator itl = order.begin();
12333 for (; itl != order.end(); itl++)
12335 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12336 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12338 listOfListOfNodes.push_back(nodes);
12341 // partition geom faces with blocFissure
12342 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12343 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12349 //================================================================================
12351 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12352 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12353 * \return TRUE if operation has been completed successfully, FALSE otherwise
12355 //================================================================================
12357 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12359 // iterates on volume elements and detect all free faces on them
12360 SMESHDS_Mesh* aMesh = GetMeshDS();
12364 ElemFeatures faceType( SMDSAbs_Face );
12365 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12366 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12369 const SMDS_MeshVolume* volume = vIt->next();
12370 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12371 vTool.SetExternalNormal();
12372 const int iQuad = volume->IsQuadratic();
12373 faceType.SetQuad( iQuad );
12374 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12376 if (!vTool.IsFreeFace(iface))
12379 vector<const SMDS_MeshNode *> nodes;
12380 int nbFaceNodes = vTool.NbFaceNodes(iface);
12381 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12383 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12384 nodes.push_back(faceNodes[inode]);
12386 if (iQuad) // add medium nodes
12388 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12389 nodes.push_back(faceNodes[inode]);
12390 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12391 nodes.push_back(faceNodes[8]);
12393 // add new face based on volume nodes
12394 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12396 nbExisted++; // face already exists
12400 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12405 return ( nbFree == ( nbExisted + nbCreated ));
12410 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12412 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12414 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12417 //================================================================================
12419 * \brief Creates missing boundary elements
12420 * \param elements - elements whose boundary is to be checked
12421 * \param dimension - defines type of boundary elements to create
12422 * \param group - a group to store created boundary elements in
12423 * \param targetMesh - a mesh to store created boundary elements in
12424 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12425 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12426 * boundary elements will be copied into the targetMesh
12427 * \param toAddExistingBondary - if true, not only new but also pre-existing
12428 * boundary elements will be added into the new group
12429 * \param aroundElements - if true, elements will be created on boundary of given
12430 * elements else, on boundary of the whole mesh.
12431 * \return nb of added boundary elements
12433 //================================================================================
12435 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12436 Bnd_Dimension dimension,
12437 SMESH_Group* group/*=0*/,
12438 SMESH_Mesh* targetMesh/*=0*/,
12439 bool toCopyElements/*=false*/,
12440 bool toCopyExistingBoundary/*=false*/,
12441 bool toAddExistingBondary/*= false*/,
12442 bool aroundElements/*= false*/)
12444 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12445 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12446 // hope that all elements are of the same type, do not check them all
12447 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12448 throw SALOME_Exception(LOCALIZED("wrong element type"));
12451 toCopyElements = toCopyExistingBoundary = false;
12453 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12454 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12455 int nbAddedBnd = 0;
12457 // editor adding present bnd elements and optionally holding elements to add to the group
12458 SMESH_MeshEditor* presentEditor;
12459 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12460 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12462 SMESH_MesherHelper helper( *myMesh );
12463 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12464 SMDS_VolumeTool vTool;
12465 TIDSortedElemSet avoidSet;
12466 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12469 typedef vector<const SMDS_MeshNode*> TConnectivity;
12470 TConnectivity tgtNodes;
12471 ElemFeatures elemKind( missType ), elemToCopy;
12473 vector<const SMDS_MeshElement*> presentBndElems;
12474 vector<TConnectivity> missingBndElems;
12475 vector<int> freeFacets;
12476 TConnectivity nodes, elemNodes;
12478 SMDS_ElemIteratorPtr eIt;
12479 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12480 else eIt = SMESHUtils::elemSetIterator( elements );
12482 while ( eIt->more() )
12484 const SMDS_MeshElement* elem = eIt->next();
12485 const int iQuad = elem->IsQuadratic();
12486 elemKind.SetQuad( iQuad );
12488 // ------------------------------------------------------------------------------------
12489 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12490 // ------------------------------------------------------------------------------------
12491 presentBndElems.clear();
12492 missingBndElems.clear();
12493 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12494 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12496 const SMDS_MeshElement* otherVol = 0;
12497 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12499 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12500 ( !aroundElements || elements.count( otherVol )))
12502 freeFacets.push_back( iface );
12504 if ( missType == SMDSAbs_Face )
12505 vTool.SetExternalNormal();
12506 for ( size_t i = 0; i < freeFacets.size(); ++i )
12508 int iface = freeFacets[i];
12509 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12510 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12511 if ( missType == SMDSAbs_Edge ) // boundary edges
12513 nodes.resize( 2+iQuad );
12514 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12516 for ( size_t j = 0; j < nodes.size(); ++j )
12517 nodes[ j ] = nn[ i+j ];
12518 if ( const SMDS_MeshElement* edge =
12519 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12520 presentBndElems.push_back( edge );
12522 missingBndElems.push_back( nodes );
12525 else // boundary face
12528 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12529 nodes.push_back( nn[inode] ); // add corner nodes
12531 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12532 nodes.push_back( nn[inode] ); // add medium nodes
12533 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12535 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12537 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12538 SMDSAbs_Face, /*noMedium=*/false ))
12539 presentBndElems.push_back( f );
12541 missingBndElems.push_back( nodes );
12543 if ( targetMesh != myMesh )
12545 // add 1D elements on face boundary to be added to a new mesh
12546 const SMDS_MeshElement* edge;
12547 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12550 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12552 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12553 if ( edge && avoidSet.insert( edge ).second )
12554 presentBndElems.push_back( edge );
12560 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12562 avoidSet.clear(), avoidSet.insert( elem );
12563 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12564 SMDS_MeshElement::iterator() );
12565 elemNodes.push_back( elemNodes[0] );
12566 nodes.resize( 2 + iQuad );
12567 const int nbLinks = elem->NbCornerNodes();
12568 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12570 nodes[0] = elemNodes[iN];
12571 nodes[1] = elemNodes[iN+1+iQuad];
12572 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12573 continue; // not free link
12575 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12576 if ( const SMDS_MeshElement* edge =
12577 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12578 presentBndElems.push_back( edge );
12580 missingBndElems.push_back( nodes );
12584 // ---------------------------------
12585 // 2. Add missing boundary elements
12586 // ---------------------------------
12587 if ( targetMesh != myMesh )
12588 // instead of making a map of nodes in this mesh and targetMesh,
12589 // we create nodes with same IDs.
12590 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12592 TConnectivity& srcNodes = missingBndElems[i];
12593 tgtNodes.resize( srcNodes.size() );
12594 for ( inode = 0; inode < srcNodes.size(); ++inode )
12595 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12596 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12598 /*noMedium=*/false))
12600 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12604 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12606 TConnectivity& nodes = missingBndElems[ i ];
12607 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12609 /*noMedium=*/false))
12611 SMDS_MeshElement* newElem =
12612 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12613 nbAddedBnd += bool( newElem );
12615 // try to set a new element to a shape
12616 if ( myMesh->HasShapeToMesh() )
12619 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12620 const size_t nbN = nodes.size() / (iQuad+1 );
12621 for ( inode = 0; inode < nbN && ok; ++inode )
12623 pair<int, TopAbs_ShapeEnum> i_stype =
12624 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12625 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12626 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12628 if ( ok && mediumShapes.size() > 1 )
12630 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12631 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12632 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12634 if (( ok = ( stype_i->first != stype_i_0.first )))
12635 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12636 aMesh->IndexToShape( stype_i_0.second ));
12639 if ( ok && mediumShapes.begin()->first == missShapeType )
12640 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12644 // ----------------------------------
12645 // 3. Copy present boundary elements
12646 // ----------------------------------
12647 if ( toCopyExistingBoundary )
12648 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12650 const SMDS_MeshElement* e = presentBndElems[i];
12651 tgtNodes.resize( e->NbNodes() );
12652 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12653 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12654 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12656 else // store present elements to add them to a group
12657 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12659 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12662 } // loop on given elements
12664 // ---------------------------------------------
12665 // 4. Fill group with boundary elements
12666 // ---------------------------------------------
12669 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12670 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12671 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12673 tgtEditor.myLastCreatedElems.clear();
12674 tgtEditor2.myLastCreatedElems.clear();
12676 // -----------------------
12677 // 5. Copy given elements
12678 // -----------------------
12679 if ( toCopyElements && targetMesh != myMesh )
12681 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12682 else eIt = SMESHUtils::elemSetIterator( elements );
12683 while (eIt->more())
12685 const SMDS_MeshElement* elem = eIt->next();
12686 tgtNodes.resize( elem->NbNodes() );
12687 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12688 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12689 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12691 tgtEditor.myLastCreatedElems.clear();
12697 //================================================================================
12699 * \brief Copy node position and set \a to node on the same geometry
12701 //================================================================================
12703 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12704 const SMDS_MeshNode* to )
12706 if ( !from || !to ) return;
12708 SMDS_PositionPtr pos = from->GetPosition();
12709 if ( !pos || from->getshapeId() < 1 ) return;
12711 switch ( pos->GetTypeOfPosition() )
12713 case SMDS_TOP_3DSPACE: break;
12715 case SMDS_TOP_FACE:
12717 SMDS_FacePositionPtr fPos = pos;
12718 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12719 fPos->GetUParameter(), fPos->GetVParameter() );
12722 case SMDS_TOP_EDGE:
12724 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12725 SMDS_EdgePositionPtr ePos = pos;
12726 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12729 case SMDS_TOP_VERTEX:
12731 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12734 case SMDS_TOP_UNSPEC: