1 // Copyright (C) 2007-2024 CEA, EDF, 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 <ShapeAnalysis.hxx>
66 #include <ShapeAnalysis_Curve.hxx>
67 #include <TColStd_ListOfInteger.hxx>
68 #include <TopAbs_State.hxx>
70 #include <TopExp_Explorer.hxx>
71 #include <TopTools_ListIteratorOfListOfShape.hxx>
72 #include <TopTools_ListOfShape.hxx>
73 #include <TopTools_SequenceOfShape.hxx>
75 #include <TopoDS_Edge.hxx>
76 #include <TopoDS_Face.hxx>
77 #include <TopoDS_Solid.hxx>
83 #include <gp_Trsf.hxx>
97 #include <boost/tuple/tuple.hpp>
98 #include <boost/container/flat_set.hpp>
100 #include <Standard_Failure.hxx>
101 #include <Standard_ErrorHandler.hxx>
103 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
105 #include <smIdType.hxx>
107 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
110 using namespace SMESH::Controls;
112 //=======================================================================
113 //function : SMESH_MeshEditor
115 //=======================================================================
117 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
118 :myMesh( theMesh ) // theMesh may be NULL
122 //================================================================================
124 * \brief Return mesh DS
126 //================================================================================
128 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
130 return myMesh->GetMeshDS();
134 //================================================================================
136 * \brief Clears myLastCreatedNodes and myLastCreatedElems
138 //================================================================================
140 void SMESH_MeshEditor::ClearLastCreated()
142 SMESHUtils::FreeVector( myLastCreatedElems );
143 SMESHUtils::FreeVector( myLastCreatedNodes );
146 //================================================================================
148 * \brief Initializes members by an existing element
149 * \param [in] elem - the source element
150 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
152 //================================================================================
154 SMESH_MeshEditor::ElemFeatures&
155 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
159 myType = elem->GetType();
160 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
162 myIsPoly = elem->IsPoly();
165 myIsQuad = elem->IsQuadratic();
166 if ( myType == SMDSAbs_Volume && !basicOnly )
168 myPolyhedQuantities = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
172 else if ( myType == SMDSAbs_Ball && !basicOnly )
174 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
180 //=======================================================================
184 //=======================================================================
187 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
188 const ElemFeatures& features)
190 SMDS_MeshElement* e = 0;
191 int nbnode = node.size();
192 SMESHDS_Mesh* mesh = GetMeshDS();
193 const smIdType ID = features.myID;
195 switch ( features.myType ) {
197 if ( !features.myIsPoly ) {
199 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
200 else e = mesh->AddFace (node[0], node[1], node[2] );
202 else if (nbnode == 4) {
203 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
204 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
206 else if (nbnode == 6) {
207 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
208 node[4], node[5], ID);
209 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
212 else if (nbnode == 7) {
213 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
214 node[4], node[5], node[6], ID);
215 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
216 node[4], node[5], node[6] );
218 else if (nbnode == 8) {
219 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
220 node[4], node[5], node[6], node[7], ID);
221 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
222 node[4], node[5], node[6], node[7] );
224 else if (nbnode == 9) {
225 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
226 node[4], node[5], node[6], node[7], node[8], ID);
227 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
228 node[4], node[5], node[6], node[7], node[8] );
231 else if ( !features.myIsQuad )
233 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
234 else e = mesh->AddPolygonalFace (node );
236 else if ( nbnode % 2 == 0 ) // just a protection
238 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
239 else e = mesh->AddQuadPolygonalFace (node );
244 if ( !features.myIsPoly ) {
246 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
247 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
249 else if (nbnode == 5) {
250 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
252 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
255 else if (nbnode == 6) {
256 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
257 node[4], node[5], ID);
258 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
261 else if (nbnode == 8) {
262 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
263 node[4], node[5], node[6], node[7], ID);
264 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
265 node[4], node[5], node[6], node[7] );
267 else if (nbnode == 10) {
268 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
269 node[4], node[5], node[6], node[7],
270 node[8], node[9], ID);
271 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
272 node[4], node[5], node[6], node[7],
275 else if (nbnode == 12) {
276 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
277 node[4], node[5], node[6], node[7],
278 node[8], node[9], node[10], node[11], ID);
279 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
280 node[4], node[5], node[6], node[7],
281 node[8], node[9], node[10], node[11] );
283 else if (nbnode == 13) {
284 if ( ID >= 1 ) e = mesh->AddVolumeWithID(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],
288 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
289 node[4], node[5], node[6], node[7],
290 node[8], node[9], node[10],node[11],
293 else if (nbnode == 15) {
294 if ( ID >= 1 ) e = mesh->AddVolumeWithID(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],ID);
298 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
299 node[4], node[5], node[6], node[7],
300 node[8], node[9], node[10],node[11],
301 node[12],node[13],node[14] );
303 else if (nbnode == 18) {
304 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
305 node[4], node[5], node[6], node[7],
306 node[8], node[9], node[10],node[11],
307 node[12],node[13],node[14],
308 node[15],node[16],node[17],ID );
309 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
310 node[4], node[5], node[6], node[7],
311 node[8], node[9], node[10],node[11],
312 node[12],node[13],node[14],
313 node[15],node[16],node[17] );
315 else if (nbnode == 20) {
316 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
317 node[4], node[5], node[6], node[7],
318 node[8], node[9], node[10],node[11],
319 node[12],node[13],node[14],node[15],
320 node[16],node[17],node[18],node[19],ID);
321 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
322 node[4], node[5], node[6], node[7],
323 node[8], node[9], node[10],node[11],
324 node[12],node[13],node[14],node[15],
325 node[16],node[17],node[18],node[19] );
327 else if (nbnode == 27) {
328 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
329 node[4], node[5], node[6], node[7],
330 node[8], node[9], node[10],node[11],
331 node[12],node[13],node[14],node[15],
332 node[16],node[17],node[18],node[19],
333 node[20],node[21],node[22],node[23],
334 node[24],node[25],node[26], ID);
335 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
336 node[4], node[5], node[6], node[7],
337 node[8], node[9], node[10],node[11],
338 node[12],node[13],node[14],node[15],
339 node[16],node[17],node[18],node[19],
340 node[20],node[21],node[22],node[23],
341 node[24],node[25],node[26] );
344 else if ( !features.myIsQuad )
346 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
347 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
351 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
352 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
358 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
359 else e = mesh->AddEdge (node[0], node[1] );
361 else if ( nbnode == 3 ) {
362 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
363 else e = mesh->AddEdge (node[0], node[1], node[2] );
367 case SMDSAbs_0DElement:
369 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
370 else e = mesh->Add0DElement (node[0] );
375 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
376 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
380 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
381 else e = mesh->AddBall (node[0], features.myBallDiameter );
386 if ( e ) myLastCreatedElems.push_back( e );
390 //=======================================================================
394 //=======================================================================
396 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<smIdType> & nodeIDs,
397 const ElemFeatures& features)
399 vector<const SMDS_MeshNode*> nodes;
400 nodes.reserve( nodeIDs.size() );
401 vector<smIdType>::const_iterator id = nodeIDs.begin();
402 while ( id != nodeIDs.end() ) {
403 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
404 nodes.push_back( node );
408 return AddElement( nodes, features );
411 //=======================================================================
413 //purpose : Remove a node or an element.
414 // Modify a compute state of sub-meshes which become empty
415 //=======================================================================
417 smIdType SMESH_MeshEditor::Remove (const std::list< smIdType >& theIDs,
422 SMESHDS_Mesh* aMesh = GetMeshDS();
423 set< SMESH_subMesh *> smmap;
425 smIdType removed = 0;
426 list<smIdType>::const_iterator it = theIDs.begin();
427 for ( ; it != theIDs.end(); it++ ) {
428 const SMDS_MeshElement * elem;
430 elem = aMesh->FindNode( *it );
432 elem = aMesh->FindElement( *it );
436 // Notify VERTEX sub-meshes about modification
438 const SMDS_MeshNode* node = cast2Node( elem );
439 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
440 if ( int aShapeID = node->getshapeId() )
441 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
444 // Find sub-meshes to notify about modification
445 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
446 // while ( nodeIt->more() ) {
447 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
448 // const SMDS_PositionPtr& aPosition = node->GetPosition();
449 // if ( aPosition.get() ) {
450 // if ( int aShapeID = aPosition->GetShapeId() ) {
451 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
452 // smmap.insert( sm );
459 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
461 aMesh->RemoveElement( elem );
465 // Notify sub-meshes about modification
466 if ( !smmap.empty() ) {
467 set< SMESH_subMesh *>::iterator smIt;
468 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
469 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
472 // // Check if the whole mesh becomes empty
473 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
474 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
479 //================================================================================
481 * \brief Remove a node and fill a hole appeared, by changing surrounding faces
483 //================================================================================
485 void SMESH_MeshEditor::RemoveNodeWithReconnection( const SMDS_MeshNode* node )
490 if ( node->NbInverseElements( SMDSAbs_Volume ) > 0 )
491 throw SALOME_Exception( "RemoveNodeWithReconnection() applies to 2D mesh only" );
493 // check that only triangles surround the node
494 for ( SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator( SMDSAbs_Face ); fIt->more(); )
496 const SMDS_MeshElement* face = fIt->next();
497 if ( face->GetGeomType() != SMDSGeom_TRIANGLE )
498 throw SALOME_Exception( "RemoveNodeWithReconnection() applies to triangle mesh only" );
499 if ( face->IsQuadratic() )
500 throw SALOME_Exception( "RemoveNodeWithReconnection() applies to linear mesh only" );
503 std::vector< const SMDS_MeshNode*> neighbours(2);
504 SMESH_MeshAlgos::IsOn2DBoundary( node, & neighbours );
506 bool toRemove = ( neighbours.size() > 2 ); // non-manifold ==> just remove
508 // if ( neighbours.size() == 2 ) // on boundary
510 // // check if theNode and neighbours are on a line
511 // gp_Pnt pN = SMESH_NodeXYZ( node );
512 // gp_Pnt p0 = SMESH_NodeXYZ( neighbours[0] );
513 // gp_Pnt p1 = SMESH_NodeXYZ( neighbours[1] );
514 // double dist01 = p0.Distance( p1 );
515 // double tol = 0.01 * dist01;
516 // double distN = ( gp_Vec( p0, p1 ) ^ gp_Vec( p0, pN )).Magnitude() / dist01;
517 // bool onLine = distN < tol;
518 // toRemove = !onLine;
521 if ( neighbours.empty() ) // not on boundary
523 TIDSortedElemSet linkedNodes;
524 GetLinkedNodes( node, linkedNodes, SMDSAbs_Face );
525 for ( const SMDS_MeshElement* e : linkedNodes ) neighbours.push_back( cast2Node( e ));
526 if ( neighbours.empty() )
532 this->Remove( std::list< smIdType >( 1, node->GetID() ), /*isNode=*/true );
536 // choose a node to replace by
537 const SMDS_MeshNode* nToReplace = nullptr;
538 SMESH_NodeXYZ nodeXYZ = node;
539 double minDist = Precision::Infinite();
540 for ( const SMDS_MeshNode* n : neighbours )
542 double dist = nodeXYZ.SquareDistance( n );
543 if ( dist < minDist )
550 // remove node + replace by nToReplace
551 std::list< const SMDS_MeshNode* > nodeGroup = { nToReplace, node };
552 TListOfListOfNodes nodesToMerge( 1, nodeGroup );
553 this->MergeNodes( nodesToMerge );
556 //================================================================================
558 * \brief Create 0D elements on all nodes of the given object.
559 * \param elements - Elements on whose nodes to create 0D elements; if empty,
560 * the all mesh is treated
561 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
562 * \param duplicateElements - to add one more 0D element to a node or not
564 //================================================================================
566 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
567 TIDSortedElemSet& all0DElems,
568 const bool duplicateElements )
570 SMDS_ElemIteratorPtr elemIt;
571 if ( elements.empty() )
573 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
577 elemIt = SMESHUtils::elemSetIterator( elements );
580 while ( elemIt->more() )
582 const SMDS_MeshElement* e = elemIt->next();
583 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
584 while ( nodeIt->more() )
586 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
587 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
588 if ( duplicateElements || !it0D->more() )
590 myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
591 all0DElems.insert( myLastCreatedElems.back() );
593 while ( it0D->more() )
594 all0DElems.insert( it0D->next() );
599 //=======================================================================
600 //function : FindShape
601 //purpose : Return an index of the shape theElem is on
602 // or zero if a shape not found
603 //=======================================================================
605 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
609 SMESHDS_Mesh * aMesh = GetMeshDS();
610 if ( aMesh->ShapeToMesh().IsNull() )
613 int aShapeID = theElem->getshapeId();
617 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
618 if ( sm->Contains( theElem ))
621 if ( theElem->GetType() == SMDSAbs_Node ) {
622 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
625 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
628 TopoDS_Shape aShape; // the shape a node of theElem is on
629 if ( theElem->GetType() != SMDSAbs_Node )
631 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
632 while ( nodeIt->more() ) {
633 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
634 if ((aShapeID = node->getshapeId()) > 0) {
635 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
636 if ( sm->Contains( theElem ))
638 if ( aShape.IsNull() )
639 aShape = aMesh->IndexToShape( aShapeID );
645 // None of nodes is on a proper shape,
646 // find the shape among ancestors of aShape on which a node is
647 if ( !aShape.IsNull() ) {
648 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
649 for ( ; ancIt.More(); ancIt.Next() ) {
650 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
651 if ( sm && sm->Contains( theElem ))
652 return aMesh->ShapeToIndex( ancIt.Value() );
657 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
658 while ( const SMESHDS_SubMesh* sm = smIt->next() )
659 if ( sm->Contains( theElem ))
666 //=======================================================================
667 //function : IsMedium
669 //=======================================================================
671 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
672 const SMDSAbs_ElementType typeToCheck)
674 bool isMedium = false;
675 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
676 while (it->more() && !isMedium ) {
677 const SMDS_MeshElement* elem = it->next();
678 isMedium = elem->IsMediumNode(node);
683 //=======================================================================
684 //function : shiftNodesQuadTria
685 //purpose : Shift nodes in the array corresponded to quadratic triangle
686 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
687 //=======================================================================
689 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
691 const SMDS_MeshNode* nd1 = aNodes[0];
692 aNodes[0] = aNodes[1];
693 aNodes[1] = aNodes[2];
695 const SMDS_MeshNode* nd2 = aNodes[3];
696 aNodes[3] = aNodes[4];
697 aNodes[4] = aNodes[5];
701 //=======================================================================
702 //function : getNodesFromTwoTria
704 //=======================================================================
706 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
707 const SMDS_MeshElement * theTria2,
708 vector< const SMDS_MeshNode*>& N1,
709 vector< const SMDS_MeshNode*>& N2)
711 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
712 if ( N1.size() < 6 ) return false;
713 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
714 if ( N2.size() < 6 ) return false;
716 int sames[3] = {-1,-1,-1};
728 if(nbsames!=2) return false;
730 shiftNodesQuadTria(N1);
732 shiftNodesQuadTria(N1);
735 i = sames[0] + sames[1] + sames[2];
737 shiftNodesQuadTria(N2);
739 // now we receive following N1 and N2 (using numeration as in the image below)
740 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
741 // i.e. first nodes from both arrays form a new diagonal
745 //=======================================================================
746 //function : InverseDiag
747 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
748 // but having other common link.
749 // Return False if args are improper
750 //=======================================================================
752 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
753 const SMDS_MeshElement * theTria2 )
757 if ( !theTria1 || !theTria2 ||
758 !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
759 !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
760 theTria1->GetType() != SMDSAbs_Face ||
761 theTria2->GetType() != SMDSAbs_Face )
764 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
765 (theTria2->GetEntityType() == SMDSEntity_Triangle))
767 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
768 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
772 // put nodes in array and find out indices of the same ones
773 const SMDS_MeshNode* aNodes [6];
774 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
776 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
777 while ( it->more() ) {
778 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
780 if ( i > 2 ) // theTria2
781 // find same node of theTria1
782 for ( int j = 0; j < 3; j++ )
783 if ( aNodes[ i ] == aNodes[ j ]) {
792 return false; // theTria1 is not a triangle
793 it = theTria2->nodesIterator();
795 if ( i == 6 && it->more() )
796 return false; // theTria2 is not a triangle
799 // find indices of 1,2 and of A,B in theTria1
800 int iA = -1, iB = 0, i1 = 0, i2 = 0;
801 for ( i = 0; i < 6; i++ ) {
802 if ( sameInd [ i ] == -1 ) {
807 if ( iA >= 0) iB = i;
811 // nodes 1 and 2 should not be the same
812 if ( aNodes[ i1 ] == aNodes[ i2 ] )
816 aNodes[ iA ] = aNodes[ i2 ];
818 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
820 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
821 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
825 } // end if(F1 && F2)
827 // check case of quadratic faces
828 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
829 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
831 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
832 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
836 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
837 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
845 vector< const SMDS_MeshNode* > N1;
846 vector< const SMDS_MeshNode* > N2;
847 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
849 // now we receive following N1 and N2 (using numeration as above image)
850 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
851 // i.e. first nodes from both arrays determ new diagonal
853 vector< const SMDS_MeshNode*> N1new( N1.size() );
854 vector< const SMDS_MeshNode*> N2new( N2.size() );
855 N1new.back() = N1.back(); // central node of biquadratic
856 N2new.back() = N2.back();
857 N1new[0] = N1[0]; N2new[0] = N1[0];
858 N1new[1] = N2[0]; N2new[1] = N1[1];
859 N1new[2] = N2[1]; N2new[2] = N2[0];
860 N1new[3] = N1[4]; N2new[3] = N1[3];
861 N1new[4] = N2[3]; N2new[4] = N2[5];
862 N1new[5] = N1[5]; N2new[5] = N1[4];
863 // change nodes in faces
864 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
865 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
867 // move the central node of biquadratic triangle
868 SMESH_MesherHelper helper( *GetMesh() );
869 for ( int is2nd = 0; is2nd < 2; ++is2nd )
871 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
872 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
873 if ( nodes.size() < 7 )
875 helper.SetSubShape( tria->getshapeId() );
876 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
880 xyz = ( SMESH_NodeXYZ( nodes[3] ) +
881 SMESH_NodeXYZ( nodes[4] ) +
882 SMESH_NodeXYZ( nodes[5] )) / 3.;
887 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
888 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
889 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
891 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
892 xyz = S->Value( uv.X(), uv.Y() );
893 xyz.Transform( loc );
894 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
895 nodes[6]->getshapeId() > 0 )
896 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
898 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
903 //=======================================================================
904 //function : findTriangles
905 //purpose : find triangles sharing theNode1-theNode2 link
906 //=======================================================================
908 static bool findTriangles(const SMDS_MeshNode * theNode1,
909 const SMDS_MeshNode * theNode2,
910 const SMDS_MeshElement*& theTria1,
911 const SMDS_MeshElement*& theTria2)
913 if ( !theNode1 || !theNode2 ) return false;
915 theTria1 = theTria2 = 0;
917 set< const SMDS_MeshElement* > emap;
918 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
920 const SMDS_MeshElement* elem = it->next();
921 if ( elem->NbCornerNodes() == 3 )
924 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
926 const SMDS_MeshElement* elem = it->next();
927 if ( emap.count( elem )) {
935 // theTria1 must be element with minimum ID
936 if ( theTria2->GetID() < theTria1->GetID() )
937 std::swap( theTria2, theTria1 );
945 //=======================================================================
946 //function : InverseDiag
947 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
948 // with ones built on the same 4 nodes but having other common link.
949 // Return false if proper faces not found
950 //=======================================================================
952 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
953 const SMDS_MeshNode * theNode2)
957 const SMDS_MeshElement *tr1, *tr2;
958 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
961 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
962 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
965 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
966 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
968 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
969 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
973 // put nodes in array
974 // and find indices of 1,2 and of A in tr1 and of B in tr2
975 int i, iA1 = 0, i1 = 0;
976 const SMDS_MeshNode* aNodes1 [3];
977 SMDS_ElemIteratorPtr it;
978 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
979 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
980 if ( aNodes1[ i ] == theNode1 )
981 iA1 = i; // node A in tr1
982 else if ( aNodes1[ i ] != theNode2 )
986 const SMDS_MeshNode* aNodes2 [3];
987 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
988 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
989 if ( aNodes2[ i ] == theNode2 )
990 iB2 = i; // node B in tr2
991 else if ( aNodes2[ i ] != theNode1 )
995 // nodes 1 and 2 should not be the same
996 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
1000 aNodes1[ iA1 ] = aNodes2[ i2 ];
1002 aNodes2[ iB2 ] = aNodes1[ i1 ];
1004 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
1005 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
1010 // check case of quadratic faces
1011 return InverseDiag(tr1,tr2);
1014 //=======================================================================
1015 //function : getQuadrangleNodes
1016 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
1017 // fusion of triangles tr1 and tr2 having shared link on
1018 // theNode1 and theNode2
1019 //=======================================================================
1021 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
1022 const SMDS_MeshNode * theNode1,
1023 const SMDS_MeshNode * theNode2,
1024 const SMDS_MeshElement * tr1,
1025 const SMDS_MeshElement * tr2 )
1027 if( tr1->NbNodes() != tr2->NbNodes() )
1030 // find the 4-th node to insert into tr1
1031 const SMDS_MeshNode* n4 = 0;
1032 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
1033 for ( int i = 0; !n4 && i < 3; ++i )
1035 const SMDS_MeshNode * n = cast2Node( it->next() );
1036 bool isDiag = ( n == theNode1 || n == theNode2 );
1041 // Make an array of nodes to be in a quadrangle
1042 int iNode = 0, iFirstDiag = -1;
1043 it = tr1->nodesIterator();
1044 for ( int i = 0; i < 3; ++i )
1046 const SMDS_MeshNode * n = cast2Node( it->next() );
1047 bool isDiag = ( n == theNode1 || n == theNode2 );
1049 if ( iFirstDiag < 0 )
1051 else if ( iNode - iFirstDiag == 1 )
1052 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
1054 else if ( n == n4 ) {
1055 return false; // tr1 and tr2 should not have all the same nodes
1057 theQuadNodes[ iNode++ ] = n;
1059 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
1060 theQuadNodes[ iNode ] = n4;
1065 //=======================================================================
1066 //function : DeleteDiag
1067 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
1068 // with a quadrangle built on the same 4 nodes.
1069 // Return false if proper faces not found
1070 //=======================================================================
1072 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1073 const SMDS_MeshNode * theNode2)
1077 const SMDS_MeshElement *tr1, *tr2;
1078 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1081 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
1082 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
1085 SMESHDS_Mesh * aMesh = GetMeshDS();
1087 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1088 (tr2->GetEntityType() == SMDSEntity_Triangle))
1090 const SMDS_MeshNode* aNodes [ 4 ];
1091 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1094 const SMDS_MeshElement* newElem = 0;
1095 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1096 myLastCreatedElems.push_back(newElem);
1097 AddToSameGroups( newElem, tr1, aMesh );
1098 int aShapeId = tr1->getshapeId();
1100 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1102 aMesh->RemoveElement( tr1 );
1103 aMesh->RemoveElement( tr2 );
1108 // check case of quadratic faces
1109 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1111 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1115 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1116 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1124 vector< const SMDS_MeshNode* > N1;
1125 vector< const SMDS_MeshNode* > N2;
1126 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1128 // now we receive following N1 and N2 (using numeration as above image)
1129 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1130 // i.e. first nodes from both arrays determ new diagonal
1132 const SMDS_MeshNode* aNodes[8];
1142 const SMDS_MeshElement* newElem = 0;
1143 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1144 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1145 myLastCreatedElems.push_back(newElem);
1146 AddToSameGroups( newElem, tr1, aMesh );
1147 int aShapeId = tr1->getshapeId();
1150 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1152 aMesh->RemoveElement( tr1 );
1153 aMesh->RemoveElement( tr2 );
1155 // remove middle node (9)
1156 GetMeshDS()->RemoveNode( N1[4] );
1161 //=======================================================================
1162 //function : SplitEdge
1163 //purpose : Replace each triangle bound by theNode1-theNode2 segment with
1164 // two triangles by connecting a node made on the link with a node opposite to the link.
1165 //=======================================================================
1167 void SMESH_MeshEditor::SplitEdge (const SMDS_MeshNode * theNode1,
1168 const SMDS_MeshNode * theNode2,
1173 SMESHDS_Mesh * mesh = GetMeshDS();
1175 // Get triangles and segments to divide
1177 std::vector<const SMDS_MeshNode *> diagNodes = { theNode1, theNode2 };
1178 std::vector<const SMDS_MeshElement *> foundElems;
1179 if ( !mesh->GetElementsByNodes( diagNodes, foundElems ) || foundElems.empty() )
1180 throw SALOME_Exception( SMESH_Comment("No triangle is bound by the edge ")
1181 << theNode1->GetID() << " - " << theNode2->GetID());
1183 SMESH_MesherHelper helper( *GetMesh() );
1185 for ( const SMDS_MeshElement * elem : foundElems )
1187 SMDSAbs_ElementType type = elem->GetType();
1189 case SMDSAbs_Volume:
1190 throw SALOME_Exception( "Can't split an edge of a volume");
1194 if ( elem->GetGeomType() != SMDSGeom_TRIANGLE )
1195 throw SALOME_Exception( "Can't split an edge of a face of type other than triangle");
1196 if ( elem->IsQuadratic() )
1198 helper.SetIsQuadratic( true );
1199 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( elem ));
1200 helper.SetIsBiQuadratic( elem->GetEntityType() == SMDSEntity_BiQuad_Triangle );
1205 if ( elem->IsQuadratic() )
1207 helper.SetIsQuadratic( true );
1208 helper.AddTLinks( static_cast< const SMDS_MeshEdge*>( elem ));
1217 const SMDS_MeshNode* nodeOnLink = helper.GetMediumNode( theNode1, theNode2,/*force3d=*/false );
1219 gp_Pnt newNodeXYZ = ( SMESH_NodeXYZ( theNode1 ) * ( 1 - thePosition ) +
1220 SMESH_NodeXYZ( theNode2 ) * thePosition );
1222 const TopoDS_Shape& S = mesh->IndexToShape( nodeOnLink->GetShapeID() );
1223 if ( !S.IsNull() && S.ShapeType() == TopAbs_FACE ) // find newNodeXYZ by UV on FACE
1225 Handle(ShapeAnalysis_Surface) surface = helper.GetSurface( TopoDS::Face( S ));
1226 double tol = 100 * helper.MaxTolerance( S );
1227 gp_Pnt2d uv = surface->ValueOfUV( newNodeXYZ, tol );
1228 if ( surface->Gap() < SMESH_NodeXYZ( theNode1 ).Distance( theNode2 ))
1230 newNodeXYZ = surface->Value( uv );
1231 if ( SMDS_FacePositionPtr nPos = nodeOnLink->GetPosition())
1232 nPos->SetParameters( uv.X(), uv.Y() );
1235 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE ) // find newNodeXYZ by param on EDGE
1237 mesh->MoveNode( nodeOnLink, newNodeXYZ.X(), newNodeXYZ.Y(), newNodeXYZ.Z() );
1238 double u = Precision::Infinite(), tol = 100 * helper.MaxTolerance( S ), distXYZ[4];
1239 helper.ToFixNodeParameters( true );
1240 if ( helper.CheckNodeU( TopoDS::Edge( S ), nodeOnLink, u, tol, /*force3D=*/false, distXYZ ))
1241 newNodeXYZ.SetCoord( distXYZ[1], distXYZ[2], distXYZ[3] );
1243 mesh->MoveNode( nodeOnLink, newNodeXYZ.X(), newNodeXYZ.Y(), newNodeXYZ.Z() );
1245 // Split triangles and segments
1247 std::vector<const SMDS_MeshNode *> nodes( 7 );
1248 for ( const SMDS_MeshElement * elem : foundElems )
1250 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
1251 nodes.resize( elem->NbCornerNodes() + 1 );
1252 nodes.back() = nodes[0];
1254 smIdType id = elem->GetID();
1255 int shapeID = elem->GetShapeID();
1257 const SMDS_MeshNode* centralNode = nullptr;
1258 if ( elem->GetEntityType() == SMDSEntity_BiQuad_Triangle )
1259 centralNode = elem->GetNode( 6 );
1261 mesh->RemoveFreeElement( elem, /*sm=*/0, /*fromGroups=*/false );
1263 mesh->RemoveFreeNode( centralNode, /*sm=*/0, /*fromGroups=*/true );
1265 for ( size_t i = 1; i < nodes.size(); ++i )
1267 const SMDS_MeshNode* n1 = nodes[i-1];
1268 const SMDS_MeshNode* n2 = nodes[i];
1269 const SMDS_MeshElement* newElem;
1270 if ( nodes.size() == 4 ) // triangle
1272 bool isDiag1 = ( n1 == theNode1 || n1 == theNode2 );
1273 bool isDiag2 = ( n2 == theNode1 || n2 == theNode2 );
1274 if ( isDiag1 && isDiag2 )
1277 newElem = helper.AddFace( n1, n2, nodeOnLink, id );
1281 newElem = helper.AddEdge( n1, nodeOnLink, id );
1283 myLastCreatedElems.push_back( newElem );
1284 AddToSameGroups( newElem, elem, mesh );
1286 mesh->SetMeshElementOnShape( newElem, shapeID );
1293 //=======================================================================
1294 //function : SplitFace
1295 //purpose : Split a face into triangles each formed by two nodes of the
1296 // face and a new node added at the given coordinates.
1297 //=======================================================================
1299 void SMESH_MeshEditor::SplitFace (const SMDS_MeshElement * theFace,
1307 throw SALOME_Exception("Null face given");
1308 if ( theFace->GetType() != SMDSAbs_Face )
1309 throw SALOME_Exception("Not a face given");
1311 SMESHDS_Mesh * mesh = GetMeshDS();
1313 SMESH_MesherHelper helper( *GetMesh() );
1314 if ( theFace->IsQuadratic() )
1316 helper.SetIsQuadratic( true );
1317 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( theFace ));
1319 const TopoDS_Shape& shape = mesh->IndexToShape( theFace->GetShapeID() );
1320 helper.SetSubShape( shape );
1321 helper.SetElementsOnShape( true );
1325 const SMDS_MeshNode* centralNode = nullptr;
1326 if ( theFace->GetEntityType() == SMDSEntity_BiQuad_Triangle )
1327 centralNode = theFace->GetNode( 6 );
1328 else if ( theFace->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
1329 centralNode = theFace->GetNode( 8 );
1333 helper.SetIsBiQuadratic( true );
1334 mesh->MoveNode( centralNode, theX, theY, theZ );
1337 centralNode = helper.AddNode( theX, theY, theZ );
1342 std::vector<const SMDS_MeshNode *> nodes( theFace->NbNodes() + 1 );
1343 nodes.assign( theFace->begin_nodes(), theFace->end_nodes() );
1344 nodes.resize( theFace->NbCornerNodes() + 1 );
1345 nodes.back() = nodes[0];
1347 smIdType id = theFace->GetID();
1348 int shapeID = theFace->GetShapeID();
1350 mesh->RemoveFreeElement( theFace, /*sm=*/0, /*fromGroups=*/false );
1352 for ( size_t i = 1; i < nodes.size(); ++i )
1354 const SMDS_MeshElement* newElem = helper.AddFace( nodes[i-1], nodes[i], centralNode, id );
1356 myLastCreatedElems.push_back( newElem );
1357 AddToSameGroups( newElem, theFace, mesh );
1359 mesh->SetMeshElementOnShape( newElem, shapeID );
1365 //=======================================================================
1366 //function : Reorient
1367 //purpose : Reverse theElement orientation
1368 //=======================================================================
1370 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1376 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1377 if ( !it || !it->more() )
1380 const SMDSAbs_ElementType type = theElem->GetType();
1381 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1384 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1385 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1387 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1389 MESSAGE("Warning: bad volumic element");
1392 SMDS_VolumeTool vTool( aPolyedre );
1393 const int nbFaces = vTool.NbFaces();
1394 vector<int> quantities( nbFaces );
1395 vector<const SMDS_MeshNode *> poly_nodes;
1397 // check if all facets are oriented equally
1398 bool sameOri = true;
1399 vector<int>& facetOri = quantities; // keep orientation in quantities so far
1400 for (int iface = 0; iface < nbFaces; iface++)
1402 facetOri[ iface ] = vTool.IsFaceExternal( iface );
1403 if ( facetOri[ iface ] != facetOri[ 0 ])
1407 // reverse faces of the polyhedron
1408 int neededOri = sameOri ? 1 - facetOri[0] : 1;
1409 poly_nodes.reserve( vTool.NbNodes() );
1410 for ( int iface = 0; iface < nbFaces; iface++ )
1412 int nbFaceNodes = vTool.NbFaceNodes( iface );
1413 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( iface );
1414 bool toReverse = ( facetOri[ iface ] != neededOri );
1416 quantities[ iface ] = nbFaceNodes;
1419 for ( int inode = nbFaceNodes - 1; inode >= 0; inode-- )
1420 poly_nodes.push_back( nodes[ inode ]);
1422 poly_nodes.insert( poly_nodes.end(), nodes, nodes + nbFaceNodes );
1424 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1426 else // other elements
1428 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1429 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1430 if ( interlace.empty() )
1432 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1436 SMDS_MeshCell::applyInterlace( interlace, nodes );
1438 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1443 //================================================================================
1445 * \brief Reorient faces.
1446 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1447 * \param theDirection - desired direction of normal of \a theRefFaces.
1448 * It can be (0,0,0) in order to keep orientation of \a theRefFaces.
1449 * \param theRefFaces - correctly oriented faces whose orientation defines
1450 * orientation of other faces.
1451 * \return number of reoriented faces.
1453 //================================================================================
1455 int SMESH_MeshEditor::Reorient2D( TIDSortedElemSet & theFaces,
1456 const gp_Vec& theDirection,
1457 TIDSortedElemSet & theRefFaces,
1458 bool theAllowNonManifold )
1462 if ( theFaces.empty() )
1464 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator();
1465 while ( fIt->more() )
1466 theFaces.insert( theFaces.end(), fIt->next() );
1468 if ( theFaces.empty() )
1472 // orient theRefFaces according to theDirection
1473 if ( theDirection.X() != 0 || theDirection.Y() != 0 || theDirection.Z() != 0 )
1474 for ( const SMDS_MeshElement* refFace : theRefFaces )
1477 SMESH_MeshAlgos::FaceNormal( refFace, normal, /*normalized=*/false );
1478 if ( normal * theDirection.XYZ() < 0 )
1479 nbReori += Reorient( refFace );
1482 // mark reference faces
1483 GetMeshDS()->SetAllCellsNotMarked();
1484 for ( const SMDS_MeshElement* refFace : theRefFaces )
1485 refFace->setIsMarked( true );
1487 // erase reference faces from theFaces
1488 for ( TIDSortedElemSet::iterator fIt = theFaces.begin(); fIt != theFaces.end(); )
1489 if ( (*fIt)->isMarked() )
1490 fIt = theFaces.erase( fIt );
1494 if ( theRefFaces.empty() )
1496 theRefFaces.insert( *theFaces.begin() );
1497 theFaces.erase( theFaces.begin() );
1502 // if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1503 // theFaces.erase( theFace );
1505 int nodeInd1, nodeInd2;
1506 const SMDS_MeshElement* refFace, *otherFace;
1507 vector< const SMDS_MeshElement* > facesNearLink;
1508 vector< std::pair< int, int > > nodeIndsOfFace;
1509 TIDSortedElemSet avoidSet, emptySet;
1510 NCollection_Map< SMESH_TLink, SMESH_TLink > checkedLinks;
1512 while ( !theRefFaces.empty() )
1514 auto refFaceIt = theRefFaces.begin();
1515 refFace = *refFaceIt;
1516 theRefFaces.erase( refFaceIt );
1519 avoidSet.insert( refFace );
1521 NLink link( refFace->GetNode( 0 ), nullptr );
1523 const int nbNodes = refFace->NbCornerNodes();
1524 for ( int i = 0; i < nbNodes; ++i ) // loop on links of refFace
1526 link.second = refFace->GetNode(( i+1 ) % nbNodes );
1527 bool isLinkVisited = checkedLinks.Contains( link );
1528 if ( isLinkVisited )
1530 // link has already been checked and won't be encountered more
1531 // if the group (theFaces) is manifold
1532 //checkedLinks.erase( linkIt_isNew.first );
1536 checkedLinks.Add( link );
1538 facesNearLink.clear();
1539 nodeIndsOfFace.clear();
1540 TIDSortedElemSet::iterator objFaceIt = theFaces.end();
1542 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1544 &nodeInd1, &nodeInd2 )))
1546 if (( otherFace->isMarked() ) || // ref face
1547 (( objFaceIt = theFaces.find( otherFace )) != theFaces.end() )) // object face
1549 facesNearLink.push_back( otherFace );
1550 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1552 avoidSet.insert( otherFace );
1554 if ( facesNearLink.size() > 1 )
1556 // NON-MANIFOLD mesh shell !
1557 if ( !theAllowNonManifold )
1559 throw SALOME_Exception("Non-manifold topology of groups");
1561 // select a face most co-directed with refFace,
1562 // other faces won't be visited this time
1564 SMESH_MeshAlgos::FaceNormal( refFace, NF, /*normalized=*/false );
1565 double proj, maxProj = -1;
1566 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1568 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1569 if (( proj = Abs( NF * NOF )) > maxProj )
1572 otherFace = facesNearLink[i];
1573 nodeInd1 = nodeIndsOfFace[i].first;
1574 nodeInd2 = nodeIndsOfFace[i].second;
1577 // not to visit rejected faces
1578 // for ( size_t i = 0; i < facesNearLink.size(); ++i )
1579 // if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1580 // visitedFaces.insert( facesNearLink[i] );
1582 else if ( facesNearLink.size() == 1 )
1584 otherFace = facesNearLink[0];
1585 nodeInd1 = nodeIndsOfFace.back().first;
1586 nodeInd2 = nodeIndsOfFace.back().second;
1590 // link must be reverse in otherFace if orientation of otherFace
1591 // is same as that of refFace
1592 if ( abs( nodeInd2 - nodeInd1 ) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1594 if ( otherFace->isMarked() )
1595 throw SALOME_Exception("Different orientation of reference faces");
1596 nbReori += Reorient( otherFace );
1598 if ( !otherFace->isMarked() )
1600 theRefFaces.insert( otherFace );
1601 if ( objFaceIt != theFaces.end() )
1602 theFaces.erase( objFaceIt );
1606 link.first = link.second; // reverse the link
1608 } // loop on links of refFace
1610 if ( theRefFaces.empty() && !theFaces.empty() )
1612 theRefFaces.insert( *theFaces.begin() );
1613 theFaces.erase( theFaces.begin() );
1616 } // while ( !theRefFaces.empty() )
1621 //================================================================================
1623 * \brief Reorient faces basing on orientation of adjacent volumes.
1624 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1625 * \param theVolumes - reference volumes.
1626 * \param theOutsideNormal - to orient faces to have their normal
1627 * pointing either \a outside or \a inside the adjacent volumes.
1628 * \return number of reoriented faces.
1630 //================================================================================
1632 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1633 TIDSortedElemSet & theVolumes,
1634 const bool theOutsideNormal)
1638 SMDS_ElemIteratorPtr faceIt;
1639 if ( theFaces.empty() )
1640 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1642 faceIt = SMESHUtils::elemSetIterator( theFaces );
1644 vector< const SMDS_MeshNode* > faceNodes;
1645 TIDSortedElemSet checkedVolumes;
1646 set< const SMDS_MeshNode* > faceNodesSet;
1647 SMDS_VolumeTool volumeTool;
1649 while ( faceIt->more() ) // loop on given faces
1651 const SMDS_MeshElement* face = faceIt->next();
1652 if ( face->GetType() != SMDSAbs_Face )
1655 const size_t nbCornersNodes = face->NbCornerNodes();
1656 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1658 checkedVolumes.clear();
1659 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1660 while ( vIt->more() )
1662 const SMDS_MeshElement* volume = vIt->next();
1664 if ( !checkedVolumes.insert( volume ).second )
1666 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1669 // is volume adjacent?
1670 bool allNodesCommon = true;
1671 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1672 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1673 if ( !allNodesCommon )
1676 // get nodes of a corresponding volume facet
1677 faceNodesSet.clear();
1678 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1679 volumeTool.Set( volume );
1680 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1681 if ( facetID < 0 ) continue;
1682 volumeTool.SetExternalNormal();
1683 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1685 // compare order of faceNodes and facetNodes
1686 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1688 for ( int i = 0; i < 2; ++i )
1690 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1691 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1692 if ( faceNodes[ iN ] == n )
1698 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1699 if ( isOutside != theOutsideNormal )
1700 nbReori += Reorient( face );
1702 } // loop on given faces
1707 //=======================================================================
1708 //function : getBadRate
1710 //=======================================================================
1712 static double getBadRate (const SMDS_MeshElement* theElem,
1713 SMESH::Controls::NumericalFunctorPtr& theCrit)
1715 SMESH::Controls::TSequenceOfXYZ P;
1716 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1718 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1719 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1722 //=======================================================================
1723 //function : QuadToTri
1724 //purpose : Cut quadrangles into triangles.
1725 // theCrit is used to select a diagonal to cut
1726 //=======================================================================
1728 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1729 SMESH::Controls::NumericalFunctorPtr theCrit)
1733 if ( !theCrit.get() )
1736 SMESHDS_Mesh * aMesh = GetMeshDS();
1737 Handle(Geom_Surface) surface;
1738 SMESH_MesherHelper helper( *GetMesh() );
1740 myLastCreatedElems.reserve( theElems.size() * 2 );
1742 TIDSortedElemSet::iterator itElem;
1743 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1745 const SMDS_MeshElement* elem = *itElem;
1746 if ( !elem || elem->GetType() != SMDSAbs_Face )
1748 if ( elem->NbCornerNodes() != 4 )
1751 // retrieve element nodes
1752 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1754 // compare two sets of possible triangles
1755 double aBadRate1, aBadRate2; // to what extent a set is bad
1756 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1757 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1758 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1760 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1761 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1762 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1764 const int aShapeId = FindShape( elem );
1765 const SMDS_MeshElement* newElem1 = 0;
1766 const SMDS_MeshElement* newElem2 = 0;
1768 if ( !elem->IsQuadratic() ) // split linear quadrangle
1770 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1771 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1772 if ( aBadRate1 <= aBadRate2 ) {
1773 // tr1 + tr2 is better
1774 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1775 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1778 // tr3 + tr4 is better
1779 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1780 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1783 else // split quadratic quadrangle
1785 helper.SetIsQuadratic( true );
1786 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1788 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1789 if ( aNodes.size() == 9 )
1791 helper.SetIsBiQuadratic( true );
1792 if ( aBadRate1 <= aBadRate2 )
1793 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1795 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1797 // create a new element
1798 if ( aBadRate1 <= aBadRate2 ) {
1799 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1800 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1803 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1804 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1808 // care of a new element
1810 myLastCreatedElems.push_back(newElem1);
1811 myLastCreatedElems.push_back(newElem2);
1812 AddToSameGroups( newElem1, elem, aMesh );
1813 AddToSameGroups( newElem2, elem, aMesh );
1815 // put a new triangle on the same shape
1817 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1818 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1820 aMesh->RemoveElement( elem );
1825 //=======================================================================
1827 * \brief Split each of given quadrangles into 4 triangles.
1828 * \param theElems - The faces to be split. If empty all faces are split.
1830 //=======================================================================
1832 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1835 myLastCreatedElems.reserve( theElems.size() * 4 );
1837 SMESH_MesherHelper helper( *GetMesh() );
1838 helper.SetElementsOnShape( true );
1840 // get standalone groups of faces
1841 vector< SMDS_MeshGroup* > allFaceGroups, faceGroups;
1842 for ( SMESHDS_GroupBase* grBase : GetMeshDS()->GetGroups() )
1843 if ( SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( grBase ))
1844 if ( group->GetType() == SMDSAbs_Face && !group->IsEmpty() )
1845 allFaceGroups.push_back( & group->SMDSGroup() );
1848 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1850 vector< const SMDS_MeshNode* > nodes;
1851 SMESHDS_SubMesh* subMeshDS = 0;
1853 Handle(Geom_Surface) surface;
1854 TopLoc_Location loc;
1856 SMDS_ElemIteratorPtr faceIt;
1857 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1858 else faceIt = SMESHUtils::elemSetIterator( theElems );
1860 while ( faceIt->more() )
1862 const SMDS_MeshElement* quad = faceIt->next();
1863 if ( !quad || quad->NbCornerNodes() != 4 )
1866 // get a surface the quad is on
1868 if ( quad->getshapeId() < 1 )
1871 helper.SetSubShape( 0 );
1874 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1876 helper.SetSubShape( quad->getshapeId() );
1877 if ( !helper.GetSubShape().IsNull() &&
1878 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1880 F = TopoDS::Face( helper.GetSubShape() );
1881 surface = BRep_Tool::Surface( F, loc );
1882 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1886 helper.SetSubShape( 0 );
1891 // create a central node
1893 const SMDS_MeshNode* nCentral;
1894 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1896 if ( nodes.size() == 9 )
1898 nCentral = nodes.back();
1905 for ( ; iN < nodes.size(); ++iN )
1906 xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1908 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1909 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1911 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1912 xyz[0], xyz[1], xyz[2], xyz[3],
1913 xyz[4], xyz[5], xyz[6], xyz[7] );
1917 for ( ; iN < nodes.size(); ++iN )
1918 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1920 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1921 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1923 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1924 uv[0], uv[1], uv[2], uv[3],
1925 uv[4], uv[5], uv[6], uv[7] );
1927 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1931 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1932 uv[8].X(), uv[8].Y() );
1933 myLastCreatedNodes.push_back( nCentral );
1936 helper.SetIsQuadratic ( nodes.size() > 4 );
1937 helper.SetIsBiQuadratic( nodes.size() == 9 );
1938 if ( helper.GetIsQuadratic() )
1939 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1941 // select groups to update
1943 for ( SMDS_MeshGroup* group : allFaceGroups )
1944 if ( group->Remove( quad ))
1945 faceGroups.push_back( group );
1947 // create 4 triangles
1949 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1951 for ( int i = 0; i < 4; ++i )
1953 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1956 myLastCreatedElems.push_back( tria );
1957 for ( SMDS_MeshGroup* group : faceGroups )
1963 //=======================================================================
1964 //function : BestSplit
1965 //purpose : Find better diagonal for cutting.
1966 //=======================================================================
1968 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1969 SMESH::Controls::NumericalFunctorPtr theCrit)
1976 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1979 if( theQuad->NbNodes()==4 ||
1980 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1982 // retrieve element nodes
1983 const SMDS_MeshNode* aNodes [4];
1984 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1986 //while (itN->more())
1988 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1990 // compare two sets of possible triangles
1991 double aBadRate1, aBadRate2; // to what extent a set is bad
1992 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1993 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1994 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1996 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1997 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1998 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1999 // for MaxElementLength2D functor we return minimum diagonal for splitting,
2000 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
2001 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
2002 return 1; // diagonal 1-3
2004 return 2; // diagonal 2-4
2011 // Methods of splitting volumes into tetra
2013 const int theHexTo5_1[5*4+1] =
2015 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
2017 const int theHexTo5_2[5*4+1] =
2019 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
2021 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
2023 const int theHexTo6_1[6*4+1] =
2025 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
2027 const int theHexTo6_2[6*4+1] =
2029 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
2031 const int theHexTo6_3[6*4+1] =
2033 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
2035 const int theHexTo6_4[6*4+1] =
2037 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
2039 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
2041 const int thePyraTo2_1[2*4+1] =
2043 0, 1, 2, 4, 0, 2, 3, 4, -1
2045 const int thePyraTo2_2[2*4+1] =
2047 1, 2, 3, 4, 1, 3, 0, 4, -1
2049 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
2051 const int thePentaTo3_1[3*4+1] =
2053 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
2055 const int thePentaTo3_2[3*4+1] =
2057 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
2059 const int thePentaTo3_3[3*4+1] =
2061 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
2063 const int thePentaTo3_4[3*4+1] =
2065 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
2067 const int thePentaTo3_5[3*4+1] =
2069 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
2071 const int thePentaTo3_6[3*4+1] =
2073 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
2075 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
2076 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
2078 // Methods of splitting hexahedron into prisms
2080 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
2082 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
2084 const int theHexTo4Prisms_LR[6*4+1] = // left-right
2086 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
2088 const int theHexTo4Prisms_FB[6*4+1] = // front-back
2090 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
2093 const int theHexTo2Prisms_BT_1[6*2+1] =
2095 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
2097 const int theHexTo2Prisms_BT_2[6*2+1] =
2099 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
2101 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
2103 const int theHexTo2Prisms_LR_1[6*2+1] =
2105 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
2107 const int theHexTo2Prisms_LR_2[6*2+1] =
2109 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
2111 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
2113 const int theHexTo2Prisms_FB_1[6*2+1] =
2115 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
2117 const int theHexTo2Prisms_FB_2[6*2+1] =
2119 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
2121 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
2124 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
2127 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
2128 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
2129 bool hasAdjacentVol( const SMDS_MeshElement* elem,
2130 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
2136 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
2137 bool _baryNode; //!< additional node is to be created at cell barycenter
2138 bool _ownConn; //!< to delete _connectivity in destructor
2139 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
2141 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
2142 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
2143 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
2144 TSplitMethod(const TSplitMethod &splitMethod)
2145 : _nbSplits(splitMethod._nbSplits),
2146 _nbCorners(splitMethod._nbCorners),
2147 _baryNode(splitMethod._baryNode),
2148 _ownConn(splitMethod._ownConn),
2149 _faceBaryNode(splitMethod._faceBaryNode)
2151 _connectivity = splitMethod._connectivity;
2152 const_cast<TSplitMethod&>(splitMethod)._connectivity = nullptr;
2153 const_cast<TSplitMethod&>(splitMethod)._ownConn = false;
2155 bool hasFacet( const TTriangleFacet& facet ) const
2157 if ( _nbCorners == 4 )
2159 const int* tetConn = _connectivity;
2160 for ( ; tetConn[0] >= 0; tetConn += 4 )
2161 if (( facet.contains( tetConn[0] ) +
2162 facet.contains( tetConn[1] ) +
2163 facet.contains( tetConn[2] ) +
2164 facet.contains( tetConn[3] )) == 3 )
2167 else // prism, _nbCorners == 6
2169 const int* prismConn = _connectivity;
2170 for ( ; prismConn[0] >= 0; prismConn += 6 )
2172 if (( facet.contains( prismConn[0] ) &&
2173 facet.contains( prismConn[1] ) &&
2174 facet.contains( prismConn[2] ))
2176 ( facet.contains( prismConn[3] ) &&
2177 facet.contains( prismConn[4] ) &&
2178 facet.contains( prismConn[5] )))
2186 //=======================================================================
2188 * \brief return TSplitMethod for the given element to split into tetrahedra
2190 //=======================================================================
2192 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
2194 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2196 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
2197 // an edge and a face barycenter; tertaherdons are based on triangles and
2198 // a volume barycenter
2199 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
2201 // Find out how adjacent volumes are split
2203 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
2204 int hasAdjacentSplits = 0, maxTetConnSize = 0;
2205 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
2207 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
2208 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
2209 if ( nbNodes < 4 ) continue;
2211 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
2212 const int* nInd = vol.GetFaceNodesIndices( iF );
2215 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2216 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2217 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
2218 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
2222 int iCom = 0; // common node of triangle faces to split into
2223 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
2225 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2226 nInd[ iQ * ( (iCom+1)%nbNodes )],
2227 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2228 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2229 nInd[ iQ * ( (iCom+2)%nbNodes )],
2230 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2231 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
2233 triaSplits.push_back( t012 );
2234 triaSplits.push_back( t023 );
2239 if ( !triaSplits.empty() )
2240 hasAdjacentSplits = true;
2243 // Among variants of split method select one compliant with adjacent volumes
2245 TSplitMethod method;
2246 if ( !vol.Element()->IsPoly() && !is24TetMode )
2248 int nbVariants = 2, nbTet = 0;
2249 const int** connVariants = 0;
2250 switch ( vol.Element()->GetEntityType() )
2252 case SMDSEntity_Hexa:
2253 case SMDSEntity_Quad_Hexa:
2254 case SMDSEntity_TriQuad_Hexa:
2255 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
2256 connVariants = theHexTo5, nbTet = 5;
2258 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
2260 case SMDSEntity_Pyramid:
2261 case SMDSEntity_Quad_Pyramid:
2262 connVariants = thePyraTo2; nbTet = 2;
2264 case SMDSEntity_Penta:
2265 case SMDSEntity_Quad_Penta:
2266 case SMDSEntity_BiQuad_Penta:
2267 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
2272 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
2274 // check method compliance with adjacent tetras,
2275 // all found splits must be among facets of tetras described by this method
2276 method = TSplitMethod( nbTet, connVariants[variant] );
2277 if ( hasAdjacentSplits && method._nbSplits > 0 )
2279 bool facetCreated = true;
2280 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
2282 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
2283 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
2284 facetCreated = method.hasFacet( *facet );
2286 if ( !facetCreated )
2287 method = TSplitMethod(0); // incompatible method
2291 if ( method._nbSplits < 1 )
2293 // No standard method is applicable, use a generic solution:
2294 // each facet of a volume is split into triangles and
2295 // each of triangles and a volume barycenter form a tetrahedron.
2297 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
2299 int* connectivity = new int[ maxTetConnSize + 1 ];
2300 method._connectivity = connectivity;
2301 method._ownConn = true;
2302 method._baryNode = !isHex27; // to create central node or not
2305 int baryCenInd = vol.NbNodes() - int( isHex27 );
2306 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
2308 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
2309 const int* nInd = vol.GetFaceNodesIndices( iF );
2310 // find common node of triangle facets of tetra to create
2311 int iCommon = 0; // index in linear numeration
2312 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
2313 if ( !triaSplits.empty() )
2316 const TTriangleFacet* facet = &triaSplits.front();
2317 for ( ; iCommon < nbNodes-1 ; ++iCommon )
2318 if ( facet->contains( nInd[ iQ * iCommon ]) &&
2319 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
2322 else if ( nbNodes > 3 && !is24TetMode )
2324 // find the best method of splitting into triangles by aspect ratio
2325 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2326 map< double, int > badness2iCommon;
2327 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
2328 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2329 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
2332 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
2334 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
2335 nodes[ iQ*((iLast-1)%nbNodes)],
2336 nodes[ iQ*((iLast )%nbNodes)]);
2337 badness += getBadRate( &tria, aspectRatio );
2339 badness2iCommon.insert( make_pair( badness, iCommon ));
2341 // use iCommon with lowest badness
2342 iCommon = badness2iCommon.begin()->second;
2344 if ( iCommon >= nbNodes )
2345 iCommon = 0; // something wrong
2347 // fill connectivity of tetrahedra based on a current face
2348 int nbTet = nbNodes - 2;
2349 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2354 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2355 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2359 method._faceBaryNode[ iF ] = 0;
2360 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2363 for ( int i = 0; i < nbTet; ++i )
2365 int i1 = i, i2 = (i+1) % nbNodes;
2366 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2367 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2368 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2369 connectivity[ connSize++ ] = faceBaryCenInd;
2370 connectivity[ connSize++ ] = baryCenInd;
2375 for ( int i = 0; i < nbTet; ++i )
2377 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2378 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2379 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2380 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2381 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2382 connectivity[ connSize++ ] = baryCenInd;
2385 method._nbSplits += nbTet;
2387 } // loop on volume faces
2389 connectivity[ connSize++ ] = -1;
2391 } // end of generic solution
2395 //=======================================================================
2397 * \brief return TSplitMethod to split haxhedron into prisms
2399 //=======================================================================
2401 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2402 const int methodFlags,
2403 const int facetToSplit)
2405 TSplitMethod method;
2407 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2409 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2411 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2413 static TSplitMethod to4methods[4]; // order BT, LR, FB
2414 if ( to4methods[iF]._nbSplits == 0 )
2418 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2419 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2420 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2423 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2424 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2425 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2428 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2429 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2430 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2432 default: return to4methods[3];
2434 to4methods[iF]._nbSplits = 4;
2435 to4methods[iF]._nbCorners = 6;
2437 method = to4methods[iF];
2438 to4methods[iF]._connectivity = method._connectivity; // as copy ctor resets _connectivity
2441 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2443 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2445 const int nbVariants = 2, nbSplits = 2;
2446 const int** connVariants = 0;
2448 case 0: connVariants = theHexTo2Prisms_BT; break;
2449 case 1: connVariants = theHexTo2Prisms_LR; break;
2450 case 2: connVariants = theHexTo2Prisms_FB; break;
2451 default: return method;
2454 // look for prisms adjacent via facetToSplit and an opposite one
2455 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2457 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2458 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2459 if ( nbNodes != 4 ) return method;
2461 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2462 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2463 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2465 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2467 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2472 // there are adjacent prism
2473 for ( int variant = 0; variant < nbVariants; ++variant )
2475 // check method compliance with adjacent prisms,
2476 // the found prism facets must be among facets of prisms described by current method
2477 method._nbSplits = nbSplits;
2478 method._nbCorners = 6;
2479 method._connectivity = connVariants[ variant ];
2480 if ( method.hasFacet( *t ))
2485 // No adjacent prisms. Select a variant with a best aspect ratio.
2487 double badness[2] = { 0., 0. };
2488 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2489 const SMDS_MeshNode** nodes = vol.GetNodes();
2490 for ( int variant = 0; variant < nbVariants; ++variant )
2491 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2493 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2494 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2496 method._connectivity = connVariants[ variant ];
2497 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2498 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2499 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2501 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2504 badness[ variant ] += getBadRate( &tria, aspectRatio );
2506 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2508 method._nbSplits = nbSplits;
2509 method._nbCorners = 6;
2510 method._connectivity = connVariants[ iBetter ];
2515 //================================================================================
2517 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2519 //================================================================================
2521 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2522 const SMDSAbs_GeometryType geom ) const
2524 // find the tetrahedron including the three nodes of facet
2525 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2526 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2527 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2528 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2529 while ( volIt1->more() )
2531 const SMDS_MeshElement* v = volIt1->next();
2532 if ( v->GetGeomType() != geom )
2534 const int lastCornerInd = v->NbCornerNodes() - 1;
2535 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2536 continue; // medium node not allowed
2537 const int ind2 = v->GetNodeIndex( n2 );
2538 if ( ind2 < 0 || lastCornerInd < ind2 )
2540 const int ind3 = v->GetNodeIndex( n3 );
2541 if ( ind3 < 0 || lastCornerInd < ind3 )
2548 //=======================================================================
2550 * \brief A key of a face of volume
2552 //=======================================================================
2554 struct TVolumeFaceKey: pair< pair< smIdType, smIdType>, pair< smIdType, smIdType> >
2556 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2558 TIDSortedNodeSet sortedNodes;
2559 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2560 int nbNodes = vol.NbFaceNodes( iF );
2561 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2562 for ( int i = 0; i < nbNodes; i += iQ )
2563 sortedNodes.insert( fNodes[i] );
2564 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2565 first.first = (*(n++))->GetID();
2566 first.second = (*(n++))->GetID();
2567 second.first = (*(n++))->GetID();
2568 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2573 //=======================================================================
2574 //function : SplitVolumes
2575 //purpose : Split volume elements into tetrahedra or prisms.
2576 // If facet ID < 0, element is split into tetrahedra,
2577 // else a hexahedron is split into prisms so that the given facet is
2578 // split into triangles
2579 //=======================================================================
2581 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2582 const int theMethodFlags)
2584 SMDS_VolumeTool volTool;
2585 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2586 fHelper.ToFixNodeParameters( true );
2588 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2589 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2591 SMESH_SequenceOfElemPtr newNodes, newElems;
2593 // map face of volume to it's baricenrtic node
2594 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2596 vector<const SMDS_MeshElement* > splitVols;
2598 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2599 for ( ; elem2facet != theElems.end(); ++elem2facet )
2601 const SMDS_MeshElement* elem = elem2facet->first;
2602 const int facetToSplit = elem2facet->second;
2603 if ( elem->GetType() != SMDSAbs_Volume )
2605 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2606 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2609 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2611 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2612 getTetraSplitMethod( volTool, theMethodFlags ) :
2613 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2614 if ( splitMethod._nbSplits < 1 ) continue;
2616 // find submesh to add new tetras to
2617 if ( !subMesh || !subMesh->Contains( elem ))
2619 int shapeID = FindShape( elem );
2620 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2621 subMesh = GetMeshDS()->MeshElements( shapeID );
2624 if ( elem->IsQuadratic() )
2627 // add quadratic links to the helper
2628 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2630 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2631 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2632 for ( int iN = 0; iN < nbN; iN += iQ )
2633 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2635 helper.SetIsQuadratic( true );
2640 helper.SetIsQuadratic( false );
2642 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2643 volTool.GetNodes() + elem->NbNodes() );
2644 helper.SetElementsOnShape( true );
2645 if ( splitMethod._baryNode )
2647 // make a node at barycenter
2648 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2649 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2650 nodes.push_back( gcNode );
2651 newNodes.push_back( gcNode );
2653 if ( !splitMethod._faceBaryNode.empty() )
2655 // make or find baricentric nodes of faces
2656 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2657 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2659 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2660 volFace2BaryNode.insert
2661 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2664 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2665 newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2667 nodes.push_back( iF_n->second = f_n->second );
2672 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2673 const int* volConn = splitMethod._connectivity;
2674 if ( splitMethod._nbCorners == 4 ) // tetra
2675 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2676 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2677 nodes[ volConn[1] ],
2678 nodes[ volConn[2] ],
2679 nodes[ volConn[3] ]));
2681 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2682 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2683 nodes[ volConn[1] ],
2684 nodes[ volConn[2] ],
2685 nodes[ volConn[3] ],
2686 nodes[ volConn[4] ],
2687 nodes[ volConn[5] ]));
2689 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2691 // Split faces on sides of the split volume
2693 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2694 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2696 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2697 if ( nbNodes < 4 ) continue;
2699 // find an existing face
2700 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2701 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2702 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2703 /*noMedium=*/false))
2706 helper.SetElementsOnShape( false );
2707 vector< const SMDS_MeshElement* > triangles;
2709 // find submesh to add new triangles in
2710 if ( !fSubMesh || !fSubMesh->Contains( face ))
2712 int shapeID = FindShape( face );
2713 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2715 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2716 if ( iF_n != splitMethod._faceBaryNode.end() )
2718 const SMDS_MeshNode *baryNode = iF_n->second;
2719 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2721 const SMDS_MeshNode* n1 = fNodes[iN];
2722 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2723 const SMDS_MeshNode *n3 = baryNode;
2724 if ( !volTool.IsFaceExternal( iF ))
2726 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2728 if ( fSubMesh ) // update position of the bary node on geometry
2731 subMesh->RemoveNode( baryNode );
2732 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2733 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2734 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2736 fHelper.SetSubShape( s );
2737 gp_XY uv( 1e100, 1e100 );
2739 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2740 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2743 // node is too far from the surface
2744 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2745 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2746 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2753 // among possible triangles create ones described by split method
2754 const int* nInd = volTool.GetFaceNodesIndices( iF );
2755 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2756 int iCom = 0; // common node of triangle faces to split into
2757 list< TTriangleFacet > facets;
2758 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2760 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2761 nInd[ iQ * ( (iCom+1)%nbNodes )],
2762 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2763 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2764 nInd[ iQ * ( (iCom+2)%nbNodes )],
2765 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2766 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2768 facets.push_back( t012 );
2769 facets.push_back( t023 );
2770 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2771 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2772 nInd[ iQ * ((iLast-1)%nbNodes )],
2773 nInd[ iQ * ((iLast )%nbNodes )]));
2777 list< TTriangleFacet >::iterator facet = facets.begin();
2778 if ( facet == facets.end() )
2780 for ( ; facet != facets.end(); ++facet )
2782 if ( !volTool.IsFaceExternal( iF ))
2783 swap( facet->_n2, facet->_n3 );
2784 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2785 volNodes[ facet->_n2 ],
2786 volNodes[ facet->_n3 ]));
2789 for ( size_t i = 0; i < triangles.size(); ++i )
2791 if ( !triangles[ i ]) continue;
2793 fSubMesh->AddElement( triangles[ i ]);
2794 newElems.push_back( triangles[ i ]);
2796 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2797 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2799 } // while a face based on facet nodes exists
2800 } // loop on volume faces to split them into triangles
2802 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2804 if ( geomType == SMDSEntity_TriQuad_Hexa )
2806 // remove medium nodes that could become free
2807 for ( int i = 20; i < volTool.NbNodes(); ++i )
2808 if ( volNodes[i]->NbInverseElements() == 0 )
2809 GetMeshDS()->RemoveNode( volNodes[i] );
2811 } // loop on volumes to split
2813 myLastCreatedNodes = newNodes;
2814 myLastCreatedElems = newElems;
2817 //=======================================================================
2818 //function : GetHexaFacetsToSplit
2819 //purpose : For hexahedra that will be split into prisms, finds facets to
2820 // split into triangles. Only hexahedra adjacent to the one closest
2821 // to theFacetNormal.Location() are returned.
2822 //param [in,out] theHexas - the hexahedra
2823 //param [in] theFacetNormal - facet normal
2824 //param [out] theFacets - the hexahedra and found facet IDs
2825 //=======================================================================
2827 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2828 const gp_Ax1& theFacetNormal,
2829 TFacetOfElem & theFacets)
2831 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2833 // Find a hexa closest to the location of theFacetNormal
2835 const SMDS_MeshElement* startHex;
2837 // get SMDS_ElemIteratorPtr on theHexas
2838 typedef const SMDS_MeshElement* TValue;
2839 typedef TIDSortedElemSet::iterator TSetIterator;
2840 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2841 typedef SMDS_MeshElement::GeomFilter TFilter;
2842 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2843 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2844 ( new TElemSetIter( theHexas.begin(),
2846 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2848 SMESH_ElementSearcher* searcher =
2849 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2851 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2856 throw SALOME_Exception( THIS_METHOD "startHex not found");
2859 // Select a facet of startHex by theFacetNormal
2861 SMDS_VolumeTool vTool( startHex );
2862 double norm[3], dot, maxDot = 0;
2864 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2865 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2867 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2875 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2877 // Fill theFacets starting from facetID of startHex
2879 // facets used for searching of volumes adjacent to already treated ones
2880 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2881 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2882 TFacetMap facetsToCheck;
2884 set<const SMDS_MeshNode*> facetNodes;
2885 const SMDS_MeshElement* curHex;
2887 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2891 // move in two directions from startHex via facetID
2892 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2895 int curFacet = facetID;
2896 if ( is2nd ) // do not treat startHex twice
2898 vTool.Set( curHex );
2899 if ( vTool.IsFreeFace( curFacet, &curHex ))
2905 vTool.GetFaceNodes( curFacet, facetNodes );
2906 vTool.Set( curHex );
2907 curFacet = vTool.GetFaceIndex( facetNodes );
2912 // store a facet to split
2913 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2915 theFacets.insert( make_pair( curHex, -1 ));
2918 if ( !allHex && !theHexas.count( curHex ))
2921 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2922 theFacets.insert( make_pair( curHex, curFacet ));
2923 if ( !facetIt2isNew.second )
2926 // remember not-to-split facets in facetsToCheck
2927 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2928 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2930 if ( iF == curFacet && iF == oppFacet )
2932 TVolumeFaceKey facetKey ( vTool, iF );
2933 TElemFacets elemFacet( facetIt2isNew.first, iF );
2934 pair< TFacetMap::iterator, bool > it2isnew =
2935 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2936 if ( !it2isnew.second )
2937 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2939 // pass to a volume adjacent via oppFacet
2940 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2946 // get a new curFacet
2947 vTool.GetFaceNodes( oppFacet, facetNodes );
2948 vTool.Set( curHex );
2949 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2952 } // move in two directions from startHex via facetID
2954 // Find a new startHex by facetsToCheck
2958 TFacetMap::iterator fIt = facetsToCheck.begin();
2959 while ( !startHex && fIt != facetsToCheck.end() )
2961 const TElemFacets& elemFacets = fIt->second;
2962 const SMDS_MeshElement* hex = elemFacets.first->first;
2963 int splitFacet = elemFacets.first->second;
2964 int lateralFacet = elemFacets.second;
2965 facetsToCheck.erase( fIt );
2966 fIt = facetsToCheck.begin();
2969 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2970 curHex->GetGeomType() != SMDSGeom_HEXA )
2972 if ( !allHex && !theHexas.count( curHex ))
2977 // find a facet of startHex to split
2979 set<const SMDS_MeshNode*> lateralNodes;
2980 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2981 vTool.GetFaceNodes( splitFacet, facetNodes );
2982 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2983 vTool.Set( startHex );
2984 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2986 // look for a facet of startHex having common nodes with facetNodes
2987 // but not lateralFacet
2988 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2990 if ( iF == lateralFacet )
2992 int nbCommonNodes = 0;
2993 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2994 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2995 nbCommonNodes += facetNodes.count( nn[ iN ]);
2997 if ( nbCommonNodes >= 2 )
3004 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
3006 } // while ( startHex )
3013 //================================================================================
3015 * \brief Selects nodes of several elements according to a given interlace
3016 * \param [in] srcNodes - nodes to select from
3017 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
3018 * \param [in] interlace - indices of nodes for all elements
3019 * \param [in] nbElems - nb of elements
3020 * \param [in] nbNodes - nb of nodes in each element
3021 * \param [in] mesh - the mesh
3022 * \param [out] elemQueue - a list to push elements found by the selected nodes
3023 * \param [in] type - type of elements to look for
3025 //================================================================================
3027 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
3028 vector< const SMDS_MeshNode* >* tgtNodesVec,
3029 const int* interlace,
3032 SMESHDS_Mesh* mesh = 0,
3033 list< const SMDS_MeshElement* >* elemQueue=0,
3034 SMDSAbs_ElementType type=SMDSAbs_All)
3036 for ( int iE = 0; iE < nbElems; ++iE )
3038 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
3039 const int* select = & interlace[iE*nbNodes];
3040 elemNodes.resize( nbNodes );
3041 for ( int iN = 0; iN < nbNodes; ++iN )
3042 elemNodes[iN] = srcNodes[ select[ iN ]];
3044 const SMDS_MeshElement* e;
3046 for ( int iE = 0; iE < nbElems; ++iE )
3047 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
3048 elemQueue->push_back( e );
3052 //=======================================================================
3054 * Split bi-quadratic elements into linear ones without creation of additional nodes
3055 * - bi-quadratic triangle will be split into 3 linear quadrangles;
3056 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
3057 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
3058 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
3059 * will be split in order to keep the mesh conformal.
3060 * \param elems - elements to split
3062 //=======================================================================
3064 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
3066 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
3067 vector<const SMDS_MeshElement* > splitElems;
3068 list< const SMDS_MeshElement* > elemQueue;
3069 list< const SMDS_MeshElement* >::iterator elemIt;
3071 SMESHDS_Mesh * mesh = GetMeshDS();
3072 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
3073 int nbElems, nbNodes;
3075 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
3076 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
3079 elemQueue.push_back( *elemSetIt );
3080 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
3082 const SMDS_MeshElement* elem = *elemIt;
3083 switch( elem->GetEntityType() )
3085 case SMDSEntity_TriQuad_Hexa: // HEX27
3087 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3088 nbElems = nbNodes = 8;
3089 elemType = & hexaType;
3091 // get nodes for new elements
3092 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
3093 { 1,9,20,8, 17,22,26,21 },
3094 { 2,10,20,9, 18,23,26,22 },
3095 { 3,11,20,10, 19,24,26,23 },
3096 { 16,21,26,24, 4,12,25,15 },
3097 { 17,22,26,21, 5,13,25,12 },
3098 { 18,23,26,22, 6,14,25,13 },
3099 { 19,24,26,23, 7,15,25,14 }};
3100 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
3102 // add boundary faces to elemQueue
3103 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
3104 { 4,5,6,7, 12,13,14,15, 25 },
3105 { 0,1,5,4, 8,17,12,16, 21 },
3106 { 1,2,6,5, 9,18,13,17, 22 },
3107 { 2,3,7,6, 10,19,14,18, 23 },
3108 { 3,0,4,7, 11,16,15,19, 24 }};
3109 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
3111 // add boundary segments to elemQueue
3112 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
3113 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
3114 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
3115 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
3118 case SMDSEntity_BiQuad_Triangle: // TRIA7
3120 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3123 elemType = & quadType;
3125 // get nodes for new elements
3126 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
3127 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
3129 // add boundary segments to elemQueue
3130 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
3131 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
3134 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
3136 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3139 elemType = & quadType;
3141 // get nodes for new elements
3142 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
3143 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
3145 // add boundary segments to elemQueue
3146 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
3147 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
3150 case SMDSEntity_Quad_Edge:
3152 if ( elemIt == elemQueue.begin() )
3153 continue; // an elem is in theElems
3154 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
3157 elemType = & segType;
3159 // get nodes for new elements
3160 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
3161 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
3165 } // switch( elem->GetEntityType() )
3167 // Create new elements
3169 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
3173 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
3174 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
3175 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
3176 //elemType->SetID( -1 );
3178 for ( int iE = 0; iE < nbElems; ++iE )
3179 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
3182 ReplaceElemInGroups( elem, splitElems, mesh );
3185 for ( size_t i = 0; i < splitElems.size(); ++i )
3186 subMesh->AddElement( splitElems[i] );
3191 //=======================================================================
3192 //function : AddToSameGroups
3193 //purpose : add elemToAdd to the groups the elemInGroups belongs to
3194 //=======================================================================
3196 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
3197 const SMDS_MeshElement* elemInGroups,
3198 SMESHDS_Mesh * aMesh)
3200 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3201 if (!groups.empty()) {
3202 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
3203 for ( ; grIt != groups.end(); grIt++ ) {
3204 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
3205 if ( group && group->Contains( elemInGroups ))
3206 group->SMDSGroup().Add( elemToAdd );
3212 //=======================================================================
3213 //function : RemoveElemFromGroups
3214 //purpose : Remove removeelem to the groups the elemInGroups belongs to
3215 //=======================================================================
3216 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
3217 SMESHDS_Mesh * aMesh)
3219 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3220 if (!groups.empty())
3222 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
3223 for (; GrIt != groups.end(); GrIt++)
3225 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
3226 if (!grp || grp->IsEmpty()) continue;
3227 grp->SMDSGroup().Remove(removeelem);
3232 //================================================================================
3234 * \brief Replace elemToRm by elemToAdd in the all groups
3236 //================================================================================
3238 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
3239 const SMDS_MeshElement* elemToAdd,
3240 SMESHDS_Mesh * aMesh)
3242 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3243 if (!groups.empty()) {
3244 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
3245 for ( ; grIt != groups.end(); grIt++ ) {
3246 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
3247 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
3248 group->SMDSGroup().Add( elemToAdd );
3253 //================================================================================
3255 * \brief Replace elemToRm by elemToAdd in the all groups
3257 //================================================================================
3259 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
3260 const vector<const SMDS_MeshElement*>& elemToAdd,
3261 SMESHDS_Mesh * aMesh)
3263 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
3264 if (!groups.empty())
3266 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
3267 for ( ; grIt != groups.end(); grIt++ ) {
3268 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
3269 if ( group && group->SMDSGroup().Remove( elemToRm ) )
3270 for ( size_t i = 0; i < elemToAdd.size(); ++i )
3271 group->SMDSGroup().Add( elemToAdd[ i ] );
3276 //=======================================================================
3277 //function : QuadToTri
3278 //purpose : Cut quadrangles into triangles.
3279 // theCrit is used to select a diagonal to cut
3280 //=======================================================================
3282 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
3283 const bool the13Diag)
3286 myLastCreatedElems.reserve( theElems.size() * 2 );
3288 SMESHDS_Mesh * aMesh = GetMeshDS();
3289 Handle(Geom_Surface) surface;
3290 SMESH_MesherHelper helper( *GetMesh() );
3292 TIDSortedElemSet::iterator itElem;
3293 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3295 const SMDS_MeshElement* elem = *itElem;
3296 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
3299 if ( elem->NbNodes() == 4 ) {
3300 // retrieve element nodes
3301 const SMDS_MeshNode* aNodes [4];
3302 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3304 while ( itN->more() )
3305 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3307 int aShapeId = FindShape( elem );
3308 const SMDS_MeshElement* newElem1 = 0;
3309 const SMDS_MeshElement* newElem2 = 0;
3311 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
3312 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
3315 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
3316 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
3318 myLastCreatedElems.push_back(newElem1);
3319 myLastCreatedElems.push_back(newElem2);
3320 // put a new triangle on the same shape and add to the same groups
3323 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3324 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3326 AddToSameGroups( newElem1, elem, aMesh );
3327 AddToSameGroups( newElem2, elem, aMesh );
3328 aMesh->RemoveElement( elem );
3331 // Quadratic quadrangle
3333 else if ( elem->NbNodes() >= 8 )
3335 // get surface elem is on
3336 int aShapeId = FindShape( elem );
3337 if ( aShapeId != helper.GetSubShapeID() ) {
3341 shape = aMesh->IndexToShape( aShapeId );
3342 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
3343 TopoDS_Face face = TopoDS::Face( shape );
3344 surface = BRep_Tool::Surface( face );
3345 if ( !surface.IsNull() )
3346 helper.SetSubShape( shape );
3350 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3351 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3352 for ( int i = 0; itN->more(); ++i )
3353 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3355 const SMDS_MeshNode* centrNode = aNodes[8];
3356 if ( centrNode == 0 )
3358 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3359 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3361 myLastCreatedNodes.push_back(centrNode);
3364 // create a new element
3365 const SMDS_MeshElement* newElem1 = 0;
3366 const SMDS_MeshElement* newElem2 = 0;
3368 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3369 aNodes[6], aNodes[7], centrNode );
3370 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3371 centrNode, aNodes[4], aNodes[5] );
3374 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3375 aNodes[7], aNodes[4], centrNode );
3376 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3377 centrNode, aNodes[5], aNodes[6] );
3379 myLastCreatedElems.push_back(newElem1);
3380 myLastCreatedElems.push_back(newElem2);
3381 // put a new triangle on the same shape and add to the same groups
3384 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3385 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3387 AddToSameGroups( newElem1, elem, aMesh );
3388 AddToSameGroups( newElem2, elem, aMesh );
3389 aMesh->RemoveElement( elem );
3396 //=======================================================================
3397 //function : getAngle
3399 //=======================================================================
3401 double getAngle(const SMDS_MeshElement * tr1,
3402 const SMDS_MeshElement * tr2,
3403 const SMDS_MeshNode * n1,
3404 const SMDS_MeshNode * n2)
3406 double angle = 2. * M_PI; // bad angle
3409 SMESH::Controls::TSequenceOfXYZ P1, P2;
3410 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3411 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3414 if(!tr1->IsQuadratic())
3415 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3417 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3418 if ( N1.SquareMagnitude() <= gp::Resolution() )
3420 if(!tr2->IsQuadratic())
3421 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3423 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3424 if ( N2.SquareMagnitude() <= gp::Resolution() )
3427 // find the first diagonal node n1 in the triangles:
3428 // take in account a diagonal link orientation
3429 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3430 for ( int t = 0; t < 2; t++ ) {
3431 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3432 int i = 0, iDiag = -1;
3433 while ( it->more()) {
3434 const SMDS_MeshElement *n = it->next();
3435 if ( n == n1 || n == n2 ) {
3439 if ( i - iDiag == 1 )
3440 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3449 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3452 angle = N1.Angle( N2 );
3457 // =================================================
3458 // class generating a unique ID for a pair of nodes
3459 // and able to return nodes by that ID
3460 // =================================================
3464 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3465 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3468 smIdType GetLinkID (const SMDS_MeshNode * n1,
3469 const SMDS_MeshNode * n2) const
3471 return ( std::min(n1->GetID(),n2->GetID()) * myMaxID + std::max(n1->GetID(),n2->GetID()));
3474 bool GetNodes (const long theLinkID,
3475 const SMDS_MeshNode* & theNode1,
3476 const SMDS_MeshNode* & theNode2) const
3478 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3479 if ( !theNode1 ) return false;
3480 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3481 if ( !theNode2 ) return false;
3487 const SMESHDS_Mesh* myMesh;
3492 //=======================================================================
3493 //function : TriToQuad
3494 //purpose : Fuse neighbour triangles into quadrangles.
3495 // theCrit is used to select a neighbour to fuse with.
3496 // theMaxAngle is a max angle between element normals at which
3497 // fusion is still performed.
3498 //=======================================================================
3500 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3501 SMESH::Controls::NumericalFunctorPtr theCrit,
3502 const double theMaxAngle)
3505 myLastCreatedElems.reserve( theElems.size() / 2 );
3507 if ( !theCrit.get() )
3510 SMESHDS_Mesh * aMesh = GetMeshDS();
3512 // Prepare data for algo: build
3513 // 1. map of elements with their linkIDs
3514 // 2. map of linkIDs with their elements
3516 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3517 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3518 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3519 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3521 TIDSortedElemSet::iterator itElem;
3522 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3524 const SMDS_MeshElement* elem = *itElem;
3525 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3526 bool IsTria = ( elem->NbCornerNodes()==3 );
3527 if (!IsTria) continue;
3529 // retrieve element nodes
3530 const SMDS_MeshNode* aNodes [4];
3531 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3534 aNodes[ i++ ] = itN->next();
3535 aNodes[ 3 ] = aNodes[ 0 ];
3538 for ( i = 0; i < 3; i++ ) {
3539 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3540 // check if elements sharing a link can be fused
3541 itLE = mapLi_listEl.find( link );
3542 if ( itLE != mapLi_listEl.end() ) {
3543 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3545 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3546 //if ( FindShape( elem ) != FindShape( elem2 ))
3547 // continue; // do not fuse triangles laying on different shapes
3548 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3549 continue; // avoid making badly shaped quads
3550 (*itLE).second.push_back( elem );
3553 mapLi_listEl[ link ].push_back( elem );
3555 mapEl_setLi [ elem ].insert( link );
3558 // Clean the maps from the links shared by a sole element, ie
3559 // links to which only one element is bound in mapLi_listEl
3561 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3562 int nbElems = (*itLE).second.size();
3563 if ( nbElems < 2 ) {
3564 const SMDS_MeshElement* elem = (*itLE).second.front();
3565 SMESH_TLink link = (*itLE).first;
3566 mapEl_setLi[ elem ].erase( link );
3567 if ( mapEl_setLi[ elem ].empty() )
3568 mapEl_setLi.erase( elem );
3572 // Algo: fuse triangles into quadrangles
3574 while ( ! mapEl_setLi.empty() ) {
3575 // Look for the start element:
3576 // the element having the least nb of shared links
3577 const SMDS_MeshElement* startElem = 0;
3579 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3580 int nbLinks = (*itEL).second.size();
3581 if ( nbLinks < minNbLinks ) {
3582 startElem = (*itEL).first;
3583 minNbLinks = nbLinks;
3584 if ( minNbLinks == 1 )
3589 // search elements to fuse starting from startElem or links of elements
3590 // fused earlyer - startLinks
3591 list< SMESH_TLink > startLinks;
3592 while ( startElem || !startLinks.empty() ) {
3593 while ( !startElem && !startLinks.empty() ) {
3594 // Get an element to start, by a link
3595 SMESH_TLink linkId = startLinks.front();
3596 startLinks.pop_front();
3597 itLE = mapLi_listEl.find( linkId );
3598 if ( itLE != mapLi_listEl.end() ) {
3599 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3600 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3601 for ( ; itE != listElem.end() ; itE++ )
3602 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3604 mapLi_listEl.erase( itLE );
3609 // Get candidates to be fused
3610 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3611 const SMESH_TLink *link12 = 0, *link13 = 0;
3613 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3614 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3615 ASSERT( !setLi.empty() );
3616 set< SMESH_TLink >::iterator itLi;
3617 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3619 const SMESH_TLink & link = (*itLi);
3620 itLE = mapLi_listEl.find( link );
3621 if ( itLE == mapLi_listEl.end() )
3624 const SMDS_MeshElement* elem = (*itLE).second.front();
3626 elem = (*itLE).second.back();
3627 mapLi_listEl.erase( itLE );
3628 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3639 // add other links of elem to list of links to re-start from
3640 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3641 set< SMESH_TLink >::iterator it;
3642 for ( it = links.begin(); it != links.end(); it++ ) {
3643 const SMESH_TLink& link2 = (*it);
3644 if ( link2 != link )
3645 startLinks.push_back( link2 );
3649 // Get nodes of possible quadrangles
3650 const SMDS_MeshNode *n12 [4], *n13 [4];
3651 bool Ok12 = false, Ok13 = false;
3652 const SMDS_MeshNode *linkNode1, *linkNode2;
3654 linkNode1 = link12->first;
3655 linkNode2 = link12->second;
3656 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3660 linkNode1 = link13->first;
3661 linkNode2 = link13->second;
3662 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3666 // Choose a pair to fuse
3667 if ( Ok12 && Ok13 ) {
3668 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3669 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3670 double aBadRate12 = getBadRate( &quad12, theCrit );
3671 double aBadRate13 = getBadRate( &quad13, theCrit );
3672 if ( aBadRate13 < aBadRate12 )
3679 // and remove fused elems and remove links from the maps
3680 mapEl_setLi.erase( tr1 );
3683 mapEl_setLi.erase( tr2 );
3684 mapLi_listEl.erase( *link12 );
3685 if ( tr1->NbNodes() == 3 )
3687 const SMDS_MeshElement* newElem = 0;
3688 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3689 myLastCreatedElems.push_back(newElem);
3690 AddToSameGroups( newElem, tr1, aMesh );
3691 int aShapeId = tr1->getshapeId();
3693 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3694 aMesh->RemoveElement( tr1 );
3695 aMesh->RemoveElement( tr2 );
3698 vector< const SMDS_MeshNode* > N1;
3699 vector< const SMDS_MeshNode* > N2;
3700 getNodesFromTwoTria(tr1,tr2,N1,N2);
3701 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3702 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3703 // i.e. first nodes from both arrays form a new diagonal
3704 const SMDS_MeshNode* aNodes[8];
3713 const SMDS_MeshElement* newElem = 0;
3714 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3715 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3716 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3718 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3719 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3720 myLastCreatedElems.push_back(newElem);
3721 AddToSameGroups( newElem, tr1, aMesh );
3722 int aShapeId = tr1->getshapeId();
3724 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3725 aMesh->RemoveElement( tr1 );
3726 aMesh->RemoveElement( tr2 );
3727 // remove middle node (9)
3728 if ( N1[4]->NbInverseElements() == 0 )
3729 aMesh->RemoveNode( N1[4] );
3730 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3731 aMesh->RemoveNode( N1[6] );
3732 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3733 aMesh->RemoveNode( N2[6] );
3738 mapEl_setLi.erase( tr3 );
3739 mapLi_listEl.erase( *link13 );
3740 if ( tr1->NbNodes() == 3 ) {
3741 const SMDS_MeshElement* newElem = 0;
3742 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3743 myLastCreatedElems.push_back(newElem);
3744 AddToSameGroups( newElem, tr1, aMesh );
3745 int aShapeId = tr1->getshapeId();
3747 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3748 aMesh->RemoveElement( tr1 );
3749 aMesh->RemoveElement( tr3 );
3752 vector< const SMDS_MeshNode* > N1;
3753 vector< const SMDS_MeshNode* > N2;
3754 getNodesFromTwoTria(tr1,tr3,N1,N2);
3755 // now we receive following N1 and N2 (using numeration as above image)
3756 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3757 // i.e. first nodes from both arrays form a new diagonal
3758 const SMDS_MeshNode* aNodes[8];
3767 const SMDS_MeshElement* newElem = 0;
3768 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3769 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3770 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3772 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3773 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3774 myLastCreatedElems.push_back(newElem);
3775 AddToSameGroups( newElem, tr1, aMesh );
3776 int aShapeId = tr1->getshapeId();
3778 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3779 aMesh->RemoveElement( tr1 );
3780 aMesh->RemoveElement( tr3 );
3781 // remove middle node (9)
3782 if ( N1[4]->NbInverseElements() == 0 )
3783 aMesh->RemoveNode( N1[4] );
3784 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3785 aMesh->RemoveNode( N1[6] );
3786 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3787 aMesh->RemoveNode( N2[6] );
3791 // Next element to fuse: the rejected one
3793 startElem = Ok12 ? tr3 : tr2;
3795 } // if ( startElem )
3796 } // while ( startElem || !startLinks.empty() )
3797 } // while ( ! mapEl_setLi.empty() )
3802 //================================================================================
3804 * \brief Return nodes linked to the given one
3805 * \param theNode - the node
3806 * \param linkedNodes - the found nodes
3807 * \param type - the type of elements to check
3809 * Medium nodes are ignored
3811 //================================================================================
3813 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3814 TIDSortedElemSet & linkedNodes,
3815 SMDSAbs_ElementType type )
3817 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3818 while ( elemIt->more() )
3820 const SMDS_MeshElement* elem = elemIt->next();
3821 if(elem->GetType() == SMDSAbs_0DElement)
3824 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3825 if ( elem->GetType() == SMDSAbs_Volume )
3827 SMDS_VolumeTool vol( elem );
3828 while ( nodeIt->more() ) {
3829 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3830 if ( theNode != n && vol.IsLinked( theNode, n ))
3831 linkedNodes.insert( n );
3836 for ( int i = 0; nodeIt->more(); ++i ) {
3837 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3838 if ( n == theNode ) {
3839 int iBefore = i - 1;
3841 if ( elem->IsQuadratic() ) {
3842 int nb = elem->NbNodes() / 2;
3843 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3844 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3846 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3847 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3854 //=======================================================================
3855 //function : averageBySurface
3856 //purpose : Auxiliar function to treat properly nodes in periodic faces in the laplacian smoother
3857 //=======================================================================
3858 void averageBySurface( const Handle(Geom_Surface)& theSurface, const SMDS_MeshNode* refNode,
3859 TIDSortedElemSet& nodeSet, map< const SMDS_MeshNode*, gp_XY* >& theUVMap, double * coord )
3861 if ( theSurface.IsNull() )
3863 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3864 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ )
3866 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3867 coord[0] += node->X();
3868 coord[1] += node->Y();
3869 coord[2] += node->Z();
3874 Standard_Real Umin,Umax,Vmin,Vmax;
3875 theSurface->Bounds( Umin, Umax, Vmin, Vmax );
3876 ASSERT( theUVMap.find( refNode ) != theUVMap.end() );
3877 gp_XY* nodeUV = theUVMap[ refNode ];
3878 Standard_Real uref = nodeUV->X();
3879 Standard_Real vref = nodeUV->Y();
3881 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3882 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ )
3884 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3885 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3886 gp_XY* uv = theUVMap[ node ];
3888 if ( theSurface->IsUPeriodic() || theSurface->IsVPeriodic() )
3890 Standard_Real u = uv->X();
3891 Standard_Real v = uv->Y();
3892 Standard_Real uCorrected = u;
3893 Standard_Real vCorrected = v;
3894 bool isUTobeCorrected = (std::fabs( uref - u ) >= 0.7 * std::fabs( Umax - Umin ));
3895 bool isVTobeCorrected = (std::fabs( vref - v ) >= 0.7 * std::fabs( Vmax - Vmin ));
3897 if( isUTobeCorrected )
3898 uCorrected = uref > u ? Umax + std::fabs(Umin - u) : Umin - std::fabs(Umax - u);
3900 if( isVTobeCorrected )
3901 vCorrected = vref > v ? Vmax + std::fabs(Vmin - v) : Vmin - std::fabs(Vmax - v);
3903 coord[0] += uCorrected;
3904 coord[1] += vCorrected;
3909 coord[0] += uv->X();
3910 coord[1] += uv->Y();
3916 //=======================================================================
3917 //function : laplacianSmooth
3918 //purpose : pulls theNode toward the center of surrounding nodes directly
3919 // connected to that node along an element edge
3920 //=======================================================================
3922 void laplacianSmooth(const SMDS_MeshNode* theNode,
3923 const Handle(Geom_Surface)& theSurface,
3924 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3926 // find surrounding nodes
3928 TIDSortedElemSet nodeSet;
3929 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3931 // compute new coodrs
3932 double coord[] = { 0., 0., 0. };
3934 averageBySurface( theSurface, theNode, nodeSet, theUVMap, coord );
3936 int nbNodes = nodeSet.size();
3940 coord[0] /= nbNodes;
3941 coord[1] /= nbNodes;
3943 if ( !theSurface.IsNull() ) {
3944 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3945 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3946 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3952 coord[2] /= nbNodes;
3956 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3959 //=======================================================================
3960 //function : correctTheValue
3961 //purpose : Given a boundaries of parametric space determine if the node coordinate (u,v) need correction
3962 // based on the reference coordinate (uref,vref)
3963 //=======================================================================
3964 void correctTheValue( Standard_Real Umax, Standard_Real Umin, Standard_Real Vmax, Standard_Real Vmin,
3965 Standard_Real uref, Standard_Real vref, Standard_Real &u, Standard_Real &v )
3967 bool isUTobeCorrected = (std::fabs( uref - u ) >= 0.7 * std::fabs( Umax - Umin ));
3968 bool isVTobeCorrected = (std::fabs( vref - v ) >= 0.7 * std::fabs( Vmax - Vmin ));
3969 if ( isUTobeCorrected )
3970 u = std::fabs(u-Umin) < 1e-7 ? Umax : Umin;
3971 if ( isVTobeCorrected )
3972 v = std::fabs(v-Vmin) < 1e-7 ? Vmax : Vmin;
3975 //=======================================================================
3976 //function : averageByElement
3977 //purpose : Auxiliar function to treat properly nodes in periodic faces in the centroidal smoother
3978 //=======================================================================
3979 void averageByElement( const Handle(Geom_Surface)& theSurface, const SMDS_MeshNode* refNode, const SMDS_MeshElement* elem,
3980 map< const SMDS_MeshNode*, gp_XY* >& theUVMap, SMESH::Controls::TSequenceOfXYZ& aNodePoints,
3981 gp_XYZ& elemCenter )
3983 int nn = elem->NbNodes();
3984 if(elem->IsQuadratic()) nn = nn/2;
3986 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3987 Standard_Real Umin,Umax,Vmin,Vmax;
3990 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3992 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3993 aNodePoints.push_back( aP );
3994 if ( !theSurface.IsNull() ) // smooth in 2D
3996 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3997 gp_XY* uv = theUVMap[ aNode ];
3999 if ( theSurface->IsUPeriodic() || theSurface->IsVPeriodic() )
4001 theSurface->Bounds( Umin, Umax, Vmin, Vmax );
4002 Standard_Real u = uv->X();
4003 Standard_Real v = uv->Y();
4004 bool isSingularPoint = std::fabs(u - Umin) < 1e-7 || std::fabs(v - Vmin) < 1e-7 || std::fabs(u - Umax) < 1e-7 || std::fabs( v - Vmax ) < 1e-7;
4005 if ( !isSingularPoint )
4007 aP.SetCoord( uv->X(), uv->Y(), 0. );
4011 gp_XY* refPoint = theUVMap[ refNode ];
4012 Standard_Real uref = refPoint->X();
4013 Standard_Real vref = refPoint->Y();
4014 correctTheValue( Umax, Umin, Vmax, Vmin, uref, vref, u, v );
4015 aP.SetCoord( u, v, 0. );
4019 aP.SetCoord( uv->X(), uv->Y(), 0. );
4025 //=======================================================================
4026 //function : centroidalSmooth
4027 //purpose : pulls theNode toward the element-area-weighted centroid of the
4028 // surrounding elements
4029 //=======================================================================
4031 void centroidalSmooth(const SMDS_MeshNode* theNode,
4032 const Handle(Geom_Surface)& theSurface,
4033 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
4035 gp_XYZ aNewXYZ(0.,0.,0.);
4036 SMESH::Controls::Area anAreaFunc;
4037 double totalArea = 0.;
4040 bool notToMoveNode = false;
4041 // Do not correct singular nodes
4042 if ( !theSurface.IsNull() && (theSurface->IsUPeriodic() || theSurface->IsVPeriodic()) )
4044 Standard_Real Umin,Umax,Vmin,Vmax;
4045 theSurface->Bounds( Umin, Umax, Vmin, Vmax );
4046 gp_XY* uv = theUVMap[ theNode ];
4047 Standard_Real u = uv->X();
4048 Standard_Real v = uv->Y();
4049 notToMoveNode = std::fabs(u - Umin) < 1e-7 || std::fabs(v - Vmin) < 1e-7 || std::fabs(u - Umax) < 1e-7 || std::fabs( v - Vmax ) < 1e-7;
4052 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
4053 while ( elemIt->more() && !notToMoveNode )
4055 const SMDS_MeshElement* elem = elemIt->next();
4058 gp_XYZ elemCenter(0.,0.,0.);
4059 SMESH::Controls::TSequenceOfXYZ aNodePoints;
4060 int nn = elem->NbNodes();
4061 if(elem->IsQuadratic()) nn = nn/2;
4062 averageByElement( theSurface, theNode, elem, theUVMap, aNodePoints, elemCenter );
4064 double elemArea = anAreaFunc.GetValue( aNodePoints );
4065 totalArea += elemArea;
4067 aNewXYZ += elemCenter * elemArea;
4069 aNewXYZ /= totalArea;
4071 if ( !theSurface.IsNull() && !notToMoveNode ) {
4072 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
4073 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
4077 if ( !notToMoveNode )
4078 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
4081 //=======================================================================
4082 //function : getClosestUV
4083 //purpose : return UV of closest projection
4084 //=======================================================================
4086 static bool getClosestUV (Extrema_GenExtPS& projector,
4087 const gp_Pnt& point,
4090 projector.Perform( point );
4091 if ( projector.IsDone() ) {
4092 double u = 0, v = 0, minVal = DBL_MAX;
4093 for ( int i = projector.NbExt(); i > 0; i-- )
4094 if ( projector.SquareDistance( i ) < minVal ) {
4095 minVal = projector.SquareDistance( i );
4096 projector.Point( i ).Parameter( u, v );
4098 result.SetCoord( u, v );
4104 //=======================================================================
4106 //purpose : Smooth theElements during theNbIterations or until a worst
4107 // element has aspect ratio <= theTgtAspectRatio.
4108 // Aspect Ratio varies in range [1.0, inf].
4109 // If theElements is empty, the whole mesh is smoothed.
4110 // theFixedNodes contains additionally fixed nodes. Nodes built
4111 // on edges and boundary nodes are always fixed.
4112 //=======================================================================
4114 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
4115 set<const SMDS_MeshNode*> & theFixedNodes,
4116 const SmoothMethod theSmoothMethod,
4117 const int theNbIterations,
4118 double theTgtAspectRatio,
4123 if ( theTgtAspectRatio < 1.0 )
4124 theTgtAspectRatio = 1.0;
4126 const double disttol = 1.e-16;
4128 SMESH::Controls::AspectRatio aQualityFunc;
4130 SMESHDS_Mesh* aMesh = GetMeshDS();
4132 if ( theElems.empty() ) {
4133 // add all faces to theElems
4134 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
4135 while ( fIt->more() ) {
4136 const SMDS_MeshElement* face = fIt->next();
4137 theElems.insert( theElems.end(), face );
4140 // get all face ids theElems are on
4141 set< int > faceIdSet;
4142 TIDSortedElemSet::iterator itElem;
4144 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4145 int fId = FindShape( *itElem );
4146 // check that corresponding submesh exists and a shape is face
4148 faceIdSet.find( fId ) == faceIdSet.end() &&
4149 aMesh->MeshElements( fId )) {
4150 TopoDS_Shape F = aMesh->IndexToShape( fId );
4151 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
4152 faceIdSet.insert( fId );
4155 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
4157 // ===============================================
4158 // smooth elements on each TopoDS_Face separately
4159 // ===============================================
4161 SMESH_MesherHelper helper( *GetMesh() );
4163 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
4164 for ( ; fId != faceIdSet.rend(); ++fId )
4166 // get face surface and submesh
4167 Handle(Geom_Surface) surface;
4168 SMESHDS_SubMesh* faceSubMesh = 0;
4171 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
4172 bool isUPeriodic = false, isVPeriodic = false;
4175 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
4176 surface = BRep_Tool::Surface( face );
4177 faceSubMesh = aMesh->MeshElements( *fId );
4178 fToler2 = BRep_Tool::Tolerance( face );
4179 fToler2 *= fToler2 * 10.;
4180 isUPeriodic = surface->IsUPeriodic();
4181 // if ( isUPeriodic )
4182 // surface->UPeriod();
4183 isVPeriodic = surface->IsVPeriodic();
4184 // if ( isVPeriodic )
4185 // surface->VPeriod();
4186 surface->Bounds( u1, u2, v1, v2 );
4187 helper.SetSubShape( face );
4189 // ---------------------------------------------------------
4190 // for elements on a face, find movable and fixed nodes and
4191 // compute UV for them
4192 // ---------------------------------------------------------
4193 bool checkBoundaryNodes = false;
4194 bool isQuadratic = false;
4195 set<const SMDS_MeshNode*> setMovableNodes;
4196 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4197 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4198 list< const SMDS_MeshElement* > elemsOnFace;
4200 Extrema_GenExtPS projector;
4201 GeomAdaptor_Surface surfAdaptor;
4202 if ( !surface.IsNull() ) {
4203 surfAdaptor.Load( surface );
4204 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4206 int nbElemOnFace = 0;
4207 itElem = theElems.begin();
4208 // loop on not yet smoothed elements: look for elems on a face
4209 while ( itElem != theElems.end() )
4211 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4212 break; // all elements found
4214 const SMDS_MeshElement* elem = *itElem;
4215 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4216 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4220 elemsOnFace.push_back( elem );
4221 theElems.erase( itElem++ );
4225 isQuadratic = elem->IsQuadratic();
4227 // get movable nodes of elem
4228 const SMDS_MeshNode* node;
4229 SMDS_TypeOfPosition posType;
4230 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4231 int nn = 0, nbn = elem->NbNodes();
4232 if(elem->IsQuadratic())
4234 while ( nn++ < nbn ) {
4235 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4236 const SMDS_PositionPtr& pos = node->GetPosition();
4237 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4238 if (posType != SMDS_TOP_EDGE &&
4239 posType != SMDS_TOP_VERTEX &&
4240 theFixedNodes.find( node ) == theFixedNodes.end())
4242 // check if all faces around the node are on faceSubMesh
4243 // because a node on edge may be bound to face
4245 if ( faceSubMesh ) {
4246 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4247 while ( eIt->more() && all ) {
4248 const SMDS_MeshElement* e = eIt->next();
4249 all = faceSubMesh->Contains( e );
4253 setMovableNodes.insert( node );
4255 checkBoundaryNodes = true;
4257 if ( posType == SMDS_TOP_3DSPACE )
4258 checkBoundaryNodes = true;
4261 if ( surface.IsNull() )
4264 // get nodes to check UV
4265 list< const SMDS_MeshNode* > uvCheckNodes;
4266 const SMDS_MeshNode* nodeInFace = 0;
4267 itN = elem->nodesIterator();
4268 nn = 0; nbn = elem->NbNodes();
4269 if(elem->IsQuadratic())
4271 while ( nn++ < nbn ) {
4272 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4273 if ( node->GetPosition()->GetDim() == 2 )
4275 if ( uvMap.find( node ) == uvMap.end() )
4276 uvCheckNodes.push_back( node );
4277 // add nodes of elems sharing node
4278 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4279 // while ( eIt->more() ) {
4280 // const SMDS_MeshElement* e = eIt->next();
4281 // if ( e != elem ) {
4282 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4283 // while ( nIt->more() ) {
4284 // const SMDS_MeshNode* n =
4285 // static_cast<const SMDS_MeshNode*>( nIt->next() );
4286 // if ( uvMap.find( n ) == uvMap.end() )
4287 // uvCheckNodes.push_back( n );
4293 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4294 for ( ; n != uvCheckNodes.end(); ++n ) {
4297 const SMDS_PositionPtr& pos = node->GetPosition();
4298 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4302 bool toCheck = true;
4303 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4305 // compute not existing UV
4306 bool project = ( posType == SMDS_TOP_3DSPACE );
4307 // double dist1 = DBL_MAX, dist2 = 0;
4308 // if ( posType != SMDS_TOP_3DSPACE ) {
4309 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4310 // project = dist1 > fToler2;
4312 if ( project ) { // compute new UV
4314 gp_Pnt pNode = SMESH_NodeXYZ( node );
4315 if ( !getClosestUV( projector, pNode, newUV )) {
4316 MESSAGE("Node Projection Failed " << node);
4320 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4322 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4324 // if ( posType != SMDS_TOP_3DSPACE )
4325 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4326 // if ( dist2 < dist1 )
4330 // store UV in the map
4331 listUV.push_back( uv );
4332 uvMap.insert( make_pair( node, &listUV.back() ));
4334 } // loop on not yet smoothed elements
4336 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4337 checkBoundaryNodes = true;
4339 // fix nodes on mesh boundary
4341 if ( checkBoundaryNodes ) {
4342 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4343 map< SMESH_TLink, int >::iterator link_nb;
4344 // put all elements links to linkNbMap
4345 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4346 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4347 const SMDS_MeshElement* elem = (*elemIt);
4348 int nbn = elem->NbCornerNodes();
4349 // loop on elem links: insert them in linkNbMap
4350 for ( int iN = 0; iN < nbn; ++iN ) {
4351 const SMDS_MeshNode* n1 = elem->GetNode( iN );
4352 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4353 SMESH_TLink link( n1, n2 );
4354 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4358 // remove nodes that are in links encountered only once from setMovableNodes
4359 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4360 if ( link_nb->second == 1 ) {
4361 setMovableNodes.erase( link_nb->first.node1() );
4362 setMovableNodes.erase( link_nb->first.node2() );
4367 // -----------------------------------------------------
4368 // for nodes on seam edge, compute one more UV ( uvMap2 );
4369 // find movable nodes linked to nodes on seam and which
4370 // are to be smoothed using the second UV ( uvMap2 )
4371 // -----------------------------------------------------
4373 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4374 if ( !surface.IsNull() ) {
4375 TopExp_Explorer eExp( face, TopAbs_EDGE );
4376 for ( ; eExp.More(); eExp.Next() ) {
4377 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4378 if ( !BRep_Tool::IsClosed( edge, face ))
4380 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4383 // find out which parameter varies for a node on seam
4386 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4387 if ( pcurve.IsNull() ) continue;
4388 uv1 = pcurve->Value( f );
4390 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4391 if ( pcurve.IsNull() ) continue;
4392 uv2 = pcurve->Value( f );
4393 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4395 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4396 std::swap( uv1, uv2 );
4397 // get nodes on seam and its vertices
4398 list< const SMDS_MeshNode* > seamNodes;
4399 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4400 while ( nSeamIt->more() ) {
4401 const SMDS_MeshNode* node = nSeamIt->next();
4402 if ( !isQuadratic || !IsMedium( node ))
4403 seamNodes.push_back( node );
4405 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4406 for ( ; vExp.More(); vExp.Next() ) {
4407 sm = aMesh->MeshElements( vExp.Current() );
4409 nSeamIt = sm->GetNodes();
4410 while ( nSeamIt->more() )
4411 seamNodes.push_back( nSeamIt->next() );
4414 // loop on nodes on seam
4415 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4416 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4417 const SMDS_MeshNode* nSeam = *noSeIt;
4418 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4419 if ( n_uv == uvMap.end() )
4422 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4423 // set the second UV
4424 listUV.push_back( *n_uv->second );
4425 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4426 if ( uvMap2.empty() )
4427 uvMap2 = uvMap; // copy the uvMap contents
4428 uvMap2[ nSeam ] = &listUV.back();
4430 // collect movable nodes linked to ones on seam in nodesNearSeam
4431 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4432 while ( eIt->more() ) {
4433 const SMDS_MeshElement* e = eIt->next();
4434 int nbUseMap1 = 0, nbUseMap2 = 0;
4435 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4436 int nn = 0, nbn = e->NbNodes();
4437 if(e->IsQuadratic()) nbn = nbn/2;
4438 while ( nn++ < nbn )
4440 const SMDS_MeshNode* n =
4441 static_cast<const SMDS_MeshNode*>( nIt->next() );
4443 setMovableNodes.find( n ) == setMovableNodes.end() )
4445 // add only nodes being closer to uv2 than to uv1
4446 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4447 // 0.5 * ( n->Y() + nSeam->Y() ),
4448 // 0.5 * ( n->Z() + nSeam->Z() ));
4450 // getClosestUV( projector, pMid, uv );
4451 double x = uvMap[ n ]->Coord( iPar );
4452 if ( Abs( uv1.Coord( iPar ) - x ) >
4453 Abs( uv2.Coord( iPar ) - x )) {
4454 nodesNearSeam.insert( n );
4460 // for centroidalSmooth all element nodes must
4461 // be on one side of a seam
4462 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4463 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4465 while ( nn++ < nbn ) {
4466 const SMDS_MeshNode* n =
4467 static_cast<const SMDS_MeshNode*>( nIt->next() );
4468 setMovableNodes.erase( n );
4472 } // loop on nodes on seam
4473 } // loop on edge of a face
4474 } // if ( !face.IsNull() )
4476 if ( setMovableNodes.empty() ) {
4477 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4478 continue; // goto next face
4486 double maxRatio = -1., maxDisplacement = -1.;
4487 set<const SMDS_MeshNode*>::iterator nodeToMove;
4488 for ( it = 0; it < theNbIterations; it++ ) {
4489 maxDisplacement = 0.;
4490 nodeToMove = setMovableNodes.begin();
4491 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4492 const SMDS_MeshNode* node = (*nodeToMove);
4493 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4496 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4497 if ( theSmoothMethod == LAPLACIAN )
4498 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4500 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4502 // node displacement
4503 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4504 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4505 if ( aDispl > maxDisplacement )
4506 maxDisplacement = aDispl;
4508 // no node movement => exit
4509 //if ( maxDisplacement < 1.e-16 ) {
4510 if ( maxDisplacement < disttol ) {
4511 MESSAGE("-- no node movement --");
4515 // check elements quality
4517 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4518 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4519 const SMDS_MeshElement* elem = (*elemIt);
4520 if ( !elem || elem->GetType() != SMDSAbs_Face )
4522 SMESH::Controls::TSequenceOfXYZ aPoints;
4523 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4524 double aValue = aQualityFunc.GetValue( aPoints );
4525 if ( aValue > maxRatio )
4529 if ( maxRatio <= theTgtAspectRatio ) {
4530 //MESSAGE("-- quality achieved --");
4533 if (it+1 == theNbIterations) {
4534 //MESSAGE("-- Iteration limit exceeded --");
4536 } // smoothing iterations
4538 // MESSAGE(" Face id: " << *fId <<
4539 // " Nb iterstions: " << it <<
4540 // " Displacement: " << maxDisplacement <<
4541 // " Aspect Ratio " << maxRatio);
4543 // ---------------------------------------
4544 // new nodes positions are computed,
4545 // record movement in DS and set new UV
4546 // ---------------------------------------
4547 nodeToMove = setMovableNodes.begin();
4548 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4549 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4550 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4551 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4552 if ( node_uv != uvMap.end() ) {
4553 gp_XY* uv = node_uv->second;
4555 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4559 // move medium nodes of quadratic elements
4562 vector<const SMDS_MeshNode*> nodes;
4564 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4565 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4567 const SMDS_MeshElement* QF = *elemIt;
4568 if ( QF->IsQuadratic() )
4570 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4571 SMDS_MeshElement::iterator() );
4572 nodes.push_back( nodes[0] );
4574 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4576 if ( !surface.IsNull() )
4578 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4579 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4580 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4581 xyz = surface->Value( uv.X(), uv.Y() );
4584 xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4586 if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4587 // we have to move a medium node
4588 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4594 } // loop on face ids
4600 //=======================================================================
4601 //function : isReverse
4602 //purpose : Return true if normal of prevNodes is not co-directied with
4603 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4604 // iNotSame is where prevNodes and nextNodes are different.
4605 // If result is true then future volume orientation is OK
4606 //=======================================================================
4608 bool isReverse(const SMDS_MeshElement* face,
4609 const vector<const SMDS_MeshNode*>& prevNodes,
4610 const vector<const SMDS_MeshNode*>& nextNodes,
4614 SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4615 SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4616 gp_XYZ extrDir( pN - pP ), faceNorm;
4617 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4619 return faceNorm * extrDir < 0.0;
4622 //================================================================================
4624 * \brief Assure that theElemSets[0] holds elements, not nodes
4626 //================================================================================
4628 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4630 if ( !theElemSets[0].empty() &&
4631 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4633 std::swap( theElemSets[0], theElemSets[1] );
4635 else if ( !theElemSets[1].empty() &&
4636 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4638 std::swap( theElemSets[0], theElemSets[1] );
4643 //=======================================================================
4645 * \brief Create elements by sweeping an element
4646 * \param elem - element to sweep
4647 * \param newNodesItVec - nodes generated from each node of the element
4648 * \param newElems - generated elements
4649 * \param nbSteps - number of sweeping steps
4650 * \param srcElements - to append elem for each generated element
4652 //=======================================================================
4654 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4655 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4656 list<const SMDS_MeshElement*>& newElems,
4657 const size_t nbSteps,
4658 SMESH_SequenceOfElemPtr& srcElements)
4660 SMESHDS_Mesh* aMesh = GetMeshDS();
4662 const int nbNodes = elem->NbNodes();
4663 const int nbCorners = elem->NbCornerNodes();
4664 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4665 polyhedron creation !!! */
4666 // Loop on elem nodes:
4667 // find new nodes and detect same nodes indices
4668 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4669 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4670 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4671 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4673 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4674 vector<int> sames(nbNodes);
4675 vector<bool> isSingleNode(nbNodes);
4677 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4678 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4679 const SMDS_MeshNode* node = nnIt->first;
4680 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4681 if ( listNewNodes.empty() )
4684 itNN [ iNode ] = listNewNodes.begin();
4685 prevNod[ iNode ] = node;
4686 nextNod[ iNode ] = listNewNodes.front();
4688 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4689 corner node of linear */
4690 if ( prevNod[ iNode ] != nextNod [ iNode ])
4691 nbDouble += !isSingleNode[iNode];
4693 if( iNode < nbCorners ) { // check corners only
4694 if ( prevNod[ iNode ] == nextNod [ iNode ])
4695 sames[nbSame++] = iNode;
4697 iNotSameNode = iNode;
4701 if ( nbSame == nbNodes || nbSame > 2) {
4702 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4706 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4708 // fix nodes order to have bottom normal external
4709 if ( baseType == SMDSEntity_Polygon )
4711 std::reverse( itNN.begin(), itNN.end() );
4712 std::reverse( prevNod.begin(), prevNod.end() );
4713 std::reverse( midlNod.begin(), midlNod.end() );
4714 std::reverse( nextNod.begin(), nextNod.end() );
4715 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4719 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4720 SMDS_MeshCell::applyInterlace( ind, itNN );
4721 SMDS_MeshCell::applyInterlace( ind, prevNod );
4722 SMDS_MeshCell::applyInterlace( ind, nextNod );
4723 SMDS_MeshCell::applyInterlace( ind, midlNod );
4724 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4727 sames[nbSame] = iNotSameNode;
4728 for ( int j = 0; j <= nbSame; ++j )
4729 for ( size_t i = 0; i < ind.size(); ++i )
4730 if ( ind[i] == sames[j] )
4735 iNotSameNode = sames[nbSame];
4739 else if ( elem->GetType() == SMDSAbs_Edge )
4741 // orient a new face same as adjacent one
4743 const SMDS_MeshElement* e;
4744 TIDSortedElemSet dummy;
4745 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4746 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4747 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4749 // there is an adjacent face, check order of nodes in it
4750 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4753 std::swap( itNN[0], itNN[1] );
4754 std::swap( prevNod[0], prevNod[1] );
4755 std::swap( nextNod[0], nextNod[1] );
4756 std::vector<bool>::swap(isSingleNode[0], isSingleNode[1]);
4758 sames[0] = 1 - sames[0];
4759 iNotSameNode = 1 - iNotSameNode;
4764 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4766 iSameNode = sames[ nbSame-1 ];
4767 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4768 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4769 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4772 if ( baseType == SMDSEntity_Polygon )
4774 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4775 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4777 else if ( baseType == SMDSEntity_Quad_Polygon )
4779 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4780 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4783 // make new elements
4784 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4787 for ( iNode = 0; iNode < nbNodes; iNode++ )
4789 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4790 nextNod[ iNode ] = *itNN[ iNode ]++;
4793 SMDS_MeshElement* aNewElem = 0;
4794 /*if(!elem->IsPoly())*/ {
4795 switch ( baseType ) {
4797 case SMDSEntity_Node: { // sweep NODE
4798 if ( nbSame == 0 ) {
4799 if ( isSingleNode[0] )
4800 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4802 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4808 case SMDSEntity_Edge: { // sweep EDGE
4809 if ( nbDouble == 0 )
4811 if ( nbSame == 0 ) // ---> quadrangle
4812 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4813 nextNod[ 1 ], nextNod[ 0 ] );
4814 else // ---> triangle
4815 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4816 nextNod[ iNotSameNode ] );
4818 else // ---> polygon
4820 vector<const SMDS_MeshNode*> poly_nodes;
4821 poly_nodes.push_back( prevNod[0] );
4822 poly_nodes.push_back( prevNod[1] );
4823 if ( prevNod[1] != nextNod[1] )
4825 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4826 poly_nodes.push_back( nextNod[1] );
4828 if ( prevNod[0] != nextNod[0] )
4830 poly_nodes.push_back( nextNod[0] );
4831 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4833 switch ( poly_nodes.size() ) {
4835 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4838 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4839 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4842 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4847 case SMDSEntity_Triangle: // TRIANGLE --->
4849 if ( nbDouble > 0 ) break;
4850 if ( nbSame == 0 ) // ---> pentahedron
4851 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4852 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4854 else if ( nbSame == 1 ) // ---> pyramid
4855 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4856 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4857 nextNod[ iSameNode ]);
4859 else // 2 same nodes: ---> tetrahedron
4860 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4861 nextNod[ iNotSameNode ]);
4864 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4868 if ( nbDouble+nbSame == 2 )
4870 if(nbSame==0) { // ---> quadratic quadrangle
4871 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4872 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4874 else { //(nbSame==1) // ---> quadratic triangle
4876 return; // medium node on axis
4878 else if(sames[0]==0)
4879 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4880 prevNod[2], midlNod[1], nextNod[2] );
4882 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4883 prevNod[2], nextNod[2], midlNod[0]);
4886 else if ( nbDouble == 3 )
4888 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4889 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4890 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4897 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4898 if ( nbDouble > 0 ) break;
4900 if ( nbSame == 0 ) // ---> hexahedron
4901 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4902 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4904 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4905 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4906 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4907 nextNod[ iSameNode ]);
4908 newElems.push_back( aNewElem );
4909 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4910 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4911 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4913 else if ( nbSame == 2 ) { // ---> pentahedron
4914 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4915 // iBeforeSame is same too
4916 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4917 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4918 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4920 // iAfterSame is same too
4921 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4922 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4923 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4927 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4928 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4929 if ( nbDouble+nbSame != 3 ) break;
4931 // ---> pentahedron with 15 nodes
4932 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4933 nextNod[0], nextNod[1], nextNod[2],
4934 prevNod[3], prevNod[4], prevNod[5],
4935 nextNod[3], nextNod[4], nextNod[5],
4936 midlNod[0], midlNod[1], midlNod[2]);
4938 else if(nbSame==1) {
4939 // ---> 2d order pyramid of 13 nodes
4940 int apex = iSameNode;
4941 int i0 = ( apex + 1 ) % nbCorners;
4942 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4946 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4947 nextNod[i0], nextNod[i1], prevNod[apex],
4948 prevNod[i01], midlNod[i0],
4949 nextNod[i01], midlNod[i1],
4950 prevNod[i1a], prevNod[i0a],
4951 nextNod[i0a], nextNod[i1a]);
4953 else if(nbSame==2) {
4954 // ---> 2d order tetrahedron of 10 nodes
4955 int n1 = iNotSameNode;
4956 int n2 = ( n1 + 1 ) % nbCorners;
4957 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4961 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4962 prevNod[n12], prevNod[n23], prevNod[n31],
4963 midlNod[n1], nextNod[n12], nextNod[n31]);
4967 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4969 if ( nbDouble != 4 ) break;
4970 // ---> hexahedron with 20 nodes
4971 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4972 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4973 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4974 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4975 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4977 else if(nbSame==1) {
4978 // ---> pyramid + pentahedron - can not be created since it is needed
4979 // additional middle node at the center of face
4980 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4983 else if( nbSame == 2 ) {
4984 if ( nbDouble != 2 ) break;
4985 // ---> 2d order Pentahedron with 15 nodes
4987 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4988 // iBeforeSame is same too
4995 // iAfterSame is same too
5005 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
5006 prevNod[n4], prevNod[n5], nextNod[n5],
5007 prevNod[n12], midlNod[n2], nextNod[n12],
5008 prevNod[n45], midlNod[n5], nextNod[n45],
5009 prevNod[n14], prevNod[n25], nextNod[n25]);
5013 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
5015 if( nbSame == 0 && nbDouble == 9 ) {
5016 // ---> tri-quadratic hexahedron with 27 nodes
5017 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
5018 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
5019 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
5020 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
5021 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
5022 prevNod[8], // bottom center
5023 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
5024 nextNod[8], // top center
5025 midlNod[8]);// elem center
5033 case SMDSEntity_Polygon: { // sweep POLYGON
5035 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
5036 // ---> hexagonal prism
5037 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
5038 prevNod[3], prevNod[4], prevNod[5],
5039 nextNod[0], nextNod[1], nextNod[2],
5040 nextNod[3], nextNod[4], nextNod[5]);
5044 case SMDSEntity_Ball:
5049 } // switch ( baseType )
5052 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
5054 if ( baseType != SMDSEntity_Polygon )
5056 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
5057 SMDS_MeshCell::applyInterlace( ind, prevNod );
5058 SMDS_MeshCell::applyInterlace( ind, nextNod );
5059 SMDS_MeshCell::applyInterlace( ind, midlNod );
5060 SMDS_MeshCell::applyInterlace( ind, itNN );
5061 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
5062 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
5064 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
5065 vector<int> quantities (nbNodes + 2);
5066 polyedre_nodes.clear();
5070 for (int inode = 0; inode < nbNodes; inode++)
5071 polyedre_nodes.push_back( prevNod[inode] );
5072 quantities.push_back( nbNodes );
5075 polyedre_nodes.push_back( nextNod[0] );
5076 for (int inode = nbNodes; inode-1; --inode )
5077 polyedre_nodes.push_back( nextNod[inode-1] );
5078 quantities.push_back( nbNodes );
5086 const int iQuad = elem->IsQuadratic();
5087 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
5089 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
5090 int inextface = (iface+1+iQuad) % nbNodes;
5091 int imid = (iface+1) % nbNodes;
5092 polyedre_nodes.push_back( prevNod[inextface] ); // 0
5093 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
5094 polyedre_nodes.push_back( prevNod[iface] ); // 1
5095 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
5097 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
5098 polyedre_nodes.push_back( nextNod[iface] ); // 2
5100 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
5101 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
5103 polyedre_nodes.push_back( nextNod[inextface] ); // 3
5104 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
5106 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
5107 if ( nbFaceNodes > 2 )
5108 quantities.push_back( nbFaceNodes );
5109 else // degenerated face
5110 polyedre_nodes.resize( prevNbNodes );
5112 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
5114 } // try to create a polyherdal prism
5117 newElems.push_back( aNewElem );
5118 myLastCreatedElems.push_back(aNewElem);
5119 srcElements.push_back( elem );
5122 // set new prev nodes
5123 for ( iNode = 0; iNode < nbNodes; iNode++ )
5124 prevNod[ iNode ] = nextNod[ iNode ];
5129 //=======================================================================
5131 * \brief Create 1D and 2D elements around swept elements
5132 * \param mapNewNodes - source nodes and ones generated from them
5133 * \param newElemsMap - source elements and ones generated from them
5134 * \param elemNewNodesMap - nodes generated from each node of each element
5135 * \param elemSet - all swept elements
5136 * \param nbSteps - number of sweeping steps
5137 * \param srcElements - to append elem for each generated element
5139 //=======================================================================
5141 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
5142 TTElemOfElemListMap & newElemsMap,
5143 TElemOfVecOfNnlmiMap & elemNewNodesMap,
5144 TIDSortedElemSet& elemSet,
5146 SMESH_SequenceOfElemPtr& srcElements)
5148 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
5149 SMESHDS_Mesh* aMesh = GetMeshDS();
5151 // Find nodes belonging to only one initial element - sweep them into edges.
5153 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
5154 for ( ; nList != mapNewNodes.end(); nList++ )
5156 const SMDS_MeshNode* node =
5157 static_cast<const SMDS_MeshNode*>( nList->first );
5158 if ( newElemsMap.count( node ))
5159 continue; // node was extruded into edge
5160 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
5161 int nbInitElems = 0;
5162 const SMDS_MeshElement* el = 0;
5163 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
5164 while ( eIt->more() && nbInitElems < 2 ) {
5165 const SMDS_MeshElement* e = eIt->next();
5166 SMDSAbs_ElementType type = e->GetType();
5167 if ( type == SMDSAbs_Volume ||
5171 if ( type > highType ) {
5178 if ( nbInitElems == 1 ) {
5179 bool NotCreateEdge = el && el->IsMediumNode(node);
5180 if(!NotCreateEdge) {
5181 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5182 list<const SMDS_MeshElement*> newEdges;
5183 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5188 // Make a ceiling for each element ie an equal element of last new nodes.
5189 // Find free links of faces - make edges and sweep them into faces.
5191 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5193 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
5194 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5195 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5197 const SMDS_MeshElement* elem = itElem->first;
5198 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5200 if(itElem->second.size()==0) continue;
5202 const bool isQuadratic = elem->IsQuadratic();
5204 if ( elem->GetType() == SMDSAbs_Edge ) {
5205 // create a ceiling edge
5206 if ( !isQuadratic ) {
5207 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5208 vecNewNodes[ 1 ]->second.back())) {
5209 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5210 vecNewNodes[ 1 ]->second.back()));
5211 srcElements.push_back( elem );
5215 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5216 vecNewNodes[ 1 ]->second.back(),
5217 vecNewNodes[ 2 ]->second.back())) {
5218 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5219 vecNewNodes[ 1 ]->second.back(),
5220 vecNewNodes[ 2 ]->second.back()));
5221 srcElements.push_back( elem );
5225 if ( elem->GetType() != SMDSAbs_Face )
5228 bool hasFreeLinks = false;
5230 TIDSortedElemSet avoidSet;
5231 avoidSet.insert( elem );
5233 set<const SMDS_MeshNode*> aFaceLastNodes;
5234 int iNode, nbNodes = vecNewNodes.size();
5235 if ( !isQuadratic ) {
5236 // loop on the face nodes
5237 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5238 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5239 // look for free links of the face
5240 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5241 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5242 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5243 // check if a link n1-n2 is free
5244 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5245 hasFreeLinks = true;
5246 // make a new edge and a ceiling for a new edge
5247 const SMDS_MeshElement* edge;
5248 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5249 myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5250 srcElements.push_back( myLastCreatedElems.back() );
5252 n1 = vecNewNodes[ iNode ]->second.back();
5253 n2 = vecNewNodes[ iNext ]->second.back();
5254 if ( !aMesh->FindEdge( n1, n2 )) {
5255 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5256 srcElements.push_back( edge );
5261 else { // elem is quadratic face
5262 int nbn = nbNodes/2;
5263 for ( iNode = 0; iNode < nbn; iNode++ ) {
5264 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5265 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5266 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5267 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5268 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5269 // check if a link is free
5270 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5271 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5272 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5273 hasFreeLinks = true;
5274 // make an edge and a ceiling for a new edge
5276 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5277 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5278 srcElements.push_back( elem );
5280 n1 = vecNewNodes[ iNode ]->second.back();
5281 n2 = vecNewNodes[ iNext ]->second.back();
5282 n3 = vecNewNodes[ iNode+nbn ]->second.back();
5283 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5284 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5285 srcElements.push_back( elem );
5289 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5290 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5294 // sweep free links into faces
5296 if ( hasFreeLinks ) {
5297 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5298 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5300 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5301 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5302 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5303 initNodeSet.insert( vecNewNodes[ iNode ]->first );
5304 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5306 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
5307 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5308 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5310 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5311 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5312 std::advance( v, volNb );
5313 // find indices of free faces of a volume and their source edges
5314 list< int > freeInd;
5315 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5316 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5317 int iF, nbF = vTool.NbFaces();
5318 for ( iF = 0; iF < nbF; iF ++ ) {
5319 if ( vTool.IsFreeFace( iF ) &&
5320 vTool.GetFaceNodes( iF, faceNodeSet ) &&
5321 initNodeSet != faceNodeSet) // except an initial face
5323 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5325 if ( faceNodeSet == initNodeSetNoCenter )
5327 freeInd.push_back( iF );
5328 // find source edge of a free face iF
5329 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5330 vector<const SMDS_MeshNode*>::iterator lastCommom;
5331 commonNodes.resize( nbNodes, 0 );
5332 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5333 initNodeSet.begin(), initNodeSet.end(),
5334 commonNodes.begin());
5335 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5336 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5338 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5340 if (SALOME::VerbosityActivated() && !srcEdges.back())
5342 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5343 << iF << " of volume #" << vTool.ID() << endl;
5347 if ( freeInd.empty() )
5350 // create wall faces for all steps;
5351 // if such a face has been already created by sweep of edge,
5352 // assure that its orientation is OK
5353 for ( int iStep = 0; iStep < nbSteps; iStep++ )
5355 vTool.Set( *v, /*ignoreCentralNodes=*/false );
5356 vTool.SetExternalNormal();
5357 const int nextShift = vTool.IsForward() ? +1 : -1;
5358 list< int >::iterator ind = freeInd.begin();
5359 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5360 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5362 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5363 int nbn = vTool.NbFaceNodes( *ind );
5364 const SMDS_MeshElement * f = 0;
5365 if ( nbn == 3 ) ///// triangle
5367 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5369 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5371 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5373 nodes[ 1 + nextShift ] };
5375 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5377 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5381 else if ( nbn == 4 ) ///// quadrangle
5383 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5385 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5387 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5388 nodes[ 2 ], nodes[ 2+nextShift ] };
5390 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5392 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5393 newOrder[ 2 ], newOrder[ 3 ]));
5396 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5398 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5400 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5402 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5404 nodes[2 + 2*nextShift],
5405 nodes[3 - 2*nextShift],
5407 nodes[3 + 2*nextShift]};
5409 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5411 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
5419 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5421 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5422 nodes[1], nodes[3], nodes[5], nodes[7] );
5424 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5426 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5427 nodes[4 - 2*nextShift],
5429 nodes[4 + 2*nextShift],
5431 nodes[5 - 2*nextShift],
5433 nodes[5 + 2*nextShift] };
5435 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5437 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5438 newOrder[ 2 ], newOrder[ 3 ],
5439 newOrder[ 4 ], newOrder[ 5 ],
5440 newOrder[ 6 ], newOrder[ 7 ]));
5443 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5445 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5446 SMDSAbs_Face, /*noMedium=*/false);
5448 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5450 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5451 nodes[4 - 2*nextShift],
5453 nodes[4 + 2*nextShift],
5455 nodes[5 - 2*nextShift],
5457 nodes[5 + 2*nextShift],
5460 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5462 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5463 newOrder[ 2 ], newOrder[ 3 ],
5464 newOrder[ 4 ], newOrder[ 5 ],
5465 newOrder[ 6 ], newOrder[ 7 ],
5469 else //////// polygon
5471 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5472 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5474 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5476 if ( !vTool.IsForward() )
5477 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5479 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5481 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5485 while ( srcElements.size() < myLastCreatedElems.size() )
5486 srcElements.push_back( *srcEdge );
5488 } // loop on free faces
5490 // go to the next volume
5492 while ( iVol++ < nbVolumesByStep ) v++;
5495 } // loop on volumes of one step
5496 } // sweep free links into faces
5498 // Make a ceiling face with a normal external to a volume
5500 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5501 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5502 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5504 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5505 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5506 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5510 lastVol.SetExternalNormal();
5511 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5512 const int nbn = lastVol.NbFaceNodes( iF );
5513 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5514 if ( !hasFreeLinks ||
5515 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5517 const vector<int>& interlace =
5518 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5519 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5521 AddElement( nodeVec, anyFace.Init( elem ));
5523 while ( srcElements.size() < myLastCreatedElems.size() )
5524 srcElements.push_back( elem );
5527 } // loop on swept elements
5530 //=======================================================================
5531 //function : RotationSweep
5533 //=======================================================================
5535 SMESH_MeshEditor::PGroupIDs
5536 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5537 const gp_Ax1& theAxis,
5538 const double theAngle,
5539 const int theNbSteps,
5540 const double theTol,
5541 const bool theMakeGroups,
5542 const bool theMakeWalls)
5546 setElemsFirst( theElemSets );
5547 myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5548 myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5550 // source elements for each generated one
5551 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5552 srcElems.reserve( theElemSets[0].size() );
5553 srcNodes.reserve( theElemSets[1].size() );
5556 aTrsf.SetRotation( theAxis, theAngle );
5558 aTrsf2.SetRotation( theAxis, theAngle/2. );
5560 gp_Lin aLine( theAxis );
5561 double aSqTol = theTol * theTol;
5563 SMESHDS_Mesh* aMesh = GetMeshDS();
5565 TNodeOfNodeListMap mapNewNodes;
5566 TElemOfVecOfNnlmiMap mapElemNewNodes;
5567 TTElemOfElemListMap newElemsMap;
5569 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5570 myMesh->NbFaces(ORDER_QUADRATIC) +
5571 myMesh->NbVolumes(ORDER_QUADRATIC) );
5572 // loop on theElemSets
5573 TIDSortedElemSet::iterator itElem;
5574 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5576 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5577 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5578 const SMDS_MeshElement* elem = *itElem;
5579 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5581 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5582 newNodesItVec.reserve( elem->NbNodes() );
5584 // loop on elem nodes
5585 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5586 while ( itN->more() )
5588 const SMDS_MeshNode* node = cast2Node( itN->next() );
5590 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5592 aXYZ.Coord( coord[0], coord[1], coord[2] );
5593 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5595 // check if a node has been already sweeped
5596 TNodeOfNodeListMapItr nIt =
5597 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5598 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5599 if ( listNewNodes.empty() )
5601 // check if we are to create medium nodes between corner ones
5602 bool needMediumNodes = false;
5603 if ( isQuadraticMesh )
5605 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5606 while (it->more() && !needMediumNodes )
5608 const SMDS_MeshElement* invElem = it->next();
5609 if ( invElem != elem && !theElems.count( invElem )) continue;
5610 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5611 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5612 needMediumNodes = true;
5617 const SMDS_MeshNode * newNode = node;
5618 for ( int i = 0; i < theNbSteps; i++ ) {
5620 if ( needMediumNodes ) // create a medium node
5622 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5623 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5624 myLastCreatedNodes.push_back(newNode);
5625 srcNodes.push_back( node );
5626 listNewNodes.push_back( newNode );
5627 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5630 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5632 // create a corner node
5633 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5634 myLastCreatedNodes.push_back(newNode);
5635 srcNodes.push_back( node );
5636 listNewNodes.push_back( newNode );
5639 listNewNodes.push_back( newNode );
5640 // if ( needMediumNodes )
5641 // listNewNodes.push_back( newNode );
5645 newNodesItVec.push_back( nIt );
5647 // make new elements
5648 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5653 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5655 PGroupIDs newGroupIDs;
5656 if ( theMakeGroups )
5657 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5662 //=======================================================================
5663 //function : ExtrusParam
5664 //purpose : standard construction
5665 //=======================================================================
5667 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5668 const int theNbSteps,
5669 const std::list<double>& theScales,
5670 const std::list<double>& theAngles,
5671 const gp_XYZ* theBasePoint,
5673 const double theTolerance):
5675 myBaseP( Precision::Infinite(), 0, 0 ),
5676 myFlags( theFlags ),
5677 myTolerance( theTolerance ),
5678 myElemsToUse( NULL )
5680 mySteps = new TColStd_HSequenceOfReal;
5681 const double stepSize = theStep.Magnitude();
5682 for (int i=1; i<=theNbSteps; i++ )
5683 mySteps->Append( stepSize );
5685 if ( !theScales.empty() )
5687 if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5688 linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5690 // add medium scales
5691 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5692 myScales.reserve( theNbSteps * 2 );
5693 myScales.push_back( 0.5 * ( *s1 + 1. ));
5694 myScales.push_back( *s1 );
5695 for ( ; s2 != theScales.end(); s1 = s2++ )
5697 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5698 myScales.push_back( *s2 );
5702 if ( !theAngles.empty() )
5704 std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5705 if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5706 linearAngleVariation( theNbSteps, angles );
5708 // accumulate angles
5711 std::list<double>::iterator a1 = angles.begin(), a2;
5712 for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5717 while ( nbAngles++ < theNbSteps )
5718 angles.push_back( angles.back() );
5720 // add medium angles
5721 a2 = angles.begin(), a1 = a2++;
5722 myAngles.push_back( 0.5 * *a1 );
5723 myAngles.push_back( *a1 );
5724 for ( ; a2 != angles.end(); a1 = a2++ )
5726 myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5727 myAngles.push_back( *a2 );
5733 myBaseP = *theBasePoint;
5736 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5737 ( theTolerance > 0 ))
5739 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5743 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5747 //=======================================================================
5748 //function : ExtrusParam
5749 //purpose : steps are given explicitly
5750 //=======================================================================
5752 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5753 Handle(TColStd_HSequenceOfReal) theSteps,
5755 const double theTolerance):
5757 mySteps( theSteps ),
5758 myFlags( theFlags ),
5759 myTolerance( theTolerance ),
5760 myElemsToUse( NULL )
5762 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5763 ( theTolerance > 0 ))
5765 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5769 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5773 //=======================================================================
5774 //function : ExtrusParam
5775 //purpose : for extrusion by normal
5776 //=======================================================================
5778 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5779 const int theNbSteps,
5783 mySteps( new TColStd_HSequenceOfReal ),
5784 myFlags( theFlags ),
5786 myElemsToUse( NULL )
5788 for (int i = 0; i < theNbSteps; i++ )
5789 mySteps->Append( theStepSize );
5793 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5797 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5801 //=======================================================================
5802 //function : ExtrusParam
5803 //purpose : for extrusion along path
5804 //=======================================================================
5806 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5807 const gp_Pnt* theBasePoint,
5808 const std::list<double>& theScales,
5809 const bool theMakeGroups )
5810 : myBaseP( Precision::Infinite(), 0, 0 ),
5811 myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5812 myPathPoints( thePoints )
5816 myBaseP = theBasePoint->XYZ();
5819 if ( !theScales.empty() )
5821 // add medium scales
5822 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5823 myScales.reserve( thePoints.size() * 2 );
5824 myScales.push_back( 0.5 * ( 1. + *s1 ));
5825 myScales.push_back( *s1 );
5826 for ( ; s2 != theScales.end(); s1 = s2++ )
5828 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5829 myScales.push_back( *s2 );
5833 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5836 //=======================================================================
5837 //function : ExtrusParam::SetElementsToUse
5838 //purpose : stores elements to use for extrusion by normal, depending on
5839 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5840 // define myBaseP for scaling
5841 //=======================================================================
5843 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5844 const TIDSortedElemSet& nodes )
5846 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5848 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5850 myBaseP.SetCoord( 0.,0.,0. );
5851 TIDSortedElemSet newNodes;
5853 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5854 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5856 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5857 TIDSortedElemSet::const_iterator itElem = elements.begin();
5858 for ( ; itElem != elements.end(); itElem++ )
5860 const SMDS_MeshElement* elem = *itElem;
5861 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5862 while ( itN->more() ) {
5863 const SMDS_MeshElement* node = itN->next();
5864 if ( newNodes.insert( node ).second )
5865 myBaseP += SMESH_NodeXYZ( node );
5869 myBaseP /= newNodes.size();
5873 //=======================================================================
5874 //function : ExtrusParam::beginStepIter
5875 //purpose : prepare iteration on steps
5876 //=======================================================================
5878 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5880 myWithMediumNodes = withMediumNodes;
5884 //=======================================================================
5885 //function : ExtrusParam::moreSteps
5886 //purpose : are there more steps?
5887 //=======================================================================
5889 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5891 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5893 //=======================================================================
5894 //function : ExtrusParam::nextStep
5895 //purpose : returns the next step
5896 //=======================================================================
5898 double SMESH_MeshEditor::ExtrusParam::nextStep()
5901 if ( !myCurSteps.empty() )
5903 res = myCurSteps.back();
5904 myCurSteps.pop_back();
5906 else if ( myNextStep <= mySteps->Length() )
5908 myCurSteps.push_back( mySteps->Value( myNextStep ));
5910 if ( myWithMediumNodes )
5912 myCurSteps.back() /= 2.;
5913 myCurSteps.push_back( myCurSteps.back() );
5920 //=======================================================================
5921 //function : ExtrusParam::makeNodesByDir
5922 //purpose : create nodes for standard extrusion
5923 //=======================================================================
5925 int SMESH_MeshEditor::ExtrusParam::
5926 makeNodesByDir( SMESHDS_Mesh* mesh,
5927 const SMDS_MeshNode* srcNode,
5928 std::list<const SMDS_MeshNode*> & newNodes,
5929 const bool makeMediumNodes)
5931 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5934 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5936 p += myDir.XYZ() * nextStep();
5937 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5938 newNodes.push_back( newNode );
5941 if ( !myScales.empty() || !myAngles.empty() )
5943 gp_XYZ center = myBaseP;
5944 gp_Ax1 ratationAxis( center, myDir );
5947 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5948 size_t i = !makeMediumNodes;
5949 for ( beginStepIter( makeMediumNodes );
5951 ++nIt, i += 1 + !makeMediumNodes )
5953 center += myDir.XYZ() * nextStep();
5955 gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5957 if ( i < myScales.size() )
5959 xyz = ( myScales[i] * ( xyz - center )) + center;
5962 if ( !myAngles.empty() )
5964 rotation.SetRotation( ratationAxis, myAngles[i] );
5965 rotation.Transforms( xyz );
5969 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5977 //=======================================================================
5978 //function : ExtrusParam::makeNodesByDirAndSew
5979 //purpose : create nodes for standard extrusion with sewing
5980 //=======================================================================
5982 int SMESH_MeshEditor::ExtrusParam::
5983 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5984 const SMDS_MeshNode* srcNode,
5985 std::list<const SMDS_MeshNode*> & newNodes,
5986 const bool makeMediumNodes)
5988 gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5991 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5993 P1 += myDir.XYZ() * nextStep();
5995 // try to search in sequence of existing nodes
5996 // if myNodes.size()>0 we 'nave to use given sequence
5997 // else - use all nodes of mesh
5998 const SMDS_MeshNode * node = 0;
5999 if ( myNodes.Length() > 0 )
6001 for ( int i = 1; i <= myNodes.Length(); i++ )
6003 SMESH_NodeXYZ P2 = myNodes.Value(i);
6004 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
6006 node = myNodes.Value(i);
6013 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
6016 SMESH_NodeXYZ P2 = itn->next();
6017 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
6026 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
6028 newNodes.push_back( node );
6035 //=======================================================================
6036 //function : ExtrusParam::makeNodesByNormal2D
6037 //purpose : create nodes for extrusion using normals of faces
6038 //=======================================================================
6040 int SMESH_MeshEditor::ExtrusParam::
6041 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
6042 const SMDS_MeshNode* srcNode,
6043 std::list<const SMDS_MeshNode*> & newNodes,
6044 const bool makeMediumNodes)
6046 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
6048 gp_XYZ p = SMESH_NodeXYZ( srcNode );
6050 // get normals to faces sharing srcNode
6051 vector< gp_XYZ > norms, baryCenters;
6052 gp_XYZ norm, avgNorm( 0,0,0 );
6053 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
6054 while ( faceIt->more() )
6056 const SMDS_MeshElement* face = faceIt->next();
6057 if ( myElemsToUse && !myElemsToUse->count( face ))
6059 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
6061 norms.push_back( norm );
6063 if ( !alongAvgNorm )
6067 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
6068 bc += SMESH_NodeXYZ( nIt->next() );
6069 baryCenters.push_back( bc / nbN );
6074 if ( norms.empty() ) return 0;
6076 double normSize = avgNorm.Modulus();
6077 if ( normSize < std::numeric_limits<double>::min() )
6080 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
6083 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
6086 avgNorm /= normSize;
6089 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
6092 double stepSize = nextStep();
6094 if ( norms.size() > 1 )
6096 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
6098 // translate plane of a face
6099 baryCenters[ iF ] += norms[ iF ] * stepSize;
6101 // find point of intersection of the face plane located at baryCenters[ iF ]
6102 // and avgNorm located at pNew
6103 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
6104 double dot = ( norms[ iF ] * avgNorm );
6105 if ( dot < std::numeric_limits<double>::min() )
6106 dot = stepSize * 1e-3;
6107 double step = -( norms[ iF ] * pNew + d ) / dot;
6108 pNew += step * avgNorm;
6113 pNew += stepSize * avgNorm;
6117 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
6118 newNodes.push_back( newNode );
6123 //=======================================================================
6124 //function : ExtrusParam::makeNodesByNormal1D
6125 //purpose : create nodes for extrusion using normals of edges
6126 //=======================================================================
6128 int SMESH_MeshEditor::ExtrusParam::
6129 makeNodesByNormal1D( SMESHDS_Mesh* /*mesh*/,
6130 const SMDS_MeshNode* /*srcNode*/,
6131 std::list<const SMDS_MeshNode*> & /*newNodes*/,
6132 const bool /*makeMediumNodes*/)
6134 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
6138 //=======================================================================
6139 //function : ExtrusParam::makeNodesAlongTrack
6140 //purpose : create nodes for extrusion along path
6141 //=======================================================================
6143 int SMESH_MeshEditor::ExtrusParam::
6144 makeNodesAlongTrack( SMESHDS_Mesh* mesh,
6145 const SMDS_MeshNode* srcNode,
6146 std::list<const SMDS_MeshNode*> & newNodes,
6147 const bool makeMediumNodes)
6149 const Standard_Real aTolAng=1.e-4;
6151 gp_Pnt aV0x = myBaseP;
6152 gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
6154 const PathPoint& aPP0 = myPathPoints[0];
6155 gp_Pnt aP0x = aPP0.myPnt;
6156 gp_Dir aDT0x= aPP0.myTgt;
6158 std::vector< gp_Pnt > centers;
6159 centers.reserve( NbSteps() * 2 );
6161 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6163 for ( size_t j = 1; j < myPathPoints.size(); ++j )
6165 const PathPoint& aPP = myPathPoints[j];
6166 const gp_Pnt& aP1x = aPP.myPnt;
6167 const gp_Dir& aDT1x = aPP.myTgt;
6170 gp_Vec aV01x( aP0x, aP1x );
6171 aTrsf.SetTranslation( aV01x );
6172 gp_Pnt aV1x = aV0x.Transformed( aTrsf );
6173 gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
6175 // rotation 1 [ T1,T0 ]
6176 Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
6177 if ( fabs( aAngleT1T0 ) > aTolAng )
6179 gp_Dir aDT1T0 = aDT1x ^ aDT0x;
6180 aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
6182 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6186 if ( aPP.myAngle != 0. )
6188 aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
6189 aPN1 = aPN1.Transformed( aTrsfRot );
6193 if ( makeMediumNodes )
6195 // create additional node
6196 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6197 const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6198 newNodes.push_back( newNode );
6201 const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6202 newNodes.push_back( newNode );
6204 centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
6205 centers.push_back( aV1x );
6214 if ( !myScales.empty() )
6217 std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
6218 for ( size_t i = !makeMediumNodes;
6219 i < myScales.size() && node != newNodes.end();
6220 i += ( 1 + !makeMediumNodes ), ++node )
6222 aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
6223 gp_Pnt aN = SMESH_NodeXYZ( *node );
6224 gp_Pnt aP = aN.Transformed( aTrsfScale );
6225 mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
6229 return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
6232 //=======================================================================
6233 //function : ExtrusionSweep
6235 //=======================================================================
6237 SMESH_MeshEditor::PGroupIDs
6238 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
6239 const gp_Vec& theStep,
6240 const int theNbSteps,
6241 TTElemOfElemListMap& newElemsMap,
6243 const double theTolerance)
6245 std::list<double> dummy;
6246 ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
6247 theFlags, theTolerance );
6248 return ExtrusionSweep( theElems, aParams, newElemsMap );
6254 //=======================================================================
6255 //function : getOriFactor
6256 //purpose : Return -1 or 1 depending on if order of given nodes corresponds to
6257 // edge curve orientation
6258 //=======================================================================
6260 double getOriFactor( const TopoDS_Edge& edge,
6261 const SMDS_MeshNode* n1,
6262 const SMDS_MeshNode* n2,
6263 SMESH_MesherHelper& helper)
6265 double u1 = helper.GetNodeU( edge, n1, n2 );
6266 double u2 = helper.GetNodeU( edge, n2, n1 );
6267 return u1 < u2 ? 1. : -1.;
6271 //=======================================================================
6272 //function : ExtrusionSweep
6274 //=======================================================================
6276 SMESH_MeshEditor::PGroupIDs
6277 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
6278 ExtrusParam& theParams,
6279 TTElemOfElemListMap& newElemsMap)
6283 setElemsFirst( theElemSets );
6284 myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
6285 myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
6287 // source elements for each generated one
6288 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6289 srcElems.reserve( theElemSets[0].size() );
6290 srcNodes.reserve( theElemSets[1].size() );
6292 const int nbSteps = theParams.NbSteps();
6293 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
6295 TNodeOfNodeListMap mapNewNodes;
6296 TElemOfVecOfNnlmiMap mapElemNewNodes;
6298 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
6299 myMesh->NbFaces(ORDER_QUADRATIC) +
6300 myMesh->NbVolumes(ORDER_QUADRATIC) );
6302 TIDSortedElemSet::iterator itElem;
6303 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6305 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
6306 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6308 // check element type
6309 const SMDS_MeshElement* elem = *itElem;
6310 if ( !elem || elem->GetType() == SMDSAbs_Volume )
6313 const size_t nbNodes = elem->NbNodes();
6314 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6315 newNodesItVec.reserve( nbNodes );
6317 // loop on elem nodes
6318 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
6319 while ( itN->more() )
6321 // check if a node has been already sweeped
6322 const SMDS_MeshNode* node = itN->next();
6323 TNodeOfNodeListMap::iterator nIt =
6324 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6325 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6326 if ( listNewNodes.empty() )
6330 // check if we are to create medium nodes between corner ones
6331 bool needMediumNodes = false;
6332 if ( isQuadraticMesh )
6334 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
6335 while (it->more() && !needMediumNodes )
6337 const SMDS_MeshElement* invElem = it->next();
6338 if ( invElem != elem && !theElems.count( invElem )) continue;
6339 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
6340 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
6341 needMediumNodes = true;
6344 // create nodes for all steps
6345 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
6347 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
6348 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
6350 myLastCreatedNodes.push_back( *newNodesIt );
6351 srcNodes.push_back( node );
6356 if ( theParams.ToMakeBoundary() )
6358 GetMeshDS()->Modified();
6359 throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
6361 break; // newNodesItVec will be shorter than nbNodes
6364 newNodesItVec.push_back( nIt );
6366 // make new elements
6367 if ( newNodesItVec.size() == nbNodes )
6368 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
6372 if ( theParams.ToMakeBoundary() ) {
6373 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
6375 PGroupIDs newGroupIDs;
6376 if ( theParams.ToMakeGroups() )
6377 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
6382 //=======================================================================
6383 //function : ExtrusionAlongTrack
6385 //=======================================================================
6386 SMESH_MeshEditor::Extrusion_Error
6387 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6388 SMESH_Mesh* theTrackMesh,
6389 SMDS_ElemIteratorPtr theTrackIterator,
6390 const SMDS_MeshNode* theN1,
6391 std::list<double>& theAngles,
6392 const bool theAngleVariation,
6393 std::list<double>& theScales,
6394 const bool theScaleVariation,
6395 const gp_Pnt* theRefPoint,
6396 const bool theMakeGroups)
6401 if ( theElements[0].empty() && theElements[1].empty() )
6402 return EXTR_NO_ELEMENTS;
6404 ASSERT( theTrackMesh );
6405 if ( ! theTrackIterator || !theTrackIterator->more() )
6406 return EXTR_NO_ELEMENTS;
6408 // 2. Get ordered nodes
6409 SMESH_MeshAlgos::TElemGroupVector branchEdges;
6410 SMESH_MeshAlgos::TNodeGroupVector branchNods;
6411 SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
6412 if ( branchEdges.empty() )
6413 return EXTR_PATH_NOT_EDGE;
6415 if ( branchEdges.size() > 1 )
6416 return EXTR_BAD_PATH_SHAPE;
6418 std::vector< const SMDS_MeshNode* >& pathNodes = branchNods[0];
6419 std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
6420 if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
6421 return EXTR_BAD_STARTING_NODE;
6423 if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
6425 // add medium nodes to pathNodes
6426 std::vector< const SMDS_MeshNode* > pathNodes2;
6427 std::vector< const SMDS_MeshElement* > pathEdges2;
6428 pathNodes2.reserve( pathNodes.size() * 2 );
6429 pathEdges2.reserve( pathEdges.size() * 2 );
6430 for ( size_t i = 0; i < pathEdges.size(); ++i )
6432 pathNodes2.push_back( pathNodes[i] );
6433 pathEdges2.push_back( pathEdges[i] );
6434 if ( pathEdges[i]->IsQuadratic() )
6436 pathNodes2.push_back( pathEdges[i]->GetNode(2) );
6437 pathEdges2.push_back( pathEdges[i] );
6440 pathNodes2.push_back( pathNodes.back() );
6441 pathEdges.swap( pathEdges2 );
6442 pathNodes.swap( pathNodes2 );
6445 // 3. Get path data at pathNodes
6447 std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
6449 if ( theAngleVariation )
6450 linearAngleVariation( points.size()-1, theAngles );
6451 if ( theScaleVariation )
6452 linearScaleVariation( points.size()-1, theScales );
6454 theAngles.push_front( 0 ); // for the 1st point that is not transformed
6455 std::list<double>::iterator angle = theAngles.begin();
6457 SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
6459 std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
6460 std::map< int, double >::iterator id2factor;
6461 SMESH_MesherHelper pathHelper( *theTrackMesh );
6462 gp_Pnt p; gp_Vec tangent;
6463 const double tol2 = gp::Resolution() * gp::Resolution();
6465 for ( size_t i = 0; i < pathNodes.size(); ++i )
6467 ExtrusParam::PathPoint & point = points[ i ];
6469 point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
6471 if ( angle != theAngles.end() )
6472 point.myAngle = *angle++;
6474 tangent.SetCoord( 0,0,0 );
6475 const int shapeID = pathNodes[ i ]->GetShapeID();
6476 const TopoDS_Shape& shape = pathMeshDS->IndexToShape( shapeID );
6477 TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
6478 switch ( shapeType )
6482 TopoDS_Edge edge = TopoDS::Edge( shape );
6483 id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
6484 if ( id2factor->second == 0 )
6486 if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6487 else id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6489 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6490 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6491 curve->D1( u, p, tangent );
6492 tangent *= id2factor->second;
6498 PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6499 while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6501 int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6502 for ( int di = -1; di <= 0; ++di )
6505 if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6507 TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6508 id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6509 if ( id2factor->second == 0 )
6512 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6514 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6516 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6517 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6519 curve->D1( u, p, du );
6520 double size2 = du.SquareMagnitude();
6521 if ( du.SquareMagnitude() > tol2 )
6523 tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6536 for ( int di = -1; di <= 1; di += 2 )
6539 if ( j < pathNodes.size() )
6541 gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6542 double size2 = dir.SquareMagnitude();
6544 tangent += dir.Divided( Sqrt( size2 )) * di;
6548 } // switch ( shapeType )
6550 if ( tangent.SquareMagnitude() < tol2 )
6551 return EXTR_CANT_GET_TANGENT;
6553 point.myTgt = tangent;
6555 } // loop on pathNodes
6558 ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6559 TTElemOfElemListMap newElemsMap;
6561 ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6566 //=======================================================================
6567 //function : linearAngleVariation
6568 //purpose : spread values over nbSteps
6569 //=======================================================================
6571 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6572 list<double>& Angles)
6574 int nbAngles = Angles.size();
6575 if( nbSteps > nbAngles && nbAngles > 0 )
6577 vector<double> theAngles(nbAngles);
6578 theAngles.assign( Angles.begin(), Angles.end() );
6581 double rAn2St = double( nbAngles ) / double( nbSteps );
6582 double angPrev = 0, angle;
6583 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6585 double angCur = rAn2St * ( iSt+1 );
6586 double angCurFloor = floor( angCur );
6587 double angPrevFloor = floor( angPrev );
6588 if ( angPrevFloor == angCurFloor )
6589 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6591 int iP = int( angPrevFloor );
6592 double angPrevCeil = ceil(angPrev);
6593 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6595 int iC = int( angCurFloor );
6596 if ( iC < nbAngles )
6597 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6599 iP = int( angPrevCeil );
6601 angle += theAngles[ iC ];
6603 res.push_back(angle);
6610 //=======================================================================
6611 //function : linearScaleVariation
6612 //purpose : spread values over nbSteps
6613 //=======================================================================
6615 void SMESH_MeshEditor::linearScaleVariation(const int theNbSteps,
6616 std::list<double>& theScales)
6618 int nbScales = theScales.size();
6619 std::vector<double> myScales;
6620 myScales.reserve( theNbSteps );
6621 std::list<double>::const_iterator scale = theScales.begin();
6622 double prevScale = 1.0;
6623 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6625 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6626 int stDelta = Max( 1, iStep - myScales.size());
6627 double scDelta = ( *scale - prevScale ) / stDelta;
6628 for ( int iStep = 0; iStep < stDelta; ++iStep )
6630 myScales.push_back( prevScale + scDelta );
6631 prevScale = myScales.back();
6635 theScales.assign( myScales.begin(), myScales.end() );
6638 //================================================================================
6640 * \brief Move or copy theElements applying theTrsf to their nodes
6641 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6642 * \param theTrsf - transformation to apply
6643 * \param theCopy - if true, create translated copies of theElems
6644 * \param theMakeGroups - if true and theCopy, create translated groups
6645 * \param theTargetMesh - mesh to copy translated elements into
6646 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6648 //================================================================================
6650 SMESH_MeshEditor::PGroupIDs
6651 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6652 const gp_Trsf& theTrsf,
6654 const bool theMakeGroups,
6655 SMESH_Mesh* theTargetMesh)
6658 myLastCreatedElems.reserve( theElems.size() );
6660 bool needReverse = false;
6661 string groupPostfix;
6662 switch ( theTrsf.Form() ) {
6665 groupPostfix = "mirrored";
6668 groupPostfix = "mirrored";
6672 groupPostfix = "mirrored";
6675 groupPostfix = "rotated";
6677 case gp_Translation:
6678 groupPostfix = "translated";
6681 groupPostfix = "scaled";
6683 case gp_CompoundTrsf: // different scale by axis
6684 groupPostfix = "scaled";
6687 needReverse = false;
6688 groupPostfix = "transformed";
6691 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6692 SMESHDS_Mesh* aMesh = GetMeshDS();
6694 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6695 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6696 SMESH_MeshEditor::ElemFeatures elemType;
6698 // map old node to new one
6699 TNodeNodeMap nodeMap;
6701 // elements sharing moved nodes; those of them which have all
6702 // nodes mirrored but are not in theElems are to be reversed
6703 TIDSortedElemSet inverseElemSet;
6705 // source elements for each generated one
6706 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6708 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6709 TIDSortedElemSet orphanNode;
6711 if ( theElems.empty() ) // transform the whole mesh
6714 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6715 while ( eIt->more() ) theElems.insert( eIt->next() );
6717 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6718 while ( nIt->more() )
6720 const SMDS_MeshNode* node = nIt->next();
6721 if ( node->NbInverseElements() == 0)
6722 orphanNode.insert( node );
6726 // loop on elements to transform nodes : first orphan nodes then elems
6727 TIDSortedElemSet::iterator itElem;
6728 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6729 for (int i=0; i<2; i++)
6730 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6732 const SMDS_MeshElement* elem = *itElem;
6736 // loop on elem nodes
6738 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6739 while ( itN->more() )
6741 const SMDS_MeshNode* node = cast2Node( itN->next() );
6742 // check if a node has been already transformed
6743 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6744 nodeMap.insert( make_pair ( node, node ));
6745 if ( !n2n_isnew.second )
6748 node->GetXYZ( coord );
6749 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6750 if ( theTargetMesh ) {
6751 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6752 n2n_isnew.first->second = newNode;
6753 myLastCreatedNodes.push_back(newNode);
6754 srcNodes.push_back( node );
6756 else if ( theCopy ) {
6757 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6758 n2n_isnew.first->second = newNode;
6759 myLastCreatedNodes.push_back(newNode);
6760 srcNodes.push_back( node );
6763 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6764 // node position on shape becomes invalid
6765 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6766 ( SMDS_SpacePosition::originSpacePosition() );
6769 // keep inverse elements
6770 if ( !theCopy && !theTargetMesh && needReverse ) {
6771 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6772 while ( invElemIt->more() ) {
6773 const SMDS_MeshElement* iel = invElemIt->next();
6774 inverseElemSet.insert( iel );
6778 } // loop on elems in { &orphanNode, &theElems };
6780 // either create new elements or reverse mirrored ones
6781 if ( !theCopy && !needReverse && !theTargetMesh )
6784 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6786 // Replicate or reverse elements
6788 std::vector<int> iForw;
6789 vector<const SMDS_MeshNode*> nodes;
6790 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6792 const SMDS_MeshElement* elem = *itElem;
6793 if ( !elem ) continue;
6795 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6796 size_t nbNodes = elem->NbNodes();
6797 if ( geomType == SMDSGeom_NONE ) continue; // node
6799 nodes.resize( nbNodes );
6801 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6803 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6807 bool allTransformed = true;
6808 int nbFaces = aPolyedre->NbFaces();
6809 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6811 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6812 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6814 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6815 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6816 if ( nodeMapIt == nodeMap.end() )
6817 allTransformed = false; // not all nodes transformed
6819 nodes.push_back((*nodeMapIt).second);
6821 if ( needReverse && allTransformed )
6822 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6824 if ( !allTransformed )
6825 continue; // not all nodes transformed
6827 else // ----------------------- the rest element types
6829 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6830 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6831 const vector<int>& i = needReverse ? iRev : iForw;
6833 // find transformed nodes
6835 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6836 while ( itN->more() ) {
6837 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6838 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6839 if ( nodeMapIt == nodeMap.end() )
6840 break; // not all nodes transformed
6841 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6843 if ( iNode != nbNodes )
6844 continue; // not all nodes transformed
6848 // copy in this or a new mesh
6849 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6850 srcElems.push_back( elem );
6853 // reverse element as it was reversed by transformation
6855 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6858 } // loop on elements
6860 if ( editor && editor != this )
6861 myLastCreatedElems.swap( editor->myLastCreatedElems );
6863 PGroupIDs newGroupIDs;
6865 if ( ( theMakeGroups && theCopy ) ||
6866 ( theMakeGroups && theTargetMesh ) )
6867 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6872 //================================================================================
6874 * \brief Make an offset mesh from a source 2D mesh
6875 * \param [in] theElements - source faces
6876 * \param [in] theValue - offset value
6877 * \param [out] theTgtMesh - a mesh to add offset elements to
6878 * \param [in] theMakeGroups - to generate groups
6879 * \return PGroupIDs - IDs of created groups. NULL means failure
6881 //================================================================================
6883 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6884 const double theValue,
6885 SMESH_Mesh* theTgtMesh,
6886 const bool theMakeGroups,
6887 const bool theCopyElements,
6888 const bool theFixSelfIntersection)
6890 SMESHDS_Mesh* meshDS = GetMeshDS();
6891 SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6892 SMESH_MeshEditor tgtEditor( theTgtMesh );
6894 SMDS_ElemIteratorPtr eIt;
6895 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6896 else eIt = SMESHUtils::elemSetIterator( theElements );
6898 SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6899 SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6900 std::unique_ptr< SMDS_Mesh > offsetMesh
6901 ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6902 theFixSelfIntersection,
6903 new2OldFaces, new2OldNodes ));
6904 if ( offsetMesh->NbElements() == 0 )
6905 return PGroupIDs(); // MakeOffset() failed
6908 if ( theTgtMesh == myMesh && !theCopyElements )
6910 // clear the source elements
6911 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6912 else eIt = SMESHUtils::elemSetIterator( theElements );
6913 while ( eIt->more() )
6914 meshDS->RemoveFreeElement( eIt->next(), 0 );
6917 // offsetMesh->Modified();
6918 // offsetMesh->CompactMesh(); // make IDs start from 1
6920 // source elements for each generated one
6921 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6922 srcElems.reserve( new2OldFaces.size() );
6923 srcNodes.reserve( new2OldNodes.size() );
6926 myLastCreatedElems.reserve( new2OldFaces.size() );
6927 myLastCreatedNodes.reserve( new2OldNodes.size() );
6929 // copy offsetMesh to theTgtMesh
6931 smIdType idShift = meshDS->MaxNodeID();
6932 for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6933 if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6936 if (!SALOME::VerbosityActivated() || n->NbInverseElements() > 0 )
6938 const SMDS_MeshNode* n2 =
6939 tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6940 myLastCreatedNodes.push_back( n2 );
6941 srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6945 ElemFeatures elemType;
6946 for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6947 if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6950 elemType.myNodes.clear();
6951 for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6953 const SMDS_MeshNode* n2 = nIt->next();
6954 elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6956 tgtEditor.AddElement( elemType.myNodes, elemType );
6957 srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6960 myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6962 PGroupIDs newGroupIDs;
6963 if ( theMakeGroups )
6964 newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6966 newGroupIDs.reset( new std::list< int > );
6971 //=======================================================================
6973 * \brief Create groups of elements made during transformation
6974 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6975 * \param elemGens - elements making corresponding myLastCreatedElems
6976 * \param postfix - to push_back to names of new groups
6977 * \param targetMesh - mesh to create groups in
6978 * \param topPresent - is there are "top" elements that are created by sweeping
6980 //=======================================================================
6982 SMESH_MeshEditor::PGroupIDs
6983 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6984 const SMESH_SequenceOfElemPtr& elemGens,
6985 const std::string& postfix,
6986 SMESH_Mesh* targetMesh,
6987 const bool topPresent)
6989 PGroupIDs newGroupIDs( new list<int> );
6990 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6992 // Sort existing groups by types and collect their names
6994 // containers to store an old group and generated new ones;
6995 // 1st new group is for result elems of different type than a source one;
6996 // 2nd new group is for same type result elems ("top" group at extrusion)
6998 using boost::make_tuple;
6999 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
7000 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
7001 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7003 set< string > groupNames;
7005 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7006 if ( !groupIt->more() ) return newGroupIDs;
7008 int newGroupID = mesh->GetGroupIds().back()+1;
7009 while ( groupIt->more() )
7011 SMESH_Group * group = groupIt->next();
7012 if ( !group ) continue;
7013 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7014 if ( !groupDS || groupDS->IsEmpty() ) continue;
7015 groupNames.insert ( group->GetName() );
7016 groupDS->SetStoreName( group->GetName() );
7017 const SMDSAbs_ElementType type = groupDS->GetType();
7018 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7019 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7020 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7021 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7024 // Loop on nodes and elements to add them in new groups
7026 vector< const SMDS_MeshElement* > resultElems;
7027 for ( int isNodes = 0; isNodes < 2; ++isNodes )
7029 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
7030 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7031 if ( gens.size() != elems.size() )
7032 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7034 // loop on created elements
7035 for (size_t iElem = 0; iElem < elems.size(); ++iElem )
7037 const SMDS_MeshElement* sourceElem = gens[ iElem ];
7038 if ( !sourceElem ) {
7039 MESSAGE("generateGroups(): NULL source element");
7042 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7043 if ( groupsOldNew.empty() ) { // no groups of this type at all
7044 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
7045 ++iElem; // skip all elements made by sourceElem
7048 // collect all elements made by the iElem-th sourceElem
7049 resultElems.clear();
7050 if ( const SMDS_MeshElement* resElem = elems[ iElem ])
7051 if ( resElem != sourceElem )
7052 resultElems.push_back( resElem );
7053 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
7054 if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
7055 if ( resElem != sourceElem )
7056 resultElems.push_back( resElem );
7058 const SMDS_MeshElement* topElem = 0;
7059 if ( isNodes ) // there must be a top element
7061 topElem = resultElems.back();
7062 resultElems.pop_back();
7066 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7067 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7068 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7070 topElem = *resElemIt;
7071 *resElemIt = 0; // erase *resElemIt
7075 // add resultElems to groups originted from ones the sourceElem belongs to
7076 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7077 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7079 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7080 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7082 // fill in a new group
7083 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7084 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7085 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7087 newGroup.Add( *resElemIt );
7089 // fill a "top" group
7092 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7093 newTopGroup.Add( topElem );
7097 } // loop on created elements
7098 }// loop on nodes and elements
7100 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7102 list<int> topGrouIds;
7103 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7105 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
7106 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7107 orderedOldNewGroups[i]->get<2>() };
7108 for ( int is2nd = 0; is2nd < 2; ++is2nd )
7110 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7111 if ( newGroupDS->IsEmpty() )
7113 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7118 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7121 const bool isTop = ( topPresent &&
7122 newGroupDS->GetType() == oldGroupDS->GetType() &&
7125 string name = oldGroupDS->GetStoreName();
7126 { // remove trailing whitespaces (issue 22599)
7127 size_t size = name.size();
7128 while ( size > 1 && isspace( name[ size-1 ]))
7130 if ( size != name.size() )
7132 name.resize( size );
7133 oldGroupDS->SetStoreName( name.c_str() );
7136 if ( !targetMesh ) {
7137 string suffix = ( isTop ? "top": postfix.c_str() );
7141 while ( !groupNames.insert( name ).second ) // name exists
7142 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7147 newGroupDS->SetStoreName( name.c_str() );
7149 // make a SMESH_Groups
7150 mesh->AddGroup( newGroupDS );
7152 topGrouIds.push_back( newGroupDS->GetID() );
7154 newGroupIDs->push_back( newGroupDS->GetID() );
7158 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7163 //================================================================================
7165 * * \brief Return list of group of nodes close to each other within theTolerance
7166 * * Search among theNodes or in the whole mesh if theNodes is empty using
7167 * * an Octree algorithm
7168 * \param [in,out] theNodes - the nodes to treat
7169 * \param [in] theTolerance - the tolerance
7170 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7171 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7172 * corner and medium nodes in separate groups
7174 //================================================================================
7176 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7177 const double theTolerance,
7178 TListOfListOfNodes & theGroupsOfNodes,
7179 bool theSeparateCornersAndMedium)
7183 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7184 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7185 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7186 theSeparateCornersAndMedium = false;
7188 TIDSortedNodeSet& corners = theNodes;
7189 TIDSortedNodeSet medium;
7191 if ( theNodes.empty() ) // get all nodes in the mesh
7193 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7194 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
7195 if ( theSeparateCornersAndMedium )
7196 while ( nIt->more() )
7198 const SMDS_MeshNode* n = nIt->next();
7199 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7200 nodeSet->insert( nodeSet->end(), n );
7203 while ( nIt->more() )
7204 theNodes.insert( theNodes.end(), nIt->next() );
7206 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7208 TIDSortedNodeSet::iterator nIt = corners.begin();
7209 while ( nIt != corners.end() )
7210 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7212 medium.insert( medium.end(), *nIt );
7213 corners.erase( nIt++ );
7221 if ( !corners.empty() )
7222 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7223 if ( !medium.empty() )
7224 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7227 //=======================================================================
7228 //function : SimplifyFace
7229 //purpose : split a chain of nodes into several closed chains
7230 //=======================================================================
7232 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7233 vector<const SMDS_MeshNode *>& poly_nodes,
7234 vector<int>& quantities) const
7236 int nbNodes = faceNodes.size();
7237 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7241 size_t prevNbQuant = quantities.size();
7243 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7244 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7245 map< const SMDS_MeshNode*, int >::iterator nInd;
7247 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7248 simpleNodes.push_back( faceNodes[0] );
7249 for ( int iCur = 1; iCur < nbNodes; iCur++ )
7251 if ( faceNodes[ iCur ] != simpleNodes.back() )
7253 int index = simpleNodes.size();
7254 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7255 int prevIndex = nInd->second;
7256 if ( prevIndex < index )
7259 int loopLen = index - prevIndex;
7262 // store the sub-loop
7263 quantities.push_back( loopLen );
7264 for ( int i = prevIndex; i < index; i++ )
7265 poly_nodes.push_back( simpleNodes[ i ]);
7267 simpleNodes.resize( prevIndex+1 );
7271 simpleNodes.push_back( faceNodes[ iCur ]);
7276 if ( simpleNodes.size() > 2 )
7278 quantities.push_back( simpleNodes.size() );
7279 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7282 return quantities.size() - prevNbQuant;
7285 //=======================================================================
7286 //function : MergeNodes
7287 //purpose : In each group, the cdr of nodes are substituted by the first one
7289 //=======================================================================
7291 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7292 const bool theAvoidMakingHoles)
7296 SMESHDS_Mesh* mesh = GetMeshDS();
7298 TNodeNodeMap nodeNodeMap; // node to replace - new node
7299 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7300 list< smIdType > rmElemIds, rmNodeIds;
7301 vector< ElemFeatures > newElemDefs;
7303 // Fill nodeNodeMap and elems
7305 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7306 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7308 list<const SMDS_MeshNode*>& nodes = *grIt;
7309 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7310 const SMDS_MeshNode* nToKeep = *nIt;
7311 for ( ++nIt; nIt != nodes.end(); nIt++ )
7313 const SMDS_MeshNode* nToRemove = *nIt;
7314 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7315 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7316 while ( invElemIt->more() ) {
7317 const SMDS_MeshElement* elem = invElemIt->next();
7323 // Apply recursive replacements (BUG 0020185)
7324 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7325 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7327 const SMDS_MeshNode* nToKeep = nnIt->second;
7328 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7329 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7331 nToKeep = nnIt_i->second;
7332 nnIt->second = nToKeep;
7333 nnIt_i = nodeNodeMap.find( nToKeep );
7337 if ( theAvoidMakingHoles )
7339 // find elements whose topology changes
7341 vector<const SMDS_MeshElement*> pbElems;
7342 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7343 for ( ; eIt != elems.end(); ++eIt )
7345 const SMDS_MeshElement* elem = *eIt;
7346 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7347 while ( itN->more() )
7349 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7350 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7351 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7353 // several nodes of elem stick
7354 pbElems.push_back( elem );
7359 // exclude from merge nodes causing spoiling element
7360 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7362 bool nodesExcluded = false;
7363 for ( size_t i = 0; i < pbElems.size(); ++i )
7365 size_t prevNbMergeNodes = nodeNodeMap.size();
7366 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7367 prevNbMergeNodes < nodeNodeMap.size() )
7368 nodesExcluded = true;
7370 if ( !nodesExcluded )
7375 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7377 const SMDS_MeshNode* nToRemove = nnIt->first;
7378 const SMDS_MeshNode* nToKeep = nnIt->second;
7379 if ( nToRemove != nToKeep )
7381 rmNodeIds.push_back( nToRemove->GetID() );
7382 AddToSameGroups( nToKeep, nToRemove, mesh );
7383 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7384 // w/o creating node in place of merged ones.
7385 SMDS_PositionPtr pos = nToRemove->GetPosition();
7386 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7387 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7388 sm->SetIsAlwaysComputed( true );
7392 // Change element nodes or remove an element
7394 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7395 for ( ; eIt != elems.end(); eIt++ )
7397 const SMDS_MeshElement* elem = *eIt;
7398 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
7399 bool marked = elem->isMarked();
7401 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7403 rmElemIds.push_back( elem->GetID() );
7405 for ( size_t i = 0; i < newElemDefs.size(); ++i )
7407 bool elemChanged = false;
7410 if ( elem->GetGeomType() == SMDSGeom_POLYHEDRA )
7411 elemChanged = mesh->ChangePolyhedronNodes( elem,
7412 newElemDefs[i].myNodes,
7413 newElemDefs[i].myPolyhedQuantities );
7415 elemChanged = mesh->ChangeElementNodes( elem,
7416 & newElemDefs[i].myNodes[0],
7417 newElemDefs[i].myNodes.size() );
7419 if ( i > 0 || !elemChanged )
7423 newElemDefs[i].SetID( elem->GetID() );
7424 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7425 if ( !keepElem ) rmElemIds.pop_back();
7429 newElemDefs[i].SetID( -1 );
7431 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7432 if ( sm && newElem )
7433 sm->AddElement( newElem );
7434 if ( elem != newElem )
7435 ReplaceElemInGroups( elem, newElem, mesh );
7436 if ( marked && newElem )
7437 newElem->setIsMarked( true );
7442 // Remove bad elements, then equal nodes (order important)
7443 Remove( rmElemIds, /*isNodes=*/false );
7444 Remove( rmNodeIds, /*isNodes=*/true );
7449 //=======================================================================
7450 //function : applyMerge
7451 //purpose : Compute new connectivity of an element after merging nodes
7452 // \param [in] elems - the element
7453 // \param [out] newElemDefs - definition(s) of result element(s)
7454 // \param [inout] nodeNodeMap - nodes to merge
7455 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
7456 // after merging (but not degenerated), removes nodes causing
7457 // the invalidity from \a nodeNodeMap.
7458 // \return bool - true if the element should be removed
7459 //=======================================================================
7461 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7462 vector< ElemFeatures >& newElemDefs,
7463 TNodeNodeMap& nodeNodeMap,
7464 const bool avoidMakingHoles )
7466 bool toRemove = false; // to remove elem
7467 int nbResElems = 1; // nb new elements
7469 newElemDefs.resize(nbResElems);
7470 newElemDefs[0].Init( elem );
7471 newElemDefs[0].myNodes.clear();
7473 set<const SMDS_MeshNode*> nodeSet;
7474 vector< const SMDS_MeshNode*> curNodes;
7475 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7478 const int nbNodes = elem->NbNodes();
7479 SMDSAbs_EntityType entity = elem->GetEntityType();
7481 curNodes.resize( nbNodes );
7482 uniqueNodes.resize( nbNodes );
7483 iRepl.resize( nbNodes );
7484 int iUnique = 0, iCur = 0, nbRepl = 0;
7486 // Get new seq of nodes
7488 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7489 while ( itN->more() )
7491 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7493 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7494 if ( nnIt != nodeNodeMap.end() ) {
7497 curNodes[ iCur ] = n;
7498 bool isUnique = nodeSet.insert( n ).second;
7500 uniqueNodes[ iUnique++ ] = n;
7502 iRepl[ nbRepl++ ] = iCur;
7506 // Analyse element topology after replacement
7508 int nbUniqueNodes = nodeSet.size();
7509 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7514 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7516 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7517 int nbCorners = nbNodes / 2;
7518 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7520 int iNext = ( iCur + 1 ) % nbCorners;
7521 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7523 int iMedium = iCur + nbCorners;
7524 vector< const SMDS_MeshNode* >::iterator i =
7525 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7527 curNodes[ iMedium ]);
7528 if ( i != uniqueNodes.end() )
7531 for ( ; i+1 != uniqueNodes.end(); ++i )
7540 case SMDSEntity_Polygon:
7541 case SMDSEntity_Quad_Polygon: // Polygon
7543 ElemFeatures* elemType = & newElemDefs[0];
7544 const bool isQuad = elemType->myIsQuad;
7546 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7547 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7549 // a polygon can divide into several elements
7550 vector<const SMDS_MeshNode *> polygons_nodes;
7551 vector<int> quantities;
7552 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7553 newElemDefs.resize( nbResElems );
7554 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7556 ElemFeatures* elemType = & newElemDefs[iface];
7557 if ( iface ) elemType->Init( elem );
7559 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7560 int nbNewNodes = quantities[iface];
7561 face_nodes.assign( polygons_nodes.begin() + inode,
7562 polygons_nodes.begin() + inode + nbNewNodes );
7563 inode += nbNewNodes;
7564 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7566 bool isValid = ( nbNewNodes % 2 == 0 );
7567 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7568 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7569 elemType->SetQuad( isValid );
7570 if ( isValid ) // put medium nodes after corners
7571 SMDS_MeshCell::applyInterlaceRev
7572 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7573 nbNewNodes ), face_nodes );
7575 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7577 nbUniqueNodes = newElemDefs[0].myNodes.size();
7581 case SMDSEntity_Polyhedra: // Polyhedral volume
7583 if ( nbUniqueNodes >= 4 )
7585 // each face has to be analyzed in order to check volume validity
7586 if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7589 int nbFaces = aPolyedre->NbFaces();
7591 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7592 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7593 vector<const SMDS_MeshNode *> faceNodes;
7597 for (int iface = 1; iface <= nbFaces; iface++)
7599 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7600 faceNodes.resize( nbFaceNodes );
7601 for (int inode = 1; inode <= nbFaceNodes; inode++)
7603 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7604 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7605 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7606 faceNode = (*nnIt).second;
7607 faceNodes[inode - 1] = faceNode;
7609 SimplifyFace(faceNodes, poly_nodes, quantities);
7612 if ( quantities.size() > 3 )
7614 // TODO: remove coincident faces
7616 nbUniqueNodes = newElemDefs[0].myNodes.size();
7624 // TODO not all the possible cases are solved. Find something more generic?
7625 case SMDSEntity_Edge: //////// EDGE
7626 case SMDSEntity_Triangle: //// TRIANGLE
7627 case SMDSEntity_Quad_Triangle:
7628 case SMDSEntity_Tetra:
7629 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7633 case SMDSEntity_Quad_Edge:
7637 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7639 if ( nbUniqueNodes < 3 )
7641 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7642 toRemove = true; // opposite nodes stick
7647 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7656 if ( nbUniqueNodes == 6 &&
7658 ( nbRepl == 1 || iRepl[1] >= 4 ))
7664 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7673 if ( nbUniqueNodes == 7 &&
7675 ( nbRepl == 1 || iRepl[1] != 8 ))
7681 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7683 if ( nbUniqueNodes == 4 ) {
7684 // ---------------------------------> tetrahedron
7685 if ( curNodes[3] == curNodes[4] &&
7686 curNodes[3] == curNodes[5] ) {
7690 else if ( curNodes[0] == curNodes[1] &&
7691 curNodes[0] == curNodes[2] ) {
7692 // bottom nodes stick: set a top before
7693 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7694 uniqueNodes[ 0 ] = curNodes [ 5 ];
7695 uniqueNodes[ 1 ] = curNodes [ 4 ];
7696 uniqueNodes[ 2 ] = curNodes [ 3 ];
7699 else if (( curNodes[0] == curNodes[3] ) +
7700 ( curNodes[1] == curNodes[4] ) +
7701 ( curNodes[2] == curNodes[5] ) == 2 ) {
7702 // a lateral face turns into a line
7706 else if ( nbUniqueNodes == 5 ) {
7707 // PENTAHEDRON --------------------> pyramid
7708 if ( curNodes[0] == curNodes[3] )
7710 uniqueNodes[ 0 ] = curNodes[ 1 ];
7711 uniqueNodes[ 1 ] = curNodes[ 4 ];
7712 uniqueNodes[ 2 ] = curNodes[ 5 ];
7713 uniqueNodes[ 3 ] = curNodes[ 2 ];
7714 uniqueNodes[ 4 ] = curNodes[ 0 ];
7717 if ( curNodes[1] == curNodes[4] )
7719 uniqueNodes[ 0 ] = curNodes[ 0 ];
7720 uniqueNodes[ 1 ] = curNodes[ 2 ];
7721 uniqueNodes[ 2 ] = curNodes[ 5 ];
7722 uniqueNodes[ 3 ] = curNodes[ 3 ];
7723 uniqueNodes[ 4 ] = curNodes[ 1 ];
7726 if ( curNodes[2] == curNodes[5] )
7728 uniqueNodes[ 0 ] = curNodes[ 0 ];
7729 uniqueNodes[ 1 ] = curNodes[ 3 ];
7730 uniqueNodes[ 2 ] = curNodes[ 4 ];
7731 uniqueNodes[ 3 ] = curNodes[ 1 ];
7732 uniqueNodes[ 4 ] = curNodes[ 2 ];
7738 case SMDSEntity_Hexa:
7740 //////////////////////////////////// HEXAHEDRON
7741 SMDS_VolumeTool hexa (elem);
7742 hexa.SetExternalNormal();
7743 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7744 //////////////////////// HEX ---> tetrahedron
7745 for ( int iFace = 0; iFace < 6; iFace++ ) {
7746 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7747 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7748 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7749 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7750 // one face turns into a point ...
7751 int pickInd = ind[ 0 ];
7752 int iOppFace = hexa.GetOppFaceIndex( iFace );
7753 ind = hexa.GetFaceNodesIndices( iOppFace );
7755 uniqueNodes.clear();
7756 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7757 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7760 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7762 if ( nbStick == 1 ) {
7763 // ... and the opposite one - into a triangle.
7765 uniqueNodes.push_back( curNodes[ pickInd ]);
7772 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7773 //////////////////////// HEX ---> prism
7774 int nbTria = 0, iTria[3];
7775 const int *ind; // indices of face nodes
7776 // look for triangular faces
7777 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7778 ind = hexa.GetFaceNodesIndices( iFace );
7779 TIDSortedNodeSet faceNodes;
7780 for ( iCur = 0; iCur < 4; iCur++ )
7781 faceNodes.insert( curNodes[ind[iCur]] );
7782 if ( faceNodes.size() == 3 )
7783 iTria[ nbTria++ ] = iFace;
7785 // check if triangles are opposite
7786 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7788 // set nodes of the bottom triangle
7789 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7791 for ( iCur = 0; iCur < 4; iCur++ )
7792 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7793 indB.push_back( ind[iCur] );
7794 if ( !hexa.IsForward() )
7795 std::swap( indB[0], indB[2] );
7796 for ( iCur = 0; iCur < 3; iCur++ )
7797 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7798 // set nodes of the top triangle
7799 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7800 for ( iCur = 0; iCur < 3; ++iCur )
7801 for ( int j = 0; j < 4; ++j )
7802 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7804 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7811 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7812 //////////////////// HEXAHEDRON ---> pyramid
7813 for ( int iFace = 0; iFace < 6; iFace++ ) {
7814 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7815 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7816 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7817 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7818 // one face turns into a point ...
7819 int iOppFace = hexa.GetOppFaceIndex( iFace );
7820 ind = hexa.GetFaceNodesIndices( iOppFace );
7821 uniqueNodes.clear();
7822 for ( iCur = 0; iCur < 4; iCur++ ) {
7823 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7826 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7828 if ( uniqueNodes.size() == 4 ) {
7829 // ... and the opposite one is a quadrangle
7831 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7832 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7840 if ( toRemove && nbUniqueNodes > 4 ) {
7841 ////////////////// HEXAHEDRON ---> polyhedron
7842 hexa.SetExternalNormal();
7843 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7844 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7845 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7846 quantities.reserve( 6 ); quantities.clear();
7847 for ( int iFace = 0; iFace < 6; iFace++ )
7849 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7850 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7851 curNodes[ind[1]] == curNodes[ind[3]] )
7854 break; // opposite nodes stick
7857 for ( iCur = 0; iCur < 4; iCur++ )
7859 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7860 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7862 if ( nodeSet.size() < 3 )
7863 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7865 quantities.push_back( nodeSet.size() );
7867 if ( quantities.size() >= 4 )
7870 nbUniqueNodes = poly_nodes.size();
7871 newElemDefs[0].SetPoly(true);
7875 } // case HEXAHEDRON
7880 } // switch ( entity )
7882 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7884 // erase from nodeNodeMap nodes whose merge spoils elem
7885 vector< const SMDS_MeshNode* > noMergeNodes;
7886 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7887 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7888 nodeNodeMap.erase( noMergeNodes[i] );
7891 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7893 uniqueNodes.resize( nbUniqueNodes );
7895 if ( !toRemove && nbResElems == 0 )
7898 newElemDefs.resize( nbResElems );
7904 // ========================================================
7905 // class : ComparableElement
7906 // purpose : allow comparing elements basing on their nodes
7907 // ========================================================
7909 class ComparableElement : public boost::container::flat_set< smIdType >
7911 typedef boost::container::flat_set< smIdType > int_set;
7913 const SMDS_MeshElement* myElem;
7915 mutable int myGroupID;
7919 ComparableElement( const SMDS_MeshElement* theElem ):
7920 myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7922 this->reserve( theElem->NbNodes() );
7923 for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7925 smIdType id = nodeIt->next()->GetID();
7931 const SMDS_MeshElement* GetElem() const { return myElem; }
7933 int& GroupID() const { return myGroupID; }
7934 //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7936 ComparableElement( const ComparableElement& theSource ) // move copy
7939 ComparableElement& src = const_cast< ComparableElement& >( theSource );
7940 (int_set&) (*this ) = std::move( src );
7941 myElem = src.myElem;
7942 mySumID = src.mySumID;
7943 myGroupID = src.myGroupID;
7946 static int HashCode(const ComparableElement& se, int limit )
7948 return ::HashCode( FromSmIdType<int>(se.mySumID), limit );
7950 static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7952 return ( se1 == se2 );
7957 //=======================================================================
7958 //function : FindEqualElements
7959 //purpose : Return list of group of elements built on the same nodes.
7960 // Search among theElements or in the whole mesh if theElements is empty
7961 //=======================================================================
7963 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet & theElements,
7964 TListOfListOfElementsID & theGroupsOfElementsID )
7968 SMDS_ElemIteratorPtr elemIt;
7969 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7970 else elemIt = SMESHUtils::elemSetIterator( theElements );
7972 typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7973 typedef std::list<smIdType> TGroupOfElems;
7974 TMapOfElements mapOfElements;
7975 std::vector< TGroupOfElems > arrayOfGroups;
7976 TGroupOfElems groupOfElems;
7978 while ( elemIt->more() )
7980 const SMDS_MeshElement* curElem = elemIt->next();
7981 if ( curElem->IsNull() )
7983 ComparableElement compElem = curElem;
7985 const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7986 if ( elemInSet.GetElem() != curElem ) // coincident elem
7988 int& iG = elemInSet.GroupID();
7991 iG = arrayOfGroups.size();
7992 arrayOfGroups.push_back( groupOfElems );
7993 arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7995 arrayOfGroups[ iG ].push_back( curElem->GetID() );
7999 groupOfElems.clear();
8000 std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8001 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8003 if ( groupIt->size() > 1 ) {
8004 //groupOfElems.sort(); -- theElements are sorted already
8005 theGroupsOfElementsID.emplace_back( *groupIt );
8010 //=======================================================================
8011 //function : MergeElements
8012 //purpose : In each given group, substitute all elements by the first one.
8013 //=======================================================================
8015 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8019 typedef list<smIdType> TListOfIDs;
8020 TListOfIDs rmElemIds; // IDs of elems to remove
8022 SMESHDS_Mesh* aMesh = GetMeshDS();
8024 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8025 while ( groupsIt != theGroupsOfElementsID.end() ) {
8026 TListOfIDs& aGroupOfElemID = *groupsIt;
8027 aGroupOfElemID.sort();
8028 int elemIDToKeep = aGroupOfElemID.front();
8029 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8030 aGroupOfElemID.pop_front();
8031 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8032 while ( idIt != aGroupOfElemID.end() ) {
8033 int elemIDToRemove = *idIt;
8034 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8035 // add the kept element in groups of removed one (PAL15188)
8036 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8037 rmElemIds.push_back( elemIDToRemove );
8043 Remove( rmElemIds, false );
8046 //=======================================================================
8047 //function : MergeEqualElements
8048 //purpose : Remove all but one of elements built on the same nodes.
8049 //=======================================================================
8051 void SMESH_MeshEditor::MergeEqualElements()
8053 TIDSortedElemSet aMeshElements; /* empty input ==
8054 to merge equal elements in the whole mesh */
8055 TListOfListOfElementsID aGroupsOfElementsID;
8056 FindEqualElements( aMeshElements, aGroupsOfElementsID );
8057 MergeElements( aGroupsOfElementsID );
8060 //=======================================================================
8061 //function : findAdjacentFace
8063 //=======================================================================
8065 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8066 const SMDS_MeshNode* n2,
8067 const SMDS_MeshElement* elem)
8069 TIDSortedElemSet elemSet, avoidSet;
8071 avoidSet.insert ( elem );
8072 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8075 //=======================================================================
8076 //function : findSegment
8077 //purpose : Return a mesh segment by two nodes one of which can be medium
8078 //=======================================================================
8080 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8081 const SMDS_MeshNode* n2)
8083 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8084 while ( it->more() )
8086 const SMDS_MeshElement* seg = it->next();
8087 if ( seg->GetNodeIndex( n2 ) >= 0 )
8093 //=======================================================================
8094 //function : FindFreeBorder
8096 //=======================================================================
8098 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8100 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8101 const SMDS_MeshNode* theSecondNode,
8102 const SMDS_MeshNode* theLastNode,
8103 list< const SMDS_MeshNode* > & theNodes,
8104 list< const SMDS_MeshElement* >& theFaces)
8106 if ( !theFirstNode || !theSecondNode )
8108 // find border face between theFirstNode and theSecondNode
8109 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8113 theFaces.push_back( curElem );
8114 theNodes.push_back( theFirstNode );
8115 theNodes.push_back( theSecondNode );
8117 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8118 //TIDSortedElemSet foundElems;
8119 bool needTheLast = ( theLastNode != 0 );
8121 vector<const SMDS_MeshNode*> nodes;
8123 while ( nStart != theLastNode ) {
8124 if ( nStart == theFirstNode )
8125 return !needTheLast;
8127 // find all free border faces sharing nStart
8129 list< const SMDS_MeshElement* > curElemList;
8130 list< const SMDS_MeshNode* > nStartList;
8131 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8132 while ( invElemIt->more() ) {
8133 const SMDS_MeshElement* e = invElemIt->next();
8134 //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
8137 nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
8138 SMDS_MeshElement::iterator() );
8139 nodes.push_back( nodes[ 0 ]);
8142 int iNode = 0, nbNodes = nodes.size() - 1;
8143 for ( iNode = 0; iNode < nbNodes; iNode++ )
8144 if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8145 ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8146 ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
8148 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
8149 curElemList.push_back( e );
8153 // analyse the found
8155 int nbNewBorders = curElemList.size();
8156 if ( nbNewBorders == 0 ) {
8157 // no free border furthermore
8158 return !needTheLast;
8160 else if ( nbNewBorders == 1 ) {
8161 // one more element found
8163 nStart = nStartList.front();
8164 curElem = curElemList.front();
8165 theFaces.push_back( curElem );
8166 theNodes.push_back( nStart );
8169 // several continuations found
8170 list< const SMDS_MeshElement* >::iterator curElemIt;
8171 list< const SMDS_MeshNode* >::iterator nStartIt;
8172 // check if one of them reached the last node
8173 if ( needTheLast ) {
8174 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8175 curElemIt!= curElemList.end();
8176 curElemIt++, nStartIt++ )
8177 if ( *nStartIt == theLastNode ) {
8178 theFaces.push_back( *curElemIt );
8179 theNodes.push_back( *nStartIt );
8183 // find the best free border by the continuations
8184 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8185 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8186 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8187 curElemIt!= curElemList.end();
8188 curElemIt++, nStartIt++ )
8190 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8191 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8192 // find one more free border
8193 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8197 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8198 // choice: clear a worse one
8199 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8200 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8201 contNodes[ iWorse ].clear();
8202 contFaces[ iWorse ].clear();
8205 if ( contNodes[0].empty() && contNodes[1].empty() )
8208 // push_back the best free border
8209 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8210 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8211 //theNodes.pop_back(); // remove nIgnore
8212 theNodes.pop_back(); // remove nStart
8213 //theFaces.pop_back(); // remove curElem
8214 theNodes.splice( theNodes.end(), *cNL );
8215 theFaces.splice( theFaces.end(), *cFL );
8218 } // several continuations found
8219 } // while ( nStart != theLastNode )
8224 //=======================================================================
8225 //function : CheckFreeBorderNodes
8226 //purpose : Return true if the tree nodes are on a free border
8227 //=======================================================================
8229 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8230 const SMDS_MeshNode* theNode2,
8231 const SMDS_MeshNode* theNode3)
8233 list< const SMDS_MeshNode* > nodes;
8234 list< const SMDS_MeshElement* > faces;
8235 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8238 //=======================================================================
8239 //function : SewFreeBorder
8241 //warning : for border-to-side sewing theSideSecondNode is considered as
8242 // the last side node and theSideThirdNode is not used
8243 //=======================================================================
8245 SMESH_MeshEditor::Sew_Error
8246 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8247 const SMDS_MeshNode* theBordSecondNode,
8248 const SMDS_MeshNode* theBordLastNode,
8249 const SMDS_MeshNode* theSideFirstNode,
8250 const SMDS_MeshNode* theSideSecondNode,
8251 const SMDS_MeshNode* theSideThirdNode,
8252 const bool theSideIsFreeBorder,
8253 const bool toCreatePolygons,
8254 const bool toCreatePolyedrs)
8258 Sew_Error aResult = SEW_OK;
8260 // ====================================
8261 // find side nodes and elements
8262 // ====================================
8264 list< const SMDS_MeshNode* > nSide[ 2 ];
8265 list< const SMDS_MeshElement* > eSide[ 2 ];
8266 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8267 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8271 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8272 nSide[0], eSide[0])) {
8273 MESSAGE(" Free Border 1 not found " );
8274 aResult = SEW_BORDER1_NOT_FOUND;
8276 if (theSideIsFreeBorder) {
8279 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8280 nSide[1], eSide[1])) {
8281 MESSAGE(" Free Border 2 not found " );
8282 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8285 if ( aResult != SEW_OK )
8288 if (!theSideIsFreeBorder) {
8292 // -------------------------------------------------------------------------
8294 // 1. If nodes to merge are not coincident, move nodes of the free border
8295 // from the coord sys defined by the direction from the first to last
8296 // nodes of the border to the correspondent sys of the side 2
8297 // 2. On the side 2, find the links most co-directed with the correspondent
8298 // links of the free border
8299 // -------------------------------------------------------------------------
8301 // 1. Since sewing may break if there are volumes to split on the side 2,
8302 // we won't move nodes but just compute new coordinates for them
8303 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8304 TNodeXYZMap nBordXYZ;
8305 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8306 list< const SMDS_MeshNode* >::iterator nBordIt;
8308 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8309 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8310 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8311 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8312 double tol2 = 1.e-8;
8313 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8314 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8315 // Need node movement.
8317 // find X and Z axes to create trsf
8318 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8320 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8322 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8325 gp_Ax3 toBordAx( Pb1, Zb, X );
8326 gp_Ax3 fromSideAx( Ps1, Zs, X );
8327 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8329 gp_Trsf toBordSys, fromSide2Sys;
8330 toBordSys.SetTransformation( toBordAx );
8331 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8332 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8335 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8336 const SMDS_MeshNode* n = *nBordIt;
8337 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8338 toBordSys.Transforms( xyz );
8339 fromSide2Sys.Transforms( xyz );
8340 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8344 // just insert nodes XYZ in the nBordXYZ map
8345 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8346 const SMDS_MeshNode* n = *nBordIt;
8347 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8351 // 2. On the side 2, find the links most co-directed with the correspondent
8352 // links of the free border
8354 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8355 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8356 sideNodes.push_back( theSideFirstNode );
8358 bool hasVolumes = false;
8359 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8360 set<long> foundSideLinkIDs, checkedLinkIDs;
8361 SMDS_VolumeTool volume;
8362 //const SMDS_MeshNode* faceNodes[ 4 ];
8364 const SMDS_MeshNode* sideNode;
8365 const SMDS_MeshElement* sideElem = 0;
8366 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8367 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8368 nBordIt = bordNodes.begin();
8370 // border node position and border link direction to compare with
8371 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8372 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8373 // choose next side node by link direction or by closeness to
8374 // the current border node:
8375 bool searchByDir = ( *nBordIt != theBordLastNode );
8377 // find the next node on the Side 2
8379 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8381 checkedLinkIDs.clear();
8382 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8384 // loop on inverse elements of current node (prevSideNode) on the Side 2
8385 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8386 while ( invElemIt->more() )
8388 const SMDS_MeshElement* elem = invElemIt->next();
8389 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8390 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8391 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8392 bool isVolume = volume.Set( elem );
8393 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8394 if ( isVolume ) // --volume
8396 else if ( elem->GetType() == SMDSAbs_Face ) { // --face
8397 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8398 SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
8399 while ( nIt->more() ) {
8400 nodes[ iNode ] = cast2Node( nIt->next() );
8401 if ( nodes[ iNode++ ] == prevSideNode )
8402 iPrevNode = iNode - 1;
8404 // there are 2 links to check
8409 // loop on links, to be precise, on the second node of links
8410 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8411 const SMDS_MeshNode* n = nodes[ iNode ];
8413 if ( !volume.IsLinked( n, prevSideNode ))
8417 if ( iNode ) // a node before prevSideNode
8418 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8419 else // a node after prevSideNode
8420 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8422 // check if this link was already used
8423 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8424 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8425 if (!isJustChecked &&
8426 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8428 // test a link geometrically
8429 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8430 bool linkIsBetter = false;
8431 double dot = 0.0, dist = 0.0;
8432 if ( searchByDir ) { // choose most co-directed link
8433 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8434 linkIsBetter = ( dot > maxDot );
8436 else { // choose link with the node closest to bordPos
8437 dist = ( nextXYZ - bordPos ).SquareModulus();
8438 linkIsBetter = ( dist < minDist );
8440 if ( linkIsBetter ) {
8449 } // loop on inverse elements of prevSideNode
8452 MESSAGE(" Can't find path by links of the Side 2 ");
8453 return SEW_BAD_SIDE_NODES;
8455 sideNodes.push_back( sideNode );
8456 sideElems.push_back( sideElem );
8457 foundSideLinkIDs.insert ( linkID );
8458 prevSideNode = sideNode;
8460 if ( *nBordIt == theBordLastNode )
8461 searchByDir = false;
8463 // find the next border link to compare with
8464 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8465 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8466 // move to next border node if sideNode is before forward border node (bordPos)
8467 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8468 prevBordNode = *nBordIt;
8470 bordPos = nBordXYZ[ *nBordIt ];
8471 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8472 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8476 while ( sideNode != theSideSecondNode );
8478 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8479 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8480 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8482 } // end nodes search on the side 2
8484 // ============================
8485 // sew the border to the side 2
8486 // ============================
8488 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8489 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8491 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8492 if ( toMergeConformal && toCreatePolygons )
8494 // do not merge quadrangles if polygons are OK (IPAL0052824)
8495 eIt[0] = eSide[0].begin();
8496 eIt[1] = eSide[1].begin();
8497 bool allQuads[2] = { true, true };
8498 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8499 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8500 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8502 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8505 TListOfListOfNodes nodeGroupsToMerge;
8506 if (( toMergeConformal ) ||
8507 ( theSideIsFreeBorder && !theSideThirdNode )) {
8509 // all nodes are to be merged
8511 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8512 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8513 nIt[0]++, nIt[1]++ )
8515 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8516 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8517 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8522 // insert new nodes into the border and the side to get equal nb of segments
8524 // get normalized parameters of nodes on the borders
8525 vector< double > param[ 2 ];
8526 param[0].resize( maxNbNodes );
8527 param[1].resize( maxNbNodes );
8529 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8530 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8531 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8532 const SMDS_MeshNode* nPrev = *nIt;
8533 double bordLength = 0;
8534 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8535 const SMDS_MeshNode* nCur = *nIt;
8536 gp_XYZ segment (nCur->X() - nPrev->X(),
8537 nCur->Y() - nPrev->Y(),
8538 nCur->Z() - nPrev->Z());
8539 double segmentLen = segment.Modulus();
8540 bordLength += segmentLen;
8541 param[ iBord ][ iNode ] = bordLength;
8544 // normalize within [0,1]
8545 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8546 param[ iBord ][ iNode ] /= bordLength;
8550 // loop on border segments
8551 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8552 int i[ 2 ] = { 0, 0 };
8553 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8554 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8556 // element can be split while iterating on border if it has two edges in the border
8557 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8558 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8560 TElemOfNodeListMap insertMap;
8561 TElemOfNodeListMap::iterator insertMapIt;
8563 // key: elem to insert nodes into
8564 // value: 2 nodes to insert between + nodes to be inserted
8566 bool next[ 2 ] = { false, false };
8568 // find min adjacent segment length after sewing
8569 double nextParam = 10., prevParam = 0;
8570 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8571 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8572 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8573 if ( i[ iBord ] > 0 )
8574 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8576 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8577 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8578 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8580 // choose to insert or to merge nodes
8581 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8582 if ( Abs( du ) <= minSegLen * 0.2 ) {
8585 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8586 const SMDS_MeshNode* n0 = *nIt[0];
8587 const SMDS_MeshNode* n1 = *nIt[1];
8588 nodeGroupsToMerge.back().push_back( n1 );
8589 nodeGroupsToMerge.back().push_back( n0 );
8590 // position of node of the border changes due to merge
8591 param[ 0 ][ i[0] ] += du;
8592 // move n1 for the sake of elem shape evaluation during insertion.
8593 // n1 will be removed by MergeNodes() anyway
8594 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8595 next[0] = next[1] = true;
8600 int intoBord = ( du < 0 ) ? 0 : 1;
8601 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8602 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8603 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8604 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8605 if ( intoBord == 1 ) {
8606 // move node of the border to be on a link of elem of the side
8607 SMESH_NodeXYZ p1( n1 ), p2( n2 );
8608 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8609 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8610 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8612 elemReplaceMapIt = elemReplaceMap.find( elem );
8613 if ( elemReplaceMapIt != elemReplaceMap.end() )
8614 elem = elemReplaceMapIt->second;
8616 insertMapIt = insertMap.find( elem );
8617 bool notFound = ( insertMapIt == insertMap.end() );
8618 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8620 // insert into another link of the same element:
8621 // 1. perform insertion into the other link of the elem
8622 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8623 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8624 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8625 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8626 // 2. perform insertion into the link of adjacent faces
8627 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8628 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8630 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8631 InsertNodesIntoLink( seg, n12, n22, nodeList );
8633 if (toCreatePolyedrs) {
8634 // perform insertion into the links of adjacent volumes
8635 UpdateVolumes(n12, n22, nodeList);
8637 // 3. find an element appeared on n1 and n2 after the insertion
8638 insertMap.erase( insertMapIt );
8639 const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8640 elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8643 if ( notFound || otherLink ) {
8644 // add element and nodes of the side into the insertMap
8645 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8646 (*insertMapIt).second.push_back( n1 );
8647 (*insertMapIt).second.push_back( n2 );
8649 // add node to be inserted into elem
8650 (*insertMapIt).second.push_back( nIns );
8651 next[ 1 - intoBord ] = true;
8654 // go to the next segment
8655 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8656 if ( next[ iBord ] ) {
8657 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8659 nPrev[ iBord ] = *nIt[ iBord ];
8660 nIt[ iBord ]++; i[ iBord ]++;
8664 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8666 // perform insertion of nodes into elements
8668 for (insertMapIt = insertMap.begin();
8669 insertMapIt != insertMap.end();
8672 const SMDS_MeshElement* elem = (*insertMapIt).first;
8673 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8674 if ( nodeList.size() < 3 ) continue;
8675 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8676 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8678 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8680 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8681 InsertNodesIntoLink( seg, n1, n2, nodeList );
8684 if ( !theSideIsFreeBorder ) {
8685 // look for and insert nodes into the faces adjacent to elem
8686 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8687 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8690 if (toCreatePolyedrs) {
8691 // perform insertion into the links of adjacent volumes
8692 UpdateVolumes(n1, n2, nodeList);
8695 } // end: insert new nodes
8697 MergeNodes ( nodeGroupsToMerge );
8700 // Remove coincident segments
8703 TIDSortedElemSet segments;
8704 SMESH_SequenceOfElemPtr newFaces;
8705 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8707 if ( !myLastCreatedElems[i] ) continue;
8708 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8709 segments.insert( segments.end(), myLastCreatedElems[i] );
8711 newFaces.push_back( myLastCreatedElems[i] );
8713 // get segments adjacent to merged nodes
8714 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8715 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8717 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8718 if ( nodes.front()->IsNull() ) continue;
8719 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8720 while ( segIt->more() )
8721 segments.insert( segIt->next() );
8725 TListOfListOfElementsID equalGroups;
8726 if ( !segments.empty() )
8727 FindEqualElements( segments, equalGroups );
8728 if ( !equalGroups.empty() )
8730 // remove from segments those that will be removed
8731 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8732 for ( ; itGroups != equalGroups.end(); ++itGroups )
8734 list< smIdType >& group = *itGroups;
8735 list< smIdType >::iterator id = group.begin();
8736 for ( ++id; id != group.end(); ++id )
8737 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8738 segments.erase( seg );
8740 // remove equal segments
8741 MergeElements( equalGroups );
8743 // restore myLastCreatedElems
8744 myLastCreatedElems = newFaces;
8745 TIDSortedElemSet::iterator seg = segments.begin();
8746 for ( ; seg != segments.end(); ++seg )
8747 myLastCreatedElems.push_back( *seg );
8753 //=======================================================================
8754 //function : InsertNodesIntoLink
8755 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8756 // and theBetweenNode2 and split theElement
8757 //=======================================================================
8759 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8760 const SMDS_MeshNode* theBetweenNode1,
8761 const SMDS_MeshNode* theBetweenNode2,
8762 list<const SMDS_MeshNode*>& theNodesToInsert,
8763 const bool toCreatePoly)
8765 if ( !theElement ) return;
8767 SMESHDS_Mesh *aMesh = GetMeshDS();
8768 vector<const SMDS_MeshElement*> newElems;
8770 if ( theElement->GetType() == SMDSAbs_Edge )
8772 theNodesToInsert.push_front( theBetweenNode1 );
8773 theNodesToInsert.push_back ( theBetweenNode2 );
8774 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8775 const SMDS_MeshNode* n1 = *n;
8776 for ( ++n; n != theNodesToInsert.end(); ++n )
8778 const SMDS_MeshNode* n2 = *n;
8779 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8780 AddToSameGroups( seg, theElement, aMesh );
8782 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8785 theNodesToInsert.pop_front();
8786 theNodesToInsert.pop_back();
8788 if ( theElement->IsQuadratic() ) // add a not split part
8790 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8791 theElement->end_nodes() );
8792 int iOther = 0, nbN = nodes.size();
8793 for ( ; iOther < nbN; ++iOther )
8794 if ( nodes[iOther] != theBetweenNode1 &&
8795 nodes[iOther] != theBetweenNode2 )
8799 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8800 AddToSameGroups( seg, theElement, aMesh );
8802 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8804 else if ( iOther == 2 )
8806 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8807 AddToSameGroups( seg, theElement, aMesh );
8809 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8812 // treat new elements
8813 for ( size_t i = 0; i < newElems.size(); ++i )
8816 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8817 myLastCreatedElems.push_back( newElems[i] );
8819 ReplaceElemInGroups( theElement, newElems, aMesh );
8820 aMesh->RemoveElement( theElement );
8823 } // if ( theElement->GetType() == SMDSAbs_Edge )
8825 const SMDS_MeshElement* theFace = theElement;
8826 if ( theFace->GetType() != SMDSAbs_Face ) return;
8828 // find indices of 2 link nodes and of the rest nodes
8829 int iNode = 0, il1, il2, i3, i4;
8830 il1 = il2 = i3 = i4 = -1;
8831 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8833 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8834 while ( nodeIt->more() ) {
8835 const SMDS_MeshNode* n = nodeIt->next();
8836 if ( n == theBetweenNode1 )
8838 else if ( n == theBetweenNode2 )
8844 nodes[ iNode++ ] = n;
8846 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8849 // arrange link nodes to go one after another regarding the face orientation
8850 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8851 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8856 aNodesToInsert.reverse();
8858 // check that not link nodes of a quadrangles are in good order
8859 int nbFaceNodes = theFace->NbNodes();
8860 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8866 if (toCreatePoly || theFace->IsPoly()) {
8869 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8871 // add nodes of face up to first node of link
8873 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8874 while ( nodeIt->more() && !isFLN ) {
8875 const SMDS_MeshNode* n = nodeIt->next();
8876 poly_nodes[iNode++] = n;
8877 isFLN = ( n == nodes[il1] );
8879 // add nodes to insert
8880 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8881 for (; nIt != aNodesToInsert.end(); nIt++) {
8882 poly_nodes[iNode++] = *nIt;
8884 // add nodes of face starting from last node of link
8885 while ( nodeIt->more() ) {
8886 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8887 poly_nodes[iNode++] = n;
8891 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8894 else if ( !theFace->IsQuadratic() )
8896 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8897 int nbLinkNodes = 2 + aNodesToInsert.size();
8898 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8899 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8900 linkNodes[ 0 ] = nodes[ il1 ];
8901 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8902 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8903 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8904 linkNodes[ iNode++ ] = *nIt;
8906 // decide how to split a quadrangle: compare possible variants
8907 // and choose which of splits to be a quadrangle
8908 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8909 if ( nbFaceNodes == 3 ) {
8910 iBestQuad = nbSplits;
8913 else if ( nbFaceNodes == 4 ) {
8914 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8915 double aBestRate = DBL_MAX;
8916 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8918 double aBadRate = 0;
8919 // evaluate elements quality
8920 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8921 if ( iSplit == iQuad ) {
8922 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8926 aBadRate += getBadRate( &quad, aCrit );
8929 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8931 nodes[ iSplit < iQuad ? i4 : i3 ]);
8932 aBadRate += getBadRate( &tria, aCrit );
8936 if ( aBadRate < aBestRate ) {
8938 aBestRate = aBadRate;
8943 // create new elements
8945 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8947 if ( iSplit == iBestQuad )
8948 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8953 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8955 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8958 const SMDS_MeshNode* newNodes[ 4 ];
8959 newNodes[ 0 ] = linkNodes[ i1 ];
8960 newNodes[ 1 ] = linkNodes[ i2 ];
8961 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8962 newNodes[ 3 ] = nodes[ i4 ];
8963 if (iSplit == iBestQuad)
8964 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8966 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8968 } // end if(!theFace->IsQuadratic())
8970 else { // theFace is quadratic
8971 // we have to split theFace on simple triangles and one simple quadrangle
8973 int nbshift = tmp*2;
8974 // shift nodes in nodes[] by nbshift
8976 for(i=0; i<nbshift; i++) {
8977 const SMDS_MeshNode* n = nodes[0];
8978 for(j=0; j<nbFaceNodes-1; j++) {
8979 nodes[j] = nodes[j+1];
8981 nodes[nbFaceNodes-1] = n;
8983 il1 = il1 - nbshift;
8984 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8985 // n0 n1 n2 n0 n1 n2
8986 // +-----+-----+ +-----+-----+
8995 // create new elements
8997 if ( nbFaceNodes == 6 ) { // quadratic triangle
8998 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8999 if ( theFace->IsMediumNode(nodes[il1]) ) {
9000 // create quadrangle
9001 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9007 // create quadrangle
9008 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9014 else { // nbFaceNodes==8 - quadratic quadrangle
9015 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9016 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9017 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9018 if ( theFace->IsMediumNode( nodes[ il1 ])) {
9019 // create quadrangle
9020 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9026 // create quadrangle
9027 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9033 // create needed triangles using n1,n2,n3 and inserted nodes
9034 int nbn = 2 + aNodesToInsert.size();
9035 vector<const SMDS_MeshNode*> aNodes(nbn);
9036 aNodes[0 ] = nodes[n1];
9037 aNodes[nbn-1] = nodes[n2];
9038 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9039 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9040 aNodes[iNode++] = *nIt;
9042 for ( i = 1; i < nbn; i++ )
9043 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9046 // remove the old face
9047 for ( size_t i = 0; i < newElems.size(); ++i )
9050 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9051 myLastCreatedElems.push_back( newElems[i] );
9053 ReplaceElemInGroups( theFace, newElems, aMesh );
9054 aMesh->RemoveElement(theFace);
9056 } // InsertNodesIntoLink()
9058 //=======================================================================
9059 //function : UpdateVolumes
9061 //=======================================================================
9063 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9064 const SMDS_MeshNode* theBetweenNode2,
9065 list<const SMDS_MeshNode*>& theNodesToInsert)
9069 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9070 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9071 const SMDS_MeshElement* elem = invElemIt->next();
9073 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9074 SMDS_VolumeTool aVolume (elem);
9075 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9078 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9079 int iface, nbFaces = aVolume.NbFaces();
9080 vector<const SMDS_MeshNode *> poly_nodes;
9081 vector<int> quantities (nbFaces);
9083 for (iface = 0; iface < nbFaces; iface++) {
9084 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9085 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9086 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9088 for (int inode = 0; inode < nbFaceNodes; inode++) {
9089 poly_nodes.push_back(faceNodes[inode]);
9091 if (nbInserted == 0) {
9092 if (faceNodes[inode] == theBetweenNode1) {
9093 if (faceNodes[inode + 1] == theBetweenNode2) {
9094 nbInserted = theNodesToInsert.size();
9096 // add nodes to insert
9097 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9098 for (; nIt != theNodesToInsert.end(); nIt++) {
9099 poly_nodes.push_back(*nIt);
9103 else if (faceNodes[inode] == theBetweenNode2) {
9104 if (faceNodes[inode + 1] == theBetweenNode1) {
9105 nbInserted = theNodesToInsert.size();
9107 // add nodes to insert in reversed order
9108 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9110 for (; nIt != theNodesToInsert.begin(); nIt--) {
9111 poly_nodes.push_back(*nIt);
9113 poly_nodes.push_back(*nIt);
9120 quantities[iface] = nbFaceNodes + nbInserted;
9123 // Replace the volume
9124 SMESHDS_Mesh *aMesh = GetMeshDS();
9126 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9128 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9129 myLastCreatedElems.push_back( newElem );
9130 ReplaceElemInGroups( elem, newElem, aMesh );
9132 aMesh->RemoveElement( elem );
9138 //================================================================================
9140 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9142 //================================================================================
9144 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9145 vector<const SMDS_MeshNode *> & nodes,
9146 vector<int> & nbNodeInFaces )
9149 nbNodeInFaces.clear();
9150 SMDS_VolumeTool vTool ( elem );
9151 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9153 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9154 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9155 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9160 //=======================================================================
9162 * \brief Convert elements contained in a sub-mesh to quadratic
9163 * \return int - nb of checked elements
9165 //=======================================================================
9167 smIdType SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9168 SMESH_MesherHelper& theHelper,
9169 const bool theForce3d)
9171 //MESSAGE("convertElemToQuadratic");
9172 smIdType nbElem = 0;
9173 if( !theSm ) return nbElem;
9175 vector<int> nbNodeInFaces;
9176 vector<const SMDS_MeshNode *> nodes;
9177 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9178 while(ElemItr->more())
9181 const SMDS_MeshElement* elem = ElemItr->next();
9182 if( !elem ) continue;
9184 // analyse a necessity of conversion
9185 const SMDSAbs_ElementType aType = elem->GetType();
9186 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9188 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9189 bool hasCentralNodes = false;
9190 if ( elem->IsQuadratic() )
9193 switch ( aGeomType ) {
9194 case SMDSEntity_Quad_Triangle:
9195 case SMDSEntity_Quad_Quadrangle:
9196 case SMDSEntity_Quad_Hexa:
9197 case SMDSEntity_Quad_Penta:
9198 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9200 case SMDSEntity_BiQuad_Triangle:
9201 case SMDSEntity_BiQuad_Quadrangle:
9202 case SMDSEntity_TriQuad_Hexa:
9203 case SMDSEntity_BiQuad_Penta:
9204 alreadyOK = theHelper.GetIsBiQuadratic();
9205 hasCentralNodes = true;
9210 // take into account already present medium nodes
9212 case SMDSAbs_Volume:
9213 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9215 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9217 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9223 // get elem data needed to re-create it
9225 const smIdType id = elem->GetID();
9226 const int nbNodes = elem->NbCornerNodes();
9227 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9228 if ( aGeomType == SMDSEntity_Polyhedra )
9229 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
9230 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9231 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9233 // remove a linear element
9234 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9236 // remove central nodes of biquadratic elements (biquad->quad conversion)
9237 if ( hasCentralNodes )
9238 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9239 if ( nodes[i]->NbInverseElements() == 0 )
9240 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9242 const SMDS_MeshElement* NewElem = 0;
9248 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9256 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9259 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9262 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9266 case SMDSAbs_Volume :
9270 case SMDSEntity_Tetra:
9271 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9273 case SMDSEntity_Pyramid:
9274 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9276 case SMDSEntity_Penta:
9277 case SMDSEntity_Quad_Penta:
9278 case SMDSEntity_BiQuad_Penta:
9279 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9281 case SMDSEntity_Hexa:
9282 case SMDSEntity_Quad_Hexa:
9283 case SMDSEntity_TriQuad_Hexa:
9284 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9285 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9287 case SMDSEntity_Hexagonal_Prism:
9289 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9296 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9297 if( NewElem && NewElem->getshapeId() < 1 )
9298 theSm->AddElement( NewElem );
9302 //=======================================================================
9303 //function : ConvertToQuadratic
9305 //=======================================================================
9307 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9309 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9310 SMESHDS_Mesh* meshDS = GetMeshDS();
9312 SMESH_MesherHelper aHelper(*myMesh);
9314 aHelper.SetIsQuadratic( true );
9315 aHelper.SetIsBiQuadratic( theToBiQuad );
9316 aHelper.SetElementsOnShape(true);
9317 aHelper.ToFixNodeParameters( true );
9319 // convert elements assigned to sub-meshes
9320 smIdType nbCheckedElems = 0;
9321 if ( myMesh->HasShapeToMesh() )
9323 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9325 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9326 while ( smIt->more() ) {
9327 SMESH_subMesh* sm = smIt->next();
9328 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9329 aHelper.SetSubShape( sm->GetSubShape() );
9330 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9336 // convert elements NOT assigned to sub-meshes
9337 smIdType totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9338 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9340 aHelper.SetElementsOnShape(false);
9341 SMESHDS_SubMesh *smDS = 0;
9344 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9345 while( aEdgeItr->more() )
9347 const SMDS_MeshEdge* edge = aEdgeItr->next();
9348 if ( !edge->IsQuadratic() )
9350 smIdType id = edge->GetID();
9351 const SMDS_MeshNode* n1 = edge->GetNode(0);
9352 const SMDS_MeshNode* n2 = edge->GetNode(1);
9354 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9356 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9357 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9361 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9366 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9367 while( aFaceItr->more() )
9369 const SMDS_MeshFace* face = aFaceItr->next();
9370 if ( !face ) continue;
9372 const SMDSAbs_EntityType type = face->GetEntityType();
9376 case SMDSEntity_Quad_Triangle:
9377 case SMDSEntity_Quad_Quadrangle:
9378 alreadyOK = !theToBiQuad;
9379 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9381 case SMDSEntity_BiQuad_Triangle:
9382 case SMDSEntity_BiQuad_Quadrangle:
9383 alreadyOK = theToBiQuad;
9384 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9386 default: alreadyOK = false;
9391 const smIdType id = face->GetID();
9392 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9394 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9396 SMDS_MeshFace * NewFace = 0;
9399 case SMDSEntity_Triangle:
9400 case SMDSEntity_Quad_Triangle:
9401 case SMDSEntity_BiQuad_Triangle:
9402 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9403 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9404 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9407 case SMDSEntity_Quadrangle:
9408 case SMDSEntity_Quad_Quadrangle:
9409 case SMDSEntity_BiQuad_Quadrangle:
9410 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9411 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9412 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9416 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9418 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9422 vector<int> nbNodeInFaces;
9423 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9424 while(aVolumeItr->more())
9426 const SMDS_MeshVolume* volume = aVolumeItr->next();
9427 if ( !volume ) continue;
9429 const SMDSAbs_EntityType type = volume->GetEntityType();
9430 if ( volume->IsQuadratic() )
9435 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9436 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9437 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
9438 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9439 default: alreadyOK = true;
9443 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9447 const smIdType id = volume->GetID();
9448 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9449 if ( type == SMDSEntity_Polyhedra )
9450 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
9451 else if ( type == SMDSEntity_Hexagonal_Prism )
9452 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9454 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9456 SMDS_MeshVolume * NewVolume = 0;
9459 case SMDSEntity_Tetra:
9460 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9462 case SMDSEntity_Hexa:
9463 case SMDSEntity_Quad_Hexa:
9464 case SMDSEntity_TriQuad_Hexa:
9465 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9466 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9467 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9468 if ( nodes[i]->NbInverseElements() == 0 )
9469 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9471 case SMDSEntity_Pyramid:
9472 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9473 nodes[3], nodes[4], id, theForce3d);
9475 case SMDSEntity_Penta:
9476 case SMDSEntity_Quad_Penta:
9477 case SMDSEntity_BiQuad_Penta:
9478 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9479 nodes[3], nodes[4], nodes[5], id, theForce3d);
9480 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9481 if ( nodes[i]->NbInverseElements() == 0 )
9482 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9484 case SMDSEntity_Hexagonal_Prism:
9486 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9488 ReplaceElemInGroups(volume, NewVolume, meshDS);
9493 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9494 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9495 // aHelper.FixQuadraticElements(myError);
9496 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9500 //================================================================================
9502 * \brief Makes given elements quadratic
9503 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9504 * \param theElements - elements to make quadratic
9506 //================================================================================
9508 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9509 TIDSortedElemSet& theElements,
9510 const bool theToBiQuad)
9512 if ( theElements.empty() ) return;
9514 // we believe that all theElements are of the same type
9515 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9517 // get all nodes shared by theElements
9518 TIDSortedNodeSet allNodes;
9519 TIDSortedElemSet::iterator eIt = theElements.begin();
9520 for ( ; eIt != theElements.end(); ++eIt )
9521 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9523 // complete theElements with elements of lower dim whose all nodes are in allNodes
9525 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9526 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9527 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9528 for ( ; nIt != allNodes.end(); ++nIt )
9530 const SMDS_MeshNode* n = *nIt;
9531 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9532 while ( invIt->more() )
9534 const SMDS_MeshElement* e = invIt->next();
9535 const SMDSAbs_ElementType type = e->GetType();
9536 if ( e->IsQuadratic() )
9538 quadAdjacentElems[ type ].insert( e );
9541 switch ( e->GetEntityType() ) {
9542 case SMDSEntity_Quad_Triangle:
9543 case SMDSEntity_Quad_Quadrangle:
9544 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9545 case SMDSEntity_BiQuad_Triangle:
9546 case SMDSEntity_BiQuad_Quadrangle:
9547 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9548 default: alreadyOK = true;
9553 if ( type >= elemType )
9554 continue; // same type or more complex linear element
9556 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9557 continue; // e is already checked
9561 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9562 while ( nodeIt->more() && allIn )
9563 allIn = allNodes.count( nodeIt->next() );
9565 theElements.insert(e );
9569 SMESH_MesherHelper helper(*myMesh);
9570 helper.SetIsQuadratic( true );
9571 helper.SetIsBiQuadratic( theToBiQuad );
9573 // add links of quadratic adjacent elements to the helper
9575 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9576 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9577 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9579 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9581 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9582 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9583 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9585 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9587 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9588 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9589 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9591 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9594 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9596 SMESHDS_Mesh* meshDS = GetMeshDS();
9597 SMESHDS_SubMesh* smDS = 0;
9598 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9600 const SMDS_MeshElement* elem = *eIt;
9603 int nbCentralNodes = 0;
9604 switch ( elem->GetEntityType() ) {
9605 // linear convertible
9606 case SMDSEntity_Edge:
9607 case SMDSEntity_Triangle:
9608 case SMDSEntity_Quadrangle:
9609 case SMDSEntity_Tetra:
9610 case SMDSEntity_Pyramid:
9611 case SMDSEntity_Hexa:
9612 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9613 // quadratic that can become bi-quadratic
9614 case SMDSEntity_Quad_Triangle:
9615 case SMDSEntity_Quad_Quadrangle:
9616 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9618 case SMDSEntity_BiQuad_Triangle:
9619 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9620 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9622 default: alreadyOK = true;
9624 if ( alreadyOK ) continue;
9626 const SMDSAbs_ElementType type = elem->GetType();
9627 const smIdType id = elem->GetID();
9628 const int nbNodes = elem->NbCornerNodes();
9629 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9631 helper.SetSubShape( elem->getshapeId() );
9633 if ( !smDS || !smDS->Contains( elem ))
9634 smDS = meshDS->MeshElements( elem->getshapeId() );
9635 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9637 SMDS_MeshElement * newElem = 0;
9640 case 4: // cases for most frequently used element types go first (for optimization)
9641 if ( type == SMDSAbs_Volume )
9642 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9644 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9647 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9648 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9651 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9654 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9657 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9658 nodes[4], id, theForce3d);
9661 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9662 nodes[4], nodes[5], id, theForce3d);
9666 ReplaceElemInGroups( elem, newElem, meshDS);
9667 if( newElem && smDS )
9668 smDS->AddElement( newElem );
9670 // remove central nodes
9671 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9672 if ( nodes[i]->NbInverseElements() == 0 )
9673 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9675 } // loop on theElements
9678 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9679 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9680 // helper.FixQuadraticElements( myError );
9681 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9685 //=======================================================================
9687 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9688 * \return smIdType - nb of checked elements
9690 //=======================================================================
9692 smIdType SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9693 SMDS_ElemIteratorPtr theItr,
9694 const int /*theShapeID*/)
9696 smIdType nbElem = 0;
9697 SMESHDS_Mesh* meshDS = GetMeshDS();
9698 ElemFeatures elemType;
9699 vector<const SMDS_MeshNode *> nodes;
9701 while( theItr->more() )
9703 const SMDS_MeshElement* elem = theItr->next();
9705 if( elem && elem->IsQuadratic())
9708 int nbCornerNodes = elem->NbCornerNodes();
9709 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9711 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9713 //remove a quadratic element
9714 if ( !theSm || !theSm->Contains( elem ))
9715 theSm = meshDS->MeshElements( elem->getshapeId() );
9716 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9718 // remove medium nodes
9719 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9720 if ( nodes[i]->NbInverseElements() == 0 )
9721 meshDS->RemoveFreeNode( nodes[i], theSm );
9723 // add a linear element
9724 nodes.resize( nbCornerNodes );
9725 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9726 ReplaceElemInGroups(elem, newElem, meshDS);
9727 if( theSm && newElem )
9728 theSm->AddElement( newElem );
9734 //=======================================================================
9735 //function : ConvertFromQuadratic
9737 //=======================================================================
9739 bool SMESH_MeshEditor::ConvertFromQuadratic()
9741 smIdType nbCheckedElems = 0;
9742 if ( myMesh->HasShapeToMesh() )
9744 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9746 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9747 while ( smIt->more() ) {
9748 SMESH_subMesh* sm = smIt->next();
9749 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9750 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9755 smIdType totalNbElems =
9756 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9757 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9759 SMESHDS_SubMesh *aSM = 0;
9760 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9768 //================================================================================
9770 * \brief Return true if all medium nodes of the element are in the node set
9772 //================================================================================
9774 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9776 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9777 if ( !nodeSet.count( elem->GetNode(i) ))
9783 //================================================================================
9785 * \brief Makes given elements linear
9787 //================================================================================
9789 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9791 if ( theElements.empty() ) return;
9793 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9794 set<smIdType> mediumNodeIDs;
9795 TIDSortedElemSet::iterator eIt = theElements.begin();
9796 for ( ; eIt != theElements.end(); ++eIt )
9798 const SMDS_MeshElement* e = *eIt;
9799 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9800 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9803 // replace given elements by linear ones
9804 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9805 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9807 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9808 // except those elements sharing medium nodes of quadratic element whose medium nodes
9809 // are not all in mediumNodeIDs
9811 // get remaining medium nodes
9812 TIDSortedNodeSet mediumNodes;
9813 set<smIdType>::iterator nIdsIt = mediumNodeIDs.begin();
9814 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9815 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9816 mediumNodes.insert( mediumNodes.end(), n );
9818 // find more quadratic elements to convert
9819 TIDSortedElemSet moreElemsToConvert;
9820 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9821 for ( ; nIt != mediumNodes.end(); ++nIt )
9823 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9824 while ( invIt->more() )
9826 const SMDS_MeshElement* e = invIt->next();
9827 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9829 // find a more complex element including e and
9830 // whose medium nodes are not in mediumNodes
9831 bool complexFound = false;
9832 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9834 SMDS_ElemIteratorPtr invIt2 =
9835 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9836 while ( invIt2->more() )
9838 const SMDS_MeshElement* eComplex = invIt2->next();
9839 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9841 int nbCommonNodes = SMESH_MeshAlgos::NbCommonNodes( e, eComplex );
9842 if ( nbCommonNodes == e->NbNodes())
9844 complexFound = true;
9845 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9851 if ( !complexFound )
9852 moreElemsToConvert.insert( e );
9856 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9857 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9860 //=======================================================================
9861 //function : SewSideElements
9863 //=======================================================================
9865 SMESH_MeshEditor::Sew_Error
9866 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9867 TIDSortedElemSet& theSide2,
9868 const SMDS_MeshNode* theFirstNode1,
9869 const SMDS_MeshNode* theFirstNode2,
9870 const SMDS_MeshNode* theSecondNode1,
9871 const SMDS_MeshNode* theSecondNode2)
9875 if ( theSide1.size() != theSide2.size() )
9876 return SEW_DIFF_NB_OF_ELEMENTS;
9878 Sew_Error aResult = SEW_OK;
9880 // 1. Build set of faces representing each side
9881 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9882 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9884 // =======================================================================
9885 // 1. Build set of faces representing each side:
9886 // =======================================================================
9887 // a. build set of nodes belonging to faces
9888 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9889 // c. create temporary faces representing side of volumes if correspondent
9890 // face does not exist
9892 SMESHDS_Mesh* aMesh = GetMeshDS();
9893 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9894 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9895 TIDSortedElemSet faceSet1, faceSet2;
9896 set<const SMDS_MeshElement*> volSet1, volSet2;
9897 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9898 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9899 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9900 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9901 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9902 int iSide, iFace, iNode;
9904 list<const SMDS_MeshElement* > tempFaceList;
9905 for ( iSide = 0; iSide < 2; iSide++ ) {
9906 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9907 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9908 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9909 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9910 set<const SMDS_MeshElement*>::iterator vIt;
9911 TIDSortedElemSet::iterator eIt;
9912 set<const SMDS_MeshNode*>::iterator nIt;
9914 // check that given nodes belong to given elements
9915 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9916 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9917 int firstIndex = -1, secondIndex = -1;
9918 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9919 const SMDS_MeshElement* elem = *eIt;
9920 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9921 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9922 if ( firstIndex > -1 && secondIndex > -1 ) break;
9924 if ( firstIndex < 0 || secondIndex < 0 ) {
9925 // we can simply return until temporary faces created
9926 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9929 // -----------------------------------------------------------
9930 // 1a. Collect nodes of existing faces
9931 // and build set of face nodes in order to detect missing
9932 // faces corresponding to sides of volumes
9933 // -----------------------------------------------------------
9935 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9937 // loop on the given element of a side
9938 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9939 //const SMDS_MeshElement* elem = *eIt;
9940 const SMDS_MeshElement* elem = *eIt;
9941 if ( elem->GetType() == SMDSAbs_Face ) {
9942 faceSet->insert( elem );
9943 set <const SMDS_MeshNode*> faceNodeSet;
9944 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9945 while ( nodeIt->more() ) {
9946 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9947 nodeSet->insert( n );
9948 faceNodeSet.insert( n );
9950 setOfFaceNodeSet.insert( faceNodeSet );
9952 else if ( elem->GetType() == SMDSAbs_Volume )
9953 volSet->insert( elem );
9955 // ------------------------------------------------------------------------------
9956 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9957 // ------------------------------------------------------------------------------
9959 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9960 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9961 while ( fIt->more() ) { // loop on faces sharing a node
9962 const SMDS_MeshElement* f = fIt->next();
9963 if ( faceSet->find( f ) == faceSet->end() ) {
9964 // check if all nodes are in nodeSet and
9965 // complete setOfFaceNodeSet if they are
9966 set <const SMDS_MeshNode*> faceNodeSet;
9967 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9968 bool allInSet = true;
9969 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9970 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9971 if ( nodeSet->find( n ) == nodeSet->end() )
9974 faceNodeSet.insert( n );
9977 faceSet->insert( f );
9978 setOfFaceNodeSet.insert( faceNodeSet );
9984 // -------------------------------------------------------------------------
9985 // 1c. Create temporary faces representing sides of volumes if correspondent
9986 // face does not exist
9987 // -------------------------------------------------------------------------
9989 if ( !volSet->empty() ) {
9990 //int nodeSetSize = nodeSet->size();
9992 // loop on given volumes
9993 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9994 SMDS_VolumeTool vol (*vIt);
9995 // loop on volume faces: find free faces
9996 // --------------------------------------
9997 list<const SMDS_MeshElement* > freeFaceList;
9998 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9999 if ( !vol.IsFreeFace( iFace ))
10001 // check if there is already a face with same nodes in a face set
10002 const SMDS_MeshElement* aFreeFace = 0;
10003 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10004 int nbNodes = vol.NbFaceNodes( iFace );
10005 set <const SMDS_MeshNode*> faceNodeSet;
10006 vol.GetFaceNodes( iFace, faceNodeSet );
10007 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10009 // no such a face is given but it still can exist, check it
10010 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10011 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10013 if ( !aFreeFace ) {
10014 // create a temporary face
10015 if ( nbNodes == 3 ) {
10016 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10017 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10019 else if ( nbNodes == 4 ) {
10020 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10021 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10024 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10025 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10026 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10029 tempFaceList.push_back( aFreeFace );
10033 freeFaceList.push_back( aFreeFace );
10035 } // loop on faces of a volume
10037 // choose one of several free faces of a volume
10038 // --------------------------------------------
10039 if ( freeFaceList.size() > 1 ) {
10040 // choose a face having max nb of nodes shared by other elems of a side
10041 int maxNbNodes = -1;
10042 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10043 while ( fIt != freeFaceList.end() ) { // loop on free faces
10044 int nbSharedNodes = 0;
10045 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10046 while ( nodeIt->more() ) { // loop on free face nodes
10047 const SMDS_MeshNode* n =
10048 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10049 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10050 while ( invElemIt->more() ) {
10051 const SMDS_MeshElement* e = invElemIt->next();
10052 nbSharedNodes += faceSet->count( e );
10053 nbSharedNodes += elemSet->count( e );
10056 if ( nbSharedNodes > maxNbNodes ) {
10057 maxNbNodes = nbSharedNodes;
10058 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10060 else if ( nbSharedNodes == maxNbNodes ) {
10064 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10067 if ( freeFaceList.size() > 1 )
10069 // could not choose one face, use another way
10070 // choose a face most close to the bary center of the opposite side
10071 gp_XYZ aBC( 0., 0., 0. );
10072 set <const SMDS_MeshNode*> addedNodes;
10073 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10074 eIt = elemSet2->begin();
10075 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10076 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10077 while ( nodeIt->more() ) { // loop on free face nodes
10078 const SMDS_MeshNode* n =
10079 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10080 if ( addedNodes.insert( n ).second )
10081 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10084 aBC /= addedNodes.size();
10085 double minDist = DBL_MAX;
10086 fIt = freeFaceList.begin();
10087 while ( fIt != freeFaceList.end() ) { // loop on free faces
10089 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10090 while ( nodeIt->more() ) { // loop on free face nodes
10091 const SMDS_MeshNode* n =
10092 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10093 gp_XYZ p( n->X(),n->Y(),n->Z() );
10094 dist += ( aBC - p ).SquareModulus();
10096 if ( dist < minDist ) {
10098 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10101 fIt = freeFaceList.erase( fIt++ );
10104 } // choose one of several free faces of a volume
10106 if ( freeFaceList.size() == 1 ) {
10107 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10108 faceSet->insert( aFreeFace );
10109 // complete a node set with nodes of a found free face
10110 // for ( iNode = 0; iNode < ; iNode++ )
10111 // nodeSet->insert( fNodes[ iNode ] );
10114 } // loop on volumes of a side
10116 // // complete a set of faces if new nodes in a nodeSet appeared
10117 // // ----------------------------------------------------------
10118 // if ( nodeSetSize != nodeSet->size() ) {
10119 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10120 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10121 // while ( fIt->more() ) { // loop on faces sharing a node
10122 // const SMDS_MeshElement* f = fIt->next();
10123 // if ( faceSet->find( f ) == faceSet->end() ) {
10124 // // check if all nodes are in nodeSet and
10125 // // complete setOfFaceNodeSet if they are
10126 // set <const SMDS_MeshNode*> faceNodeSet;
10127 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10128 // bool allInSet = true;
10129 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10130 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10131 // if ( nodeSet->find( n ) == nodeSet->end() )
10132 // allInSet = false;
10134 // faceNodeSet.insert( n );
10136 // if ( allInSet ) {
10137 // faceSet->insert( f );
10138 // setOfFaceNodeSet.insert( faceNodeSet );
10144 } // Create temporary faces, if there are volumes given
10147 if ( faceSet1.size() != faceSet2.size() ) {
10148 // delete temporary faces: they are in reverseElements of actual nodes
10149 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10150 // while ( tmpFaceIt->more() )
10151 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10152 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10153 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10154 // aMesh->RemoveElement(*tmpFaceIt);
10155 MESSAGE("Diff nb of faces");
10156 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10159 // ============================================================
10160 // 2. Find nodes to merge:
10161 // bind a node to remove to a node to put instead
10162 // ============================================================
10164 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10165 if ( theFirstNode1 != theFirstNode2 )
10166 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10167 if ( theSecondNode1 != theSecondNode2 )
10168 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10170 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10171 set< long > linkIdSet; // links to process
10172 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10174 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10175 list< NLink > linkList[2];
10176 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10177 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10178 // loop on links in linkList; find faces by links and append links
10179 // of the found faces to linkList
10180 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10181 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10183 NLink link[] = { *linkIt[0], *linkIt[1] };
10184 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10185 if ( !linkIdSet.count( linkID ) )
10188 // by links, find faces in the face sets,
10189 // and find indices of link nodes in the found faces;
10190 // in a face set, there is only one or no face sharing a link
10191 // ---------------------------------------------------------------
10193 const SMDS_MeshElement* face[] = { 0, 0 };
10194 vector<const SMDS_MeshNode*> fnodes[2];
10195 int iLinkNode[2][2];
10196 TIDSortedElemSet avoidSet;
10197 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10198 const SMDS_MeshNode* n1 = link[iSide].first;
10199 const SMDS_MeshNode* n2 = link[iSide].second;
10200 //cout << "Side " << iSide << " ";
10201 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10202 // find a face by two link nodes
10203 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10204 *faceSetPtr[ iSide ], avoidSet,
10205 &iLinkNode[iSide][0],
10206 &iLinkNode[iSide][1] );
10207 if ( face[ iSide ])
10209 //cout << " F " << face[ iSide]->GetID() <<endl;
10210 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10211 // put face nodes to fnodes
10212 SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
10213 fnodes[ iSide ].assign( nIt, nEnd );
10214 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10218 // check similarity of elements of the sides
10219 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10220 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10221 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10222 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10225 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10227 break; // do not return because it's necessary to remove tmp faces
10230 // set nodes to merge
10231 // -------------------
10233 if ( face[0] && face[1] ) {
10234 const int nbNodes = face[0]->NbNodes();
10235 if ( nbNodes != face[1]->NbNodes() ) {
10236 MESSAGE("Diff nb of face nodes");
10237 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10238 break; // do not return because it s necessary to remove tmp faces
10240 bool reverse[] = { false, false }; // order of nodes in the link
10241 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10242 // analyse link orientation in faces
10243 int i1 = iLinkNode[ iSide ][ 0 ];
10244 int i2 = iLinkNode[ iSide ][ 1 ];
10245 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10247 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10248 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10249 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10251 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10252 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10255 // add other links of the faces to linkList
10256 // -----------------------------------------
10258 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10259 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10260 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10261 if ( !iter_isnew.second ) { // already in a set: no need to process
10262 linkIdSet.erase( iter_isnew.first );
10264 else // new in set == encountered for the first time: add
10266 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10267 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10268 linkList[0].push_back ( NLink( n1, n2 ));
10269 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10274 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10277 } // loop on link lists
10279 if ( aResult == SEW_OK &&
10280 ( //linkIt[0] != linkList[0].end() ||
10281 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10282 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10283 " " << (faceSetPtr[1]->empty()));
10284 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10287 // ====================================================================
10288 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10289 // ====================================================================
10291 // delete temporary faces
10292 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10293 // while ( tmpFaceIt->more() )
10294 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10295 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10296 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10297 aMesh->RemoveElement(*tmpFaceIt);
10299 if ( aResult != SEW_OK)
10302 list< smIdType > nodeIDsToRemove;
10303 vector< const SMDS_MeshNode*> nodes;
10304 ElemFeatures elemType;
10306 // loop on nodes replacement map
10307 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10308 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10309 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10311 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10312 nodeIDsToRemove.push_back( nToRemove->GetID() );
10313 // loop on elements sharing nToRemove
10314 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10315 while ( invElemIt->more() ) {
10316 const SMDS_MeshElement* e = invElemIt->next();
10317 // get a new suite of nodes: make replacement
10318 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10319 nodes.resize( nbNodes );
10320 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10321 while ( nIt->more() ) {
10322 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10323 nnIt = nReplaceMap.find( n );
10324 if ( nnIt != nReplaceMap.end() ) {
10326 n = (*nnIt).second;
10330 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10331 // elemIDsToRemove.push_back( e->GetID() );
10335 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10336 aMesh->RemoveElement( e );
10338 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10340 AddToSameGroups( newElem, e, aMesh );
10341 if ( int aShapeId = e->getshapeId() )
10342 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10348 Remove( nodeIDsToRemove, true );
10353 //================================================================================
10355 * \brief Find corresponding nodes in two sets of faces
10356 * \param theSide1 - first face set
10357 * \param theSide2 - second first face
10358 * \param theFirstNode1 - a boundary node of set 1
10359 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10360 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10361 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10362 * \param nReplaceMap - output map of corresponding nodes
10363 * \return bool - is a success or not
10365 //================================================================================
10368 //#define DEBUG_MATCHING_NODES
10371 SMESH_MeshEditor::Sew_Error
10372 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10373 set<const SMDS_MeshElement*>& theSide2,
10374 const SMDS_MeshNode* theFirstNode1,
10375 const SMDS_MeshNode* theFirstNode2,
10376 const SMDS_MeshNode* theSecondNode1,
10377 const SMDS_MeshNode* theSecondNode2,
10378 TNodeNodeMap & nReplaceMap)
10380 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10382 nReplaceMap.clear();
10383 //if ( theFirstNode1 != theFirstNode2 )
10384 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10385 //if ( theSecondNode1 != theSecondNode2 )
10386 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10388 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10389 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10391 list< NLink > linkList[2];
10392 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10393 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10395 // loop on links in linkList; find faces by links and append links
10396 // of the found faces to linkList
10397 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10398 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10399 NLink link[] = { *linkIt[0], *linkIt[1] };
10400 if ( linkSet.find( link[0] ) == linkSet.end() )
10403 // by links, find faces in the face sets,
10404 // and find indices of link nodes in the found faces;
10405 // in a face set, there is only one or no face sharing a link
10406 // ---------------------------------------------------------------
10408 const SMDS_MeshElement* face[] = { 0, 0 };
10409 list<const SMDS_MeshNode*> notLinkNodes[2];
10410 //bool reverse[] = { false, false }; // order of notLinkNodes
10412 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10414 const SMDS_MeshNode* n1 = link[iSide].first;
10415 const SMDS_MeshNode* n2 = link[iSide].second;
10416 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10417 set< const SMDS_MeshElement* > facesOfNode1;
10418 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10420 // during a loop of the first node, we find all faces around n1,
10421 // during a loop of the second node, we find one face sharing both n1 and n2
10422 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10423 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10424 while ( fIt->more() ) { // loop on faces sharing a node
10425 const SMDS_MeshElement* f = fIt->next();
10426 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10427 ! facesOfNode1.insert( f ).second ) // f encounters twice
10429 if ( face[ iSide ] ) {
10430 MESSAGE( "2 faces per link " );
10431 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10434 faceSet->erase( f );
10436 // get not link nodes
10437 int nbN = f->NbNodes();
10438 if ( f->IsQuadratic() )
10440 nbNodes[ iSide ] = nbN;
10441 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10442 int i1 = f->GetNodeIndex( n1 );
10443 int i2 = f->GetNodeIndex( n2 );
10444 int iEnd = nbN, iBeg = -1, iDelta = 1;
10445 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10447 std::swap( iEnd, iBeg ); iDelta = -1;
10452 if ( i == iEnd ) i = iBeg + iDelta;
10453 if ( i == i1 ) break;
10454 nodes.push_back ( f->GetNode( i ) );
10460 // check similarity of elements of the sides
10461 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10462 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10463 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10464 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10467 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10471 // set nodes to merge
10472 // -------------------
10474 if ( face[0] && face[1] ) {
10475 if ( nbNodes[0] != nbNodes[1] ) {
10476 MESSAGE("Diff nb of face nodes");
10477 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10479 #ifdef DEBUG_MATCHING_NODES
10480 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10481 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10482 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10484 int nbN = nbNodes[0];
10486 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10487 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10488 for ( int i = 0 ; i < nbN - 2; ++i ) {
10489 #ifdef DEBUG_MATCHING_NODES
10490 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10492 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10496 // add other links of the face 1 to linkList
10497 // -----------------------------------------
10499 const SMDS_MeshElement* f0 = face[0];
10500 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10501 for ( int i = 0; i < nbN; i++ )
10503 const SMDS_MeshNode* n2 = f0->GetNode( i );
10504 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10505 linkSet.insert( SMESH_TLink( n1, n2 ));
10506 if ( !iter_isnew.second ) { // already in a set: no need to process
10507 linkSet.erase( iter_isnew.first );
10509 else // new in set == encountered for the first time: add
10511 #ifdef DEBUG_MATCHING_NODES
10512 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10513 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10515 linkList[0].push_back ( NLink( n1, n2 ));
10516 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10521 } // loop on link lists
10526 namespace // automatically find theAffectedElems for DoubleNodes()
10528 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10530 //--------------------------------------------------------------------------------
10531 // Nodes shared by adjacent FissureBorder's.
10532 // 1 node if FissureBorder separates faces
10533 // 2 nodes if FissureBorder separates volumes
10536 const SMDS_MeshNode* _nodes[2];
10539 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10543 _nbNodes = bool( n1 ) + bool( n2 );
10544 if ( _nbNodes == 2 && n1 > n2 )
10545 std::swap( _nodes[0], _nodes[1] );
10547 bool operator<( const SubBorder& other ) const
10549 for ( int i = 0; i < _nbNodes; ++i )
10551 if ( _nodes[i] < other._nodes[i] ) return true;
10552 if ( _nodes[i] > other._nodes[i] ) return false;
10558 //--------------------------------------------------------------------------------
10559 // Map a SubBorder to all FissureBorder it bounds
10560 struct FissureBorder;
10561 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10562 typedef TBorderLinks::iterator TMappedSub;
10564 //--------------------------------------------------------------------------------
10566 * \brief Element border (volume facet or face edge) at a fissure
10568 struct FissureBorder
10570 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10571 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10573 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10574 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10576 FissureBorder( FissureBorder && from ) // move constructor
10578 std::swap( _nodes, from._nodes );
10579 std::swap( _sortedNodes, from._sortedNodes );
10580 _elems[0] = from._elems[0];
10581 _elems[1] = from._elems[1];
10584 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10585 std::vector< const SMDS_MeshElement* > & adjElems)
10586 : _nodes( elemToDuplicate->NbCornerNodes() )
10588 for ( size_t i = 0; i < _nodes.size(); ++i )
10589 _nodes[i] = elemToDuplicate->GetNode( i );
10591 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10592 findAdjacent( type, adjElems );
10595 FissureBorder( const SMDS_MeshNode** nodes,
10596 const size_t nbNodes,
10597 const SMDSAbs_ElementType adjElemsType,
10598 std::vector< const SMDS_MeshElement* > & adjElems)
10599 : _nodes( nodes, nodes + nbNodes )
10601 findAdjacent( adjElemsType, adjElems );
10604 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10605 std::vector< const SMDS_MeshElement* > & adjElems)
10607 _elems[0] = _elems[1] = 0;
10609 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10610 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10611 _elems[i] = adjElems[i];
10614 bool operator<( const FissureBorder& other ) const
10616 return GetSortedNodes() < other.GetSortedNodes();
10619 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10621 if ( _sortedNodes.empty() && !_nodes.empty() )
10623 FissureBorder* me = const_cast<FissureBorder*>( this );
10624 me->_sortedNodes = me->_nodes;
10625 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10627 return _sortedNodes;
10630 size_t NbSub() const
10632 return _nodes.size();
10635 SubBorder Sub(size_t i) const
10637 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10640 void AddSelfTo( TBorderLinks& borderLinks )
10642 _mappedSubs.resize( NbSub() );
10643 for ( size_t i = 0; i < NbSub(); ++i )
10645 TBorderLinks::iterator s2b =
10646 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10647 s2b->second.push_back( this );
10648 _mappedSubs[ i ] = s2b;
10657 const SMDS_MeshElement* GetMarkedElem() const
10659 if ( _nodes.empty() ) return 0; // cleared
10660 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10661 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10665 gp_XYZ GetNorm() const // normal to the border
10668 if ( _nodes.size() == 2 )
10670 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10671 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10673 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10676 gp_XYZ bordDir( SMESH_NodeXYZ( this->_nodes[0] ) - SMESH_NodeXYZ( this->_nodes[1] ));
10677 norm = bordDir ^ avgNorm;
10681 SMESH_NodeXYZ p0( _nodes[0] );
10682 SMESH_NodeXYZ p1( _nodes[1] );
10683 SMESH_NodeXYZ p2( _nodes[2] );
10684 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10686 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10692 void ChooseSide() // mark an _elem located at positive side of fissure
10694 _elems[0]->setIsMarked( true );
10695 gp_XYZ norm = GetNorm();
10696 double maxX = norm.Coord(1);
10697 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10698 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10701 _elems[0]->setIsMarked( false );
10703 _elems[1]->setIsMarked( true );
10707 }; // struct FissureBorder
10709 //--------------------------------------------------------------------------------
10711 * \brief Classifier of elements at fissure edge
10713 class FissureNormal
10715 std::vector< gp_XYZ > _normals;
10719 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10722 _normals.reserve(2);
10723 _normals.push_back( bord.GetNorm() );
10724 if ( _normals.size() == 2 )
10725 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10728 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10731 switch ( _normals.size() ) {
10734 isIn = !isOut( n, _normals[0], elem );
10739 bool in1 = !isOut( n, _normals[0], elem );
10740 bool in2 = !isOut( n, _normals[1], elem );
10741 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10748 //================================================================================
10750 * \brief Classify an element by a plane passing through a node
10752 //================================================================================
10754 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10756 SMESH_NodeXYZ p = n;
10758 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10760 SMESH_NodeXYZ pi = elem->GetNode( i );
10761 sumDot += norm * ( pi - p );
10763 return sumDot < -1e-100;
10766 //================================================================================
10768 * \brief Find FissureBorder's by nodes to duplicate
10770 //================================================================================
10772 void findFissureBorders( const TIDSortedElemSet& theNodes,
10773 std::vector< FissureBorder > & theFissureBorders )
10775 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10776 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10778 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10779 if ( n->NbInverseElements( elemType ) == 0 )
10781 elemType = SMDSAbs_Face;
10782 if ( n->NbInverseElements( elemType ) == 0 )
10785 // unmark elements touching the fissure
10786 for ( ; nIt != theNodes.end(); ++nIt )
10787 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10789 // loop on elements touching the fissure to get their borders belonging to the fissure
10790 std::set< FissureBorder > fissureBorders;
10791 std::vector< const SMDS_MeshElement* > adjElems;
10792 std::vector< const SMDS_MeshNode* > nodes;
10793 SMDS_VolumeTool volTool;
10794 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10796 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10797 while ( invIt->more() )
10799 const SMDS_MeshElement* eInv = invIt->next();
10800 if ( eInv->isMarked() ) continue;
10801 eInv->setIsMarked( true );
10803 if ( elemType == SMDSAbs_Volume )
10805 volTool.Set( eInv );
10806 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10807 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10809 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10810 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10812 bool allOnFissure = true;
10813 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10814 if (( allOnFissure = theNodes.count( nn[ iN ])))
10815 nodes.push_back( nn[ iN ]);
10816 if ( allOnFissure )
10817 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10818 elemType, adjElems )));
10821 else // elemType == SMDSAbs_Face
10823 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10824 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10825 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10827 nn[1] = eInv->GetNode( iN );
10828 onFissure1 = theNodes.count( nn[1] );
10829 if ( onFissure0 && onFissure1 )
10830 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10832 onFissure0 = onFissure1;
10838 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10839 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10840 for ( ; bord != fissureBorders.end(); ++bord )
10842 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10845 } // findFissureBorders()
10847 //================================================================================
10849 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10850 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10851 * \param [in] theNodesNot - nodes not to duplicate
10852 * \param [out] theAffectedElems - the found elements
10854 //================================================================================
10856 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10857 TIDSortedElemSet& theAffectedElems)
10859 if ( theElemsOrNodes.empty() ) return;
10861 // find FissureBorder's
10863 std::vector< FissureBorder > fissure;
10864 std::vector< const SMDS_MeshElement* > elemsByFacet;
10866 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10867 if ( (*elIt)->GetType() == SMDSAbs_Node )
10869 findFissureBorders( theElemsOrNodes, fissure );
10873 fissure.reserve( theElemsOrNodes.size() );
10874 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10876 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10877 if ( !fissure.back()._elems[1] )
10878 fissure.pop_back();
10881 if ( fissure.empty() )
10884 // fill borderLinks
10886 TBorderLinks borderLinks;
10888 for ( size_t i = 0; i < fissure.size(); ++i )
10890 fissure[i].AddSelfTo( borderLinks );
10893 // get theAffectedElems
10895 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10896 for ( size_t i = 0; i < fissure.size(); ++i )
10897 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10899 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10900 false, /*markElem=*/true );
10903 std::vector<const SMDS_MeshNode *> facetNodes;
10904 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10905 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10907 // choose a side of fissure
10908 fissure[0].ChooseSide();
10909 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10911 size_t nbCheckedBorders = 0;
10912 while ( nbCheckedBorders < fissure.size() )
10914 // find a FissureBorder to treat
10915 FissureBorder* bord = 0;
10916 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10917 if ( fissure[i].GetMarkedElem() )
10918 bord = & fissure[i];
10919 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10920 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10922 bord = & fissure[i];
10923 bord->ChooseSide();
10924 theAffectedElems.insert( bord->GetMarkedElem() );
10926 if ( !bord ) return;
10927 ++nbCheckedBorders;
10929 // treat FissureBorder's linked to bord
10930 fissureNodes.clear();
10931 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10932 for ( size_t i = 0; i < bord->NbSub(); ++i )
10934 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10935 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10936 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10937 const SubBorder& sb = l2b->first;
10938 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10940 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10942 for ( int j = 0; j < sb._nbNodes; ++j )
10943 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10947 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10948 // until an elem adjacent to a neighbour FissureBorder is found
10949 facetNodes.clear();
10950 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10951 facetNodes.resize( sb._nbNodes + 1 );
10955 // check if bordElem is adjacent to a neighbour FissureBorder
10956 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10958 FissureBorder* bord2 = linkedBorders[j];
10959 if ( bord2 == bord ) continue;
10960 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10963 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10968 // find the next bordElem
10969 const SMDS_MeshElement* nextBordElem = 0;
10970 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10972 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10973 if ( fissureNodes.count( n )) continue;
10975 facetNodes[ sb._nbNodes ] = n;
10976 elemsByFacet.clear();
10977 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10979 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10980 if ( elemsByFacet[ iE ] != bordElem &&
10981 !elemsByFacet[ iE ]->isMarked() )
10983 theAffectedElems.insert( elemsByFacet[ iE ]);
10984 elemsByFacet[ iE ]->setIsMarked( true );
10985 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10986 nextBordElem = elemsByFacet[ iE ];
10990 bordElem = nextBordElem;
10992 } // while ( bordElem )
10994 linkedBorders.clear(); // not to treat this link any more
10996 } // loop on SubBorder's of a FissureBorder
11000 } // loop on FissureBorder's
11003 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
11005 // mark nodes of theAffectedElems
11006 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
11008 // unmark nodes of the fissure
11009 elIt = theElemsOrNodes.begin();
11010 if ( (*elIt)->GetType() == SMDSAbs_Node )
11011 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
11013 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
11015 std::vector< gp_XYZ > normVec;
11017 // loop on nodes of the fissure, add elements having marked nodes
11018 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
11020 const SMDS_MeshElement* e = (*elIt);
11021 if ( e->GetType() != SMDSAbs_Node )
11022 e->setIsMarked( true ); // avoid adding a fissure element
11024 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
11026 const SMDS_MeshNode* n = e->GetNode( iN );
11027 if ( fissEdgeNodes2Norm.count( n ))
11030 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
11031 while ( invIt->more() )
11033 const SMDS_MeshElement* eInv = invIt->next();
11034 if ( eInv->isMarked() ) continue;
11035 eInv->setIsMarked( true );
11037 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
11038 while( nIt->more() )
11039 if ( nIt->next()->isMarked())
11041 theAffectedElems.insert( eInv );
11042 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
11043 n->setIsMarked( false );
11050 // add elements on the fissure edge
11051 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
11052 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
11054 const SMDS_MeshNode* edgeNode = n2N->first;
11055 const FissureNormal & normals = n2N->second;
11057 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
11058 while ( invIt->more() )
11060 const SMDS_MeshElement* eInv = invIt->next();
11061 if ( eInv->isMarked() ) continue;
11062 eInv->setIsMarked( true );
11064 // classify eInv using normals
11065 bool toAdd = normals.IsIn( edgeNode, eInv );
11066 if ( toAdd ) // check if all nodes lie on the fissure edge
11068 bool notOnEdge = false;
11069 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
11070 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
11075 theAffectedElems.insert( eInv );
11081 } // findAffectedElems()
11084 //================================================================================
11086 * \brief Create elements equal (on same nodes) to given ones
11087 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
11088 * elements of the uppest dimension are duplicated.
11090 //================================================================================
11092 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
11094 ClearLastCreated();
11095 SMESHDS_Mesh* mesh = GetMeshDS();
11097 // get an element type and an iterator over elements
11099 SMDSAbs_ElementType type = SMDSAbs_All;
11100 SMDS_ElemIteratorPtr elemIt;
11101 if ( theElements.empty() )
11103 if ( mesh->NbNodes() == 0 )
11105 // get most complex type
11106 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
11107 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
11108 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
11110 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
11111 if ( mesh->GetMeshInfo().NbElements( types[i] ))
11114 elemIt = mesh->elementsIterator( type );
11120 //type = (*theElements.begin())->GetType();
11121 elemIt = SMESHUtils::elemSetIterator( theElements );
11124 // un-mark all elements to avoid duplicating just created elements
11125 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
11127 // duplicate elements
11129 ElemFeatures elemType;
11131 vector< const SMDS_MeshNode* > nodes;
11132 while ( elemIt->more() )
11134 const SMDS_MeshElement* elem = elemIt->next();
11135 if (( type != SMDSAbs_All && elem->GetType() != type ) ||
11136 ( elem->isMarked() ))
11139 elemType.Init( elem, /*basicOnly=*/false );
11140 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
11142 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
11143 newElem->setIsMarked( true );
11147 //================================================================================
11149 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11150 \param theElems - the list of elements (edges or faces) to be replicated
11151 The nodes for duplication could be found from these elements
11152 \param theNodesNot - list of nodes to NOT replicate
11153 \param theAffectedElems - the list of elements (cells and edges) to which the
11154 replicated nodes should be associated to.
11155 \return TRUE if operation has been completed successfully, FALSE otherwise
11157 //================================================================================
11159 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
11160 const TIDSortedElemSet& theNodesNot,
11161 const TIDSortedElemSet& theAffectedElems )
11163 ClearLastCreated();
11165 if ( theElems.size() == 0 )
11168 SMESHDS_Mesh* aMeshDS = GetMeshDS();
11173 TNodeNodeMap anOldNodeToNewNode;
11174 // duplicate elements and nodes
11175 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
11176 // replce nodes by duplications
11177 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
11181 //================================================================================
11183 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11184 \param theMeshDS - mesh instance
11185 \param theElems - the elements replicated or modified (nodes should be changed)
11186 \param theNodesNot - nodes to NOT replicate
11187 \param theNodeNodeMap - relation of old node to new created node
11188 \param theIsDoubleElem - flag os to replicate element or modify
11189 \return TRUE if operation has been completed successfully, FALSE otherwise
11191 //================================================================================
11193 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
11194 const TIDSortedElemSet& theElems,
11195 const TIDSortedElemSet& theNodesNot,
11196 TNodeNodeMap& theNodeNodeMap,
11197 const bool theIsDoubleElem )
11199 // iterate through element and duplicate them (by nodes duplication)
11201 std::vector<const SMDS_MeshNode*> newNodes;
11202 ElemFeatures elemType;
11204 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11205 for ( ; elemItr != theElems.end(); ++elemItr )
11207 const SMDS_MeshElement* anElem = *elemItr;
11211 // duplicate nodes to duplicate element
11212 bool isDuplicate = false;
11213 newNodes.resize( anElem->NbNodes() );
11214 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11216 while ( anIter->more() )
11218 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
11219 const SMDS_MeshNode* aNewNode = aCurrNode;
11220 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
11221 if ( n2n != theNodeNodeMap.end() )
11223 aNewNode = n2n->second;
11225 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
11228 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
11229 copyPosition( aCurrNode, aNewNode );
11230 theNodeNodeMap[ aCurrNode ] = aNewNode;
11231 myLastCreatedNodes.push_back( aNewNode );
11233 isDuplicate |= (aCurrNode != aNewNode);
11234 newNodes[ ind++ ] = aNewNode;
11236 if ( !isDuplicate )
11239 if ( theIsDoubleElem )
11240 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
11243 // change element nodes
11244 const SMDSAbs_EntityType geomType = anElem->GetEntityType();
11245 if ( geomType == SMDSEntity_Polyhedra )
11247 // special treatment for polyhedron
11248 const SMDS_MeshVolume* aPolyhedron = SMDS_Mesh::DownCast< SMDS_MeshVolume >( anElem );
11249 if (!aPolyhedron) {
11250 MESSAGE("Warning: bad volumic element");
11253 theMeshDS->ChangePolyhedronNodes( anElem, newNodes, aPolyhedron->GetQuantities() );
11256 // standard entity type
11257 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
11265 //================================================================================
11267 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11268 \param theNodes - identifiers of nodes to be doubled
11269 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
11270 nodes. If list of element identifiers is empty then nodes are doubled but
11271 they not assigned to elements
11272 \return TRUE if operation has been completed successfully, FALSE otherwise
11274 //================================================================================
11276 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
11277 const std::list< int >& theListOfModifiedElems )
11279 ClearLastCreated();
11281 if ( theListOfNodes.size() == 0 )
11284 SMESHDS_Mesh* aMeshDS = GetMeshDS();
11288 // iterate through nodes and duplicate them
11290 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
11292 std::list< int >::const_iterator aNodeIter;
11293 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
11295 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
11301 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11304 copyPosition( aNode, aNewNode );
11305 anOldNodeToNewNode[ aNode ] = aNewNode;
11306 myLastCreatedNodes.push_back( aNewNode );
11310 // Change nodes of elements
11312 std::vector<const SMDS_MeshNode*> aNodeArr;
11314 std::list< int >::const_iterator anElemIter;
11315 for ( anElemIter = theListOfModifiedElems.begin();
11316 anElemIter != theListOfModifiedElems.end();
11319 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
11323 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
11324 for( size_t i = 0; i < aNodeArr.size(); ++i )
11326 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
11327 anOldNodeToNewNode.find( aNodeArr[ i ]);
11328 if ( n2n != anOldNodeToNewNode.end() )
11329 aNodeArr[ i ] = n2n->second;
11331 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
11339 //================================================================================
11341 \brief Check if element located inside shape
11342 \return TRUE if IN or ON shape, FALSE otherwise
11344 //================================================================================
11346 template<class Classifier>
11347 bool isInside(const SMDS_MeshElement* theElem,
11348 Classifier& theClassifier,
11349 const double theTol)
11351 gp_XYZ centerXYZ (0, 0, 0);
11352 for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
11353 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
11355 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11356 theClassifier.Perform(aPnt, theTol);
11357 TopAbs_State aState = theClassifier.State();
11358 return (aState == TopAbs_IN || aState == TopAbs_ON );
11361 //================================================================================
11363 * \brief Classifier of the 3D point on the TopoDS_Face
11364 * with interaface suitable for isInside()
11366 //================================================================================
11368 struct _FaceClassifier
11370 Extrema_ExtPS _extremum;
11371 BRepAdaptor_Surface _surface;
11372 TopAbs_State _state;
11374 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11376 _extremum.Initialize( _surface,
11377 _surface.FirstUParameter(), _surface.LastUParameter(),
11378 _surface.FirstVParameter(), _surface.LastVParameter(),
11379 _surface.Tolerance(), _surface.Tolerance() );
11381 void Perform(const gp_Pnt& aPnt, double theTol)
11384 _state = TopAbs_OUT;
11385 _extremum.Perform(aPnt);
11386 if ( _extremum.IsDone() )
11387 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11388 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11390 TopAbs_State State() const
11397 //================================================================================
11399 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11400 This method is the first step of DoubleNodeElemGroupsInRegion.
11401 \param theElems - list of groups of elements (edges or faces) to be replicated
11402 \param theNodesNot - list of groups of nodes not to replicate
11403 \param theShape - shape to detect affected elements (element which geometric center
11404 located on or inside shape). If the shape is null, detection is done on faces orientations
11405 (select elements with a gravity center on the side given by faces normals).
11406 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11407 The replicated nodes should be associated to affected elements.
11409 \sa DoubleNodeElemGroupsInRegion()
11411 //================================================================================
11413 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11414 const TIDSortedElemSet& theNodesNot,
11415 const TopoDS_Shape& theShape,
11416 TIDSortedElemSet& theAffectedElems)
11418 if ( theShape.IsNull() )
11420 findAffectedElems( theElems, theAffectedElems );
11424 const double aTol = Precision::Confusion();
11425 std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
11426 std::unique_ptr<_FaceClassifier> aFaceClassifier;
11427 if ( theShape.ShapeType() == TopAbs_SOLID )
11429 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11430 bsc3d->PerformInfinitePoint(aTol);
11432 else if (theShape.ShapeType() == TopAbs_FACE )
11434 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11437 // iterates on indicated elements and get elements by back references from their nodes
11438 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11439 for ( ; elemItr != theElems.end(); ++elemItr )
11441 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11442 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11443 while ( nodeItr->more() )
11445 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11446 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11448 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11449 while ( backElemItr->more() )
11451 const SMDS_MeshElement* curElem = backElemItr->next();
11452 if ( curElem && theElems.find(curElem) == theElems.end() &&
11454 isInside( curElem, *bsc3d, aTol ) :
11455 isInside( curElem, *aFaceClassifier, aTol )))
11456 theAffectedElems.insert( curElem );
11464 //================================================================================
11466 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11467 \param theElems - group of of elements (edges or faces) to be replicated
11468 \param theNodesNot - group of nodes not to replicate
11469 \param theShape - shape to detect affected elements (element which geometric center
11470 located on or inside shape).
11471 The replicated nodes should be associated to affected elements.
11472 \return TRUE if operation has been completed successfully, FALSE otherwise
11474 //================================================================================
11476 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11477 const TIDSortedElemSet& theNodesNot,
11478 const TopoDS_Shape& theShape )
11480 if ( theShape.IsNull() )
11483 const double aTol = Precision::Confusion();
11484 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11485 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
11486 if ( theShape.ShapeType() == TopAbs_SOLID )
11488 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11489 bsc3d->PerformInfinitePoint(aTol);
11491 else if (theShape.ShapeType() == TopAbs_FACE )
11493 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11496 // iterates on indicated elements and get elements by back references from their nodes
11497 TIDSortedElemSet anAffected;
11498 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11499 for ( ; elemItr != theElems.end(); ++elemItr )
11501 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11505 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11506 while ( nodeItr->more() )
11508 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11509 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11511 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11512 while ( backElemItr->more() )
11514 const SMDS_MeshElement* curElem = backElemItr->next();
11515 if ( curElem && theElems.find(curElem) == theElems.end() &&
11517 isInside( curElem, *bsc3d, aTol ) :
11518 isInside( curElem, *aFaceClassifier, aTol )))
11519 anAffected.insert( curElem );
11523 return DoubleNodes( theElems, theNodesNot, anAffected );
11527 * \brief compute an oriented angle between two planes defined by four points.
11528 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11529 * @param p0 base of the rotation axe
11530 * @param p1 extremity of the rotation axe
11531 * @param g1 belongs to the first plane
11532 * @param g2 belongs to the second plane
11534 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11536 gp_Vec vref(p0, p1);
11539 gp_Vec n1 = vref.Crossed(v1);
11540 gp_Vec n2 = vref.Crossed(v2);
11542 return n2.AngleWithRef(n1, vref);
11544 catch ( Standard_Failure& ) {
11546 return Max( v1.Magnitude(), v2.Magnitude() );
11550 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11551 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11552 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11553 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11554 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11555 * 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.
11556 * 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.
11557 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11558 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11559 * \param theElems - list of groups of volumes, where a group of volume is a set of
11560 * SMDS_MeshElements sorted by Id.
11561 * \param createJointElems - if TRUE, create the elements
11562 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11563 * the boundary between \a theDomains and the rest mesh
11564 * \return TRUE if operation has been completed successfully, FALSE otherwise
11566 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11567 bool createJointElems,
11568 bool onAllBoundaries)
11570 // MESSAGE("----------------------------------------------");
11571 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11572 // MESSAGE("----------------------------------------------");
11574 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11575 meshDS->BuildDownWardConnectivity(true);
11577 SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11579 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11580 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11581 // build the list of nodes shared by 2 or more domains, with their domain indexes
11583 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11584 std::map<int,int> celldom; // cell vtkId --> domain
11585 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11586 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11588 //MESSAGE(".. Number of domains :"<<theElems.size());
11590 TIDSortedElemSet theRestDomElems;
11591 const int iRestDom = -1;
11592 const int idom0 = onAllBoundaries ? iRestDom : 0;
11593 const int nbDomains = theElems.size();
11595 // Check if the domains do not share an element
11596 for (int idom = 0; idom < nbDomains-1; idom++)
11598 // MESSAGE("... Check of domain #" << idom);
11599 const TIDSortedElemSet& domain = theElems[idom];
11600 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11601 for (; elemItr != domain.end(); ++elemItr)
11603 const SMDS_MeshElement* anElem = *elemItr;
11604 int idombisdeb = idom + 1 ;
11605 // check if the element belongs to a domain further in the list
11606 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11608 const TIDSortedElemSet& domainbis = theElems[idombis];
11609 if ( domainbis.count( anElem ))
11611 MESSAGE(".... Domain #" << idom);
11612 MESSAGE(".... Domain #" << idombis);
11613 throw SALOME_Exception("The domains are not disjoint.");
11620 for (int idom = 0; idom < nbDomains; idom++)
11623 // --- build a map (face to duplicate --> volume to modify)
11624 // with all the faces shared by 2 domains (group of elements)
11625 // and corresponding volume of this domain, for each shared face.
11626 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11628 //MESSAGE("... Neighbors of domain #" << idom);
11629 const TIDSortedElemSet& domain = theElems[idom];
11630 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11631 for (; elemItr != domain.end(); ++elemItr)
11633 const SMDS_MeshElement* anElem = *elemItr;
11636 vtkIdType vtkId = anElem->GetVtkID();
11637 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11638 int neighborsVtkIds[NBMAXNEIGHBORS];
11639 int downIds[NBMAXNEIGHBORS];
11640 unsigned char downTypes[NBMAXNEIGHBORS];
11641 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11642 for (int n = 0; n < nbNeighbors; n++)
11644 smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11645 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11646 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11649 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11651 // MESSAGE("Domain " << idombis);
11652 const TIDSortedElemSet& domainbis = theElems[idombis];
11653 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11655 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11657 DownIdType face(downIds[n], downTypes[n]);
11658 if (!faceDomains[face].count(idom))
11660 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11661 celldom[vtkId] = idom;
11662 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11666 theRestDomElems.insert( elem );
11667 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11668 celldom[neighborsVtkIds[n]] = iRestDom;
11676 //MESSAGE("Number of shared faces " << faceDomains.size());
11677 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11679 // --- explore the shared faces domain by domain,
11680 // explore the nodes of the face and see if they belong to a cell in the domain,
11681 // which has only a node or an edge on the border (not a shared face)
11683 for (int idomain = idom0; idomain < nbDomains; idomain++)
11685 //MESSAGE("Domain " << idomain);
11686 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11687 itface = faceDomains.begin();
11688 for (; itface != faceDomains.end(); ++itface)
11690 const std::map<int, int>& domvol = itface->second;
11691 if (!domvol.count(idomain))
11693 DownIdType face = itface->first;
11694 //MESSAGE(" --- face " << face.cellId);
11695 std::set<int> oldNodes;
11696 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11697 std::set<int>::iterator itn = oldNodes.begin();
11698 for (; itn != oldNodes.end(); ++itn)
11701 //MESSAGE(" node " << oldId);
11702 vtkCellLinks::Link l = (static_cast <vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11703 for (int i=0; i<l.ncells; i++)
11705 int vtkId = l.cells[i];
11706 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11707 if (!domain.count(anElem))
11709 int vtkType = grid->GetCellType(vtkId);
11710 int downId = grid->CellIdToDownId(vtkId);
11713 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11714 continue; // not OK at this stage of the algorithm:
11715 //no cells created after BuildDownWardConnectivity
11717 DownIdType aCell(downId, vtkType);
11718 cellDomains[aCell][idomain] = vtkId;
11719 celldom[vtkId] = idomain;
11720 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11726 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11727 // for each shared face, get the nodes
11728 // for each node, for each domain of the face, create a clone of the node
11730 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11731 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11732 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11734 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11735 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11736 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11738 //MESSAGE(".. Duplication of the nodes");
11739 for (int idomain = idom0; idomain < nbDomains; idomain++)
11741 itface = faceDomains.begin();
11742 for (; itface != faceDomains.end(); ++itface)
11744 const std::map<int, int>& domvol = itface->second;
11745 if (!domvol.count(idomain))
11747 DownIdType face = itface->first;
11748 //MESSAGE(" --- face " << face.cellId);
11749 std::set<int> oldNodes;
11750 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11751 std::set<int>::iterator itn = oldNodes.begin();
11752 for (; itn != oldNodes.end(); ++itn)
11755 if (nodeDomains[oldId].empty())
11757 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11758 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11760 std::map<int, int>::const_iterator itdom = domvol.begin();
11761 for (; itdom != domvol.end(); ++itdom)
11763 int idom = itdom->first;
11764 //MESSAGE(" domain " << idom);
11765 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11767 if (nodeDomains[oldId].size() >= 2) // a multiple node
11769 vector<int> orderedDoms;
11770 //MESSAGE("multiple node " << oldId);
11771 if (mutipleNodes.count(oldId))
11772 orderedDoms = mutipleNodes[oldId];
11775 map<int,int>::iterator it = nodeDomains[oldId].begin();
11776 for (; it != nodeDomains[oldId].end(); ++it)
11777 orderedDoms.push_back(it->first);
11779 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11780 //stringstream txt;
11781 //for (int i=0; i<orderedDoms.size(); i++)
11782 // txt << orderedDoms[i] << " ";
11783 //MESSAGE("orderedDoms " << txt.str());
11784 mutipleNodes[oldId] = orderedDoms;
11786 double *coords = grid->GetPoint(oldId);
11787 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11788 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11789 int newId = newNode->GetVtkID();
11790 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11791 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11798 //MESSAGE(".. Creation of elements");
11799 for (int idomain = idom0; idomain < nbDomains; idomain++)
11801 itface = faceDomains.begin();
11802 for (; itface != faceDomains.end(); ++itface)
11804 std::map<int, int> domvol = itface->second;
11805 if (!domvol.count(idomain))
11807 DownIdType face = itface->first;
11808 //MESSAGE(" --- face " << face.cellId);
11809 std::set<int> oldNodes;
11810 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11811 int nbMultipleNodes = 0;
11812 std::set<int>::iterator itn = oldNodes.begin();
11813 for (; itn != oldNodes.end(); ++itn)
11816 if (mutipleNodes.count(oldId))
11819 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11821 //MESSAGE("multiple Nodes detected on a shared face");
11822 int downId = itface->first.cellId;
11823 unsigned char cellType = itface->first.cellType;
11824 // --- shared edge or shared face ?
11825 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11828 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11829 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11830 if (mutipleNodes.count(nodes[i]))
11831 if (!mutipleNodesToFace.count(nodes[i]))
11832 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11834 else // shared face (between two volumes)
11836 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11837 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11838 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11839 for (int ie =0; ie < nbEdges; ie++)
11842 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11843 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11845 vector<int> vn0 = mutipleNodes[nodes[0]];
11846 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11848 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11849 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11850 if ( vn0[i0] == vn1[i1] )
11851 doms.push_back( vn0[ i0 ]);
11852 if ( doms.size() > 2 )
11854 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11855 double *coords = grid->GetPoint(nodes[0]);
11856 gp_Pnt p0(coords[0], coords[1], coords[2]);
11857 coords = grid->GetPoint(nodes[nbNodes - 1]);
11858 gp_Pnt p1(coords[0], coords[1], coords[2]);
11860 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11861 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11862 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11863 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11864 for ( size_t id = 0; id < doms.size(); id++ )
11866 int idom = doms[id];
11867 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11868 for ( int ivol = 0; ivol < nbvol; ivol++ )
11870 smIdType smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11871 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11872 if (domain.count(elem))
11874 const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11875 domvol[idom] = (SMDS_MeshVolume*) svol;
11876 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11877 double values[3] = { 0,0,0 };
11878 vtkIdType npts = 0;
11879 vtkIdType const *pts(nullptr);
11880 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11881 for ( vtkIdType i = 0; i < npts; ++i )
11883 double *coords = grid->GetPoint( pts[i] );
11884 for ( int j = 0; j < 3; ++j )
11885 values[j] += coords[j] / npts;
11889 gref.SetCoord( values[0], values[1], values[2] );
11890 angleDom[idom] = 0;
11894 gp_Pnt g( values[0], values[1], values[2] );
11895 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11896 //MESSAGE(" angle=" << angleDom[idom]);
11902 map<double, int> sortedDom; // sort domains by angle
11903 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11904 sortedDom[ia->second] = ia->first;
11905 vector<int> vnodes;
11907 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11909 vdom.push_back(ib->second);
11910 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11912 for (int ino = 0; ino < nbNodes; ino++)
11913 vnodes.push_back(nodes[ino]);
11914 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11923 // --- iterate on shared faces (volumes to modify, face to extrude)
11924 // get node id's of the face (id SMDS = id VTK)
11925 // create flat element with old and new nodes if requested
11927 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11928 // (domain1 X domain2) = domain1 + MAXINT*domain2
11930 std::map<int, std::map<long,int> > nodeQuadDomains;
11931 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11933 //MESSAGE(".. Creation of elements: simple junction");
11934 if ( createJointElems )
11936 string joints2DName = "joints2D";
11937 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11938 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11939 string joints3DName = "joints3D";
11940 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11941 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11943 itface = faceDomains.begin();
11944 for (; itface != faceDomains.end(); ++itface)
11946 DownIdType face = itface->first;
11947 std::set<int> oldNodes;
11948 std::set<int>::iterator itn;
11949 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11951 std::map<int, int> domvol = itface->second;
11952 std::map<int, int>::iterator itdom = domvol.begin();
11953 int dom1 = itdom->first;
11954 int vtkVolId = itdom->second;
11956 int dom2 = itdom->first;
11957 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11959 stringstream grpname;
11962 grpname << dom1 << "_" << dom2;
11964 grpname << dom2 << "_" << dom1;
11965 string namegrp = grpname.str();
11966 if (!mapOfJunctionGroups.count(namegrp))
11967 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11968 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11970 sgrp->Add(vol->GetID());
11971 if (vol->GetType() == SMDSAbs_Volume)
11972 joints3DGrp->Add(vol->GetID());
11973 else if (vol->GetType() == SMDSAbs_Face)
11974 joints2DGrp->Add(vol->GetID());
11978 // --- create volumes on multiple domain intersection if requested
11979 // iterate on mutipleNodesToFace
11980 // iterate on edgesMultiDomains
11982 //MESSAGE(".. Creation of elements: multiple junction");
11983 if (createJointElems)
11985 // --- iterate on mutipleNodesToFace
11987 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11988 for (; itn != mutipleNodesToFace.end(); ++itn)
11990 int node = itn->first;
11991 vector<int> orderDom = itn->second;
11992 vector<vtkIdType> orderedNodes;
11993 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11994 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11995 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11997 stringstream grpname;
11999 grpname << 0 << "_" << 0;
12000 string namegrp = grpname.str();
12001 if (!mapOfJunctionGroups.count(namegrp))
12002 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
12003 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12005 sgrp->Add(face->GetID());
12008 // --- iterate on edgesMultiDomains
12010 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
12011 for (; ite != edgesMultiDomains.end(); ++ite)
12013 vector<int> nodes = ite->first;
12014 vector<int> orderDom = ite->second;
12015 vector<vtkIdType> orderedNodes;
12016 if (nodes.size() == 2)
12018 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
12019 for ( size_t ino = 0; ino < nodes.size(); ino++ )
12020 if ( orderDom.size() == 3 )
12021 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
12022 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
12024 for (int idom = orderDom.size()-1; idom >=0; idom--)
12025 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
12026 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
12028 string namegrp = "jointsMultiples";
12029 if (!mapOfJunctionGroups.count(namegrp))
12030 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
12031 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12033 sgrp->Add(vol->GetID());
12037 //INFOS("Quadratic multiple joints not implemented");
12038 // TODO quadratic nodes
12043 // --- list the explicit faces and edges of the mesh that need to be modified,
12044 // i.e. faces and edges built with one or more duplicated nodes.
12045 // associate these faces or edges to their corresponding domain.
12046 // only the first domain found is kept when a face or edge is shared
12048 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
12049 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
12051 //MESSAGE(".. Modification of elements");
12052 SMDSAbs_ElementType domainType = (*theElems[0].begin())->GetType();
12053 for (int idomain = idom0; idomain < nbDomains; idomain++)
12055 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
12056 for (; itnod != nodeDomains.end(); ++itnod)
12058 int oldId = itnod->first;
12059 //MESSAGE(" node " << oldId);
12060 vtkCellLinks::Link l = (static_cast< vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
12061 for (int i = 0; i < l.ncells; i++)
12063 int vtkId = l.cells[i];
12064 int vtkType = grid->GetCellType(vtkId);
12065 int downId = grid->CellIdToDownId(vtkId);
12067 continue; // new cells: not to be modified
12068 DownIdType aCell(downId, vtkType);
12069 int volParents[1000];
12071 nbvol = grid->GetParentVolumes(volParents, vtkId);
12072 if ( domainType == SMDSAbs_Volume )
12074 nbvol = grid->GetParentVolumes(volParents, vtkId);
12076 else // domainType == SMDSAbs_Face
12078 const int nbFaces = grid->getDownArray(vtkType)->getNumberOfUpCells(downId);
12079 const int *upCells = grid->getDownArray(vtkType)->getUpCells(downId);
12080 const unsigned char* upTypes = grid->getDownArray(vtkType)->getUpTypes(downId);
12081 for (int i=0; i< nbFaces; i++)
12083 int vtkFaceId = grid->getDownArray( upTypes[i] )->getVtkCellId(upCells[i]);
12084 if (vtkFaceId >= 0)
12085 volParents[nbvol++] = vtkFaceId;
12088 for (int j = 0; j < nbvol; j++)
12089 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
12090 if (!feDom.count(vtkId))
12092 feDom[vtkId] = idomain;
12093 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
12094 //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
12095 // << " type " << vtkType << " downId " << downId);
12101 // --- iterate on shared faces (volumes to modify, face to extrude)
12102 // get node id's of the face
12103 // replace old nodes by new nodes in volumes, and update inverse connectivity
12105 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
12106 for (int m=0; m<3; m++)
12108 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
12109 itface = (*amap).begin();
12110 for (; itface != (*amap).end(); ++itface)
12112 DownIdType face = itface->first;
12113 std::set<int> oldNodes;
12114 std::set<int>::iterator itn;
12115 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
12116 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
12117 std::map<int, int> localClonedNodeIds;
12119 std::map<int, int> domvol = itface->second;
12120 std::map<int, int>::iterator itdom = domvol.begin();
12121 for (; itdom != domvol.end(); ++itdom)
12123 int idom = itdom->first;
12124 int vtkVolId = itdom->second;
12125 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
12126 localClonedNodeIds.clear();
12127 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
12130 if (nodeDomains[oldId].count(idom))
12132 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
12133 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
12136 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
12141 // Remove empty groups (issue 0022812)
12142 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
12143 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
12145 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
12146 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
12149 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
12150 grid->DeleteLinks();
12158 * \brief Double nodes on some external faces and create flat elements.
12159 * Flat elements are mainly used by some types of mechanic calculations.
12161 * Each group of the list must be constituted of faces.
12162 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
12163 * @param theElems - list of groups of faces, where a group of faces is a set of
12164 * SMDS_MeshElements sorted by Id.
12165 * @return TRUE if operation has been completed successfully, FALSE otherwise
12167 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
12169 // MESSAGE("-------------------------------------------------");
12170 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
12171 // MESSAGE("-------------------------------------------------");
12173 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12175 // --- For each group of faces
12176 // duplicate the nodes, create a flat element based on the face
12177 // replace the nodes of the faces by their clones
12179 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
12180 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
12181 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
12183 for ( size_t idom = 0; idom < theElems.size(); idom++ )
12185 const TIDSortedElemSet& domain = theElems[idom];
12186 TIDSortedElemSet::const_iterator elemItr = domain.begin();
12187 for ( ; elemItr != domain.end(); ++elemItr )
12189 const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
12192 // MESSAGE("aFace=" << aFace->GetID());
12193 bool isQuad = aFace->IsQuadratic();
12194 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
12196 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
12198 SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
12199 while (nodeIt->more())
12201 const SMDS_MeshNode* node = nodeIt->next();
12202 bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
12204 ln2.push_back(node);
12206 ln0.push_back(node);
12208 const SMDS_MeshNode* clone = 0;
12209 if (!clonedNodes.count(node))
12211 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12212 copyPosition( node, clone );
12213 clonedNodes[node] = clone;
12216 clone = clonedNodes[node];
12219 ln3.push_back(clone);
12221 ln1.push_back(clone);
12223 const SMDS_MeshNode* inter = 0;
12224 if (isQuad && (!isMedium))
12226 if (!intermediateNodes.count(node))
12228 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12229 copyPosition( node, inter );
12230 intermediateNodes[node] = inter;
12233 inter = intermediateNodes[node];
12234 ln4.push_back(inter);
12238 // --- extrude the face
12240 vector<const SMDS_MeshNode*> ln;
12241 SMDS_MeshVolume* vol = 0;
12242 vtkIdType aType = aFace->GetVtkType();
12246 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12247 // MESSAGE("vol prism " << vol->GetID());
12248 ln.push_back(ln1[0]);
12249 ln.push_back(ln1[1]);
12250 ln.push_back(ln1[2]);
12253 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12254 // MESSAGE("vol hexa " << vol->GetID());
12255 ln.push_back(ln1[0]);
12256 ln.push_back(ln1[1]);
12257 ln.push_back(ln1[2]);
12258 ln.push_back(ln1[3]);
12260 case VTK_QUADRATIC_TRIANGLE:
12261 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12262 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12263 // MESSAGE("vol quad prism " << vol->GetID());
12264 ln.push_back(ln1[0]);
12265 ln.push_back(ln1[1]);
12266 ln.push_back(ln1[2]);
12267 ln.push_back(ln3[0]);
12268 ln.push_back(ln3[1]);
12269 ln.push_back(ln3[2]);
12271 case VTK_QUADRATIC_QUAD:
12272 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12273 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12274 // ln4[0], ln4[1], ln4[2], ln4[3]);
12275 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12276 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12277 ln4[0], ln4[1], ln4[2], ln4[3]);
12278 // MESSAGE("vol quad hexa " << vol->GetID());
12279 ln.push_back(ln1[0]);
12280 ln.push_back(ln1[1]);
12281 ln.push_back(ln1[2]);
12282 ln.push_back(ln1[3]);
12283 ln.push_back(ln3[0]);
12284 ln.push_back(ln3[1]);
12285 ln.push_back(ln3[2]);
12286 ln.push_back(ln3[3]);
12296 stringstream grpname;
12299 string namegrp = grpname.str();
12300 if (!mapOfJunctionGroups.count(namegrp))
12301 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
12302 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12304 sgrp->Add(vol->GetID());
12307 // --- modify the face
12309 const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
12316 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
12317 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
12318 * groups of faces to remove inside the object, (idem edges).
12319 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12321 void SMESH_MeshEditor::CreateHoleSkin(double radius,
12322 const TopoDS_Shape& theShape,
12323 SMESH_NodeSearcher* theNodeSearcher,
12324 const char* groupName,
12325 std::vector<double>& nodesCoords,
12326 std::vector<std::vector<int> >& listOfListOfNodes)
12328 // MESSAGE("--------------------------------");
12329 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12330 // MESSAGE("--------------------------------");
12332 // --- zone of volumes to remove is given :
12333 // 1 either by a geom shape (one or more vertices) and a radius,
12334 // 2 either by a group of nodes (representative of the shape)to use with the radius,
12335 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12336 // In the case 2, the group of nodes is an external group of nodes from another mesh,
12337 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12338 // defined by it's name.
12340 SMESHDS_GroupBase* groupDS = 0;
12341 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12342 while ( groupIt->more() )
12345 SMESH_Group * group = groupIt->next();
12346 if ( !group ) continue;
12347 groupDS = group->GetGroupDS();
12348 if ( !groupDS || groupDS->IsEmpty() ) continue;
12349 std::string grpName = group->GetName();
12350 //MESSAGE("grpName=" << grpName);
12351 if (grpName == groupName)
12357 bool isNodeGroup = false;
12358 bool isNodeCoords = false;
12361 if (groupDS->GetType() != SMDSAbs_Node)
12363 isNodeGroup = true; // a group of nodes exists and it is in this mesh
12366 if (nodesCoords.size() > 0)
12367 isNodeCoords = true; // a list o nodes given by their coordinates
12368 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12370 // --- define groups to build
12372 // --- group of SMDS volumes
12373 string grpvName = groupName;
12374 grpvName += "_vol";
12375 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
12378 MESSAGE("group not created " << grpvName);
12381 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12383 // --- group of SMDS faces on the skin
12384 string grpsName = groupName;
12385 grpsName += "_skin";
12386 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
12389 MESSAGE("group not created " << grpsName);
12392 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12394 // --- group of SMDS faces internal (several shapes)
12395 string grpiName = groupName;
12396 grpiName += "_internalFaces";
12397 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
12400 MESSAGE("group not created " << grpiName);
12403 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12405 // --- group of SMDS faces internal (several shapes)
12406 string grpeiName = groupName;
12407 grpeiName += "_internalEdges";
12408 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
12411 MESSAGE("group not created " << grpeiName);
12414 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12416 // --- build downward connectivity
12418 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12419 meshDS->BuildDownWardConnectivity(true);
12420 SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
12422 // --- set of volumes detected inside
12424 std::set<int> setOfInsideVol;
12425 std::set<int> setOfVolToCheck;
12427 std::vector<gp_Pnt> gpnts;
12429 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12431 //MESSAGE("group of nodes provided");
12432 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12433 while ( elemIt->more() )
12435 const SMDS_MeshElement* elem = elemIt->next();
12438 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12441 SMDS_MeshElement* vol = 0;
12442 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12443 while (volItr->more())
12445 vol = (SMDS_MeshElement*)volItr->next();
12446 setOfInsideVol.insert(vol->GetVtkID());
12447 sgrp->Add(vol->GetID());
12451 else if (isNodeCoords)
12453 //MESSAGE("list of nodes coordinates provided");
12456 while ( i < nodesCoords.size()-2 )
12458 double x = nodesCoords[i++];
12459 double y = nodesCoords[i++];
12460 double z = nodesCoords[i++];
12461 gp_Pnt p = gp_Pnt(x, y ,z);
12462 gpnts.push_back(p);
12463 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12467 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12469 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12470 TopTools_IndexedMapOfShape vertexMap;
12471 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12472 gp_Pnt p = gp_Pnt(0,0,0);
12473 if (vertexMap.Extent() < 1)
12476 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12478 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12479 p = BRep_Tool::Pnt(vertex);
12480 gpnts.push_back(p);
12481 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12485 if (gpnts.size() > 0)
12487 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12488 //MESSAGE("startNode->nodeId " << nodeId);
12490 double radius2 = radius*radius;
12491 //MESSAGE("radius2 " << radius2);
12493 // --- volumes on start node
12495 setOfVolToCheck.clear();
12496 SMDS_MeshElement* startVol = 0;
12497 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12498 while (volItr->more())
12500 startVol = (SMDS_MeshElement*)volItr->next();
12501 setOfVolToCheck.insert(startVol->GetVtkID());
12503 if (setOfVolToCheck.empty())
12505 MESSAGE("No volumes found");
12509 // --- starting with central volumes then their neighbors, check if they are inside
12510 // or outside the domain, until no more new neighbor volume is inside.
12511 // Fill the group of inside volumes
12513 std::map<int, double> mapOfNodeDistance2;
12514 std::set<int> setOfOutsideVol;
12515 while (!setOfVolToCheck.empty())
12517 std::set<int>::iterator it = setOfVolToCheck.begin();
12519 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12520 bool volInside = false;
12521 vtkIdType npts = 0;
12522 vtkIdType const *pts(nullptr);
12523 grid->GetCellPoints(vtkId, npts, pts);
12524 for (int i=0; i<npts; i++)
12526 double distance2 = 0;
12527 if (mapOfNodeDistance2.count(pts[i]))
12529 distance2 = mapOfNodeDistance2[pts[i]];
12530 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12534 double *coords = grid->GetPoint(pts[i]);
12535 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12537 for ( size_t j = 0; j < gpnts.size(); j++ )
12539 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12540 if (d2 < distance2)
12543 if (distance2 < radius2)
12547 mapOfNodeDistance2[pts[i]] = distance2;
12548 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12550 if (distance2 < radius2)
12552 volInside = true; // one or more nodes inside the domain
12553 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12559 setOfInsideVol.insert(vtkId);
12560 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12561 int neighborsVtkIds[NBMAXNEIGHBORS];
12562 int downIds[NBMAXNEIGHBORS];
12563 unsigned char downTypes[NBMAXNEIGHBORS];
12564 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12565 for (int n = 0; n < nbNeighbors; n++)
12566 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12567 setOfVolToCheck.insert(neighborsVtkIds[n]);
12571 setOfOutsideVol.insert(vtkId);
12572 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12574 setOfVolToCheck.erase(vtkId);
12578 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12579 // If yes, add the volume to the inside set
12581 bool addedInside = true;
12582 std::set<int> setOfVolToReCheck;
12583 while (addedInside)
12585 //MESSAGE(" --------------------------- re check");
12586 addedInside = false;
12587 std::set<int>::iterator itv = setOfInsideVol.begin();
12588 for (; itv != setOfInsideVol.end(); ++itv)
12591 int neighborsVtkIds[NBMAXNEIGHBORS];
12592 int downIds[NBMAXNEIGHBORS];
12593 unsigned char downTypes[NBMAXNEIGHBORS];
12594 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12595 for (int n = 0; n < nbNeighbors; n++)
12596 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12597 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12599 setOfVolToCheck = setOfVolToReCheck;
12600 setOfVolToReCheck.clear();
12601 while (!setOfVolToCheck.empty())
12603 std::set<int>::iterator it = setOfVolToCheck.begin();
12605 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12607 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12608 int countInside = 0;
12609 int neighborsVtkIds[NBMAXNEIGHBORS];
12610 int downIds[NBMAXNEIGHBORS];
12611 unsigned char downTypes[NBMAXNEIGHBORS];
12612 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12613 for (int n = 0; n < nbNeighbors; n++)
12614 if (setOfInsideVol.count(neighborsVtkIds[n]))
12616 //MESSAGE("countInside " << countInside);
12617 if (countInside > 1)
12619 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12620 setOfInsideVol.insert(vtkId);
12621 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12622 addedInside = true;
12625 setOfVolToReCheck.insert(vtkId);
12627 setOfVolToCheck.erase(vtkId);
12631 // --- map of Downward faces at the boundary, inside the global volume
12632 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12633 // fill group of SMDS faces inside the volume (when several volume shapes)
12634 // fill group of SMDS faces on the skin of the global volume (if skin)
12636 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12637 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12638 std::set<int>::iterator it = setOfInsideVol.begin();
12639 for (; it != setOfInsideVol.end(); ++it)
12642 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12643 int neighborsVtkIds[NBMAXNEIGHBORS];
12644 int downIds[NBMAXNEIGHBORS];
12645 unsigned char downTypes[NBMAXNEIGHBORS];
12646 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12647 for (int n = 0; n < nbNeighbors; n++)
12649 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12650 if (neighborDim == 3)
12652 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12654 DownIdType face(downIds[n], downTypes[n]);
12655 boundaryFaces[face] = vtkId;
12657 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12658 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12659 if (vtkFaceId >= 0)
12661 sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12662 // find also the smds edges on this face
12663 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12664 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12665 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12666 for (int i = 0; i < nbEdges; i++)
12668 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12669 if (vtkEdgeId >= 0)
12670 sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12674 else if (neighborDim == 2) // skin of the volume
12676 DownIdType face(downIds[n], downTypes[n]);
12677 skinFaces[face] = vtkId;
12678 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12679 if (vtkFaceId >= 0)
12680 sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12685 // --- identify the edges constituting the wire of each subshape on the skin
12686 // define polylines with the nodes of edges, equivalent to wires
12687 // project polylines on subshapes, and partition, to get geom faces
12689 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12690 std::set<int> shapeIds;
12692 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12693 while (itelem->more())
12695 const SMDS_MeshElement *elem = itelem->next();
12696 int shapeId = elem->getshapeId();
12697 int vtkId = elem->GetVtkID();
12698 if (!shapeIdToVtkIdSet.count(shapeId))
12700 shapeIds.insert(shapeId);
12702 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12705 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12706 std::set<DownIdType, DownIdCompare> emptyEdges;
12708 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12709 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12711 int shapeId = itShape->first;
12712 //MESSAGE(" --- Shape ID --- "<< shapeId);
12713 shapeIdToEdges[shapeId] = emptyEdges;
12715 std::vector<int> nodesEdges;
12717 std::set<int>::iterator its = itShape->second.begin();
12718 for (; its != itShape->second.end(); ++its)
12721 //MESSAGE(" " << vtkId);
12722 int neighborsVtkIds[NBMAXNEIGHBORS];
12723 int downIds[NBMAXNEIGHBORS];
12724 unsigned char downTypes[NBMAXNEIGHBORS];
12725 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12726 for (int n = 0; n < nbNeighbors; n++)
12728 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12730 smIdType smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12731 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12732 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12734 DownIdType edge(downIds[n], downTypes[n]);
12735 if (!shapeIdToEdges[shapeId].count(edge))
12737 shapeIdToEdges[shapeId].insert(edge);
12739 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12740 nodesEdges.push_back(vtkNodeId[0]);
12741 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12742 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12748 std::list<int> order;
12749 if (nodesEdges.size() > 0)
12751 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12752 nodesEdges[0] = -1;
12753 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12754 nodesEdges[1] = -1; // do not reuse this edge
12758 int nodeTofind = order.back(); // try first to push back
12760 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12761 if (nodesEdges[i] == nodeTofind)
12763 if ( i == (int) nodesEdges.size() )
12764 found = false; // no follower found on back
12767 if (i%2) // odd ==> use the previous one
12768 if (nodesEdges[i-1] < 0)
12772 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12773 nodesEdges[i-1] = -1;
12775 else // even ==> use the next one
12776 if (nodesEdges[i+1] < 0)
12780 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12781 nodesEdges[i+1] = -1;
12786 // try to push front
12788 nodeTofind = order.front(); // try to push front
12789 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12790 if ( nodesEdges[i] == nodeTofind )
12792 if ( i == (int)nodesEdges.size() )
12794 found = false; // no predecessor found on front
12797 if (i%2) // odd ==> use the previous one
12798 if (nodesEdges[i-1] < 0)
12802 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12803 nodesEdges[i-1] = -1;
12805 else // even ==> use the next one
12806 if (nodesEdges[i+1] < 0)
12810 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12811 nodesEdges[i+1] = -1;
12817 std::vector<int> nodes;
12818 nodes.push_back(shapeId);
12819 std::list<int>::iterator itl = order.begin();
12820 for (; itl != order.end(); itl++)
12822 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12823 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12825 listOfListOfNodes.push_back(nodes);
12828 // partition geom faces with blocFissure
12829 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12830 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12836 //================================================================================
12838 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12839 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12840 * \return TRUE if operation has been completed successfully, FALSE otherwise
12842 //================================================================================
12844 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12846 // iterates on volume elements and detect all free faces on them
12847 SMESHDS_Mesh* aMesh = GetMeshDS();
12851 ElemFeatures faceType( SMDSAbs_Face );
12852 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12853 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12856 const SMDS_MeshVolume* volume = vIt->next();
12857 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12858 vTool.SetExternalNormal();
12859 const int iQuad = volume->IsQuadratic();
12860 faceType.SetQuad( iQuad );
12861 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12863 if (!vTool.IsFreeFace(iface))
12866 vector<const SMDS_MeshNode *> nodes;
12867 int nbFaceNodes = vTool.NbFaceNodes(iface);
12868 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12870 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12871 nodes.push_back(faceNodes[inode]);
12873 if (iQuad) // add medium nodes
12875 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12876 nodes.push_back(faceNodes[inode]);
12877 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12878 nodes.push_back(faceNodes[8]);
12880 // add new face based on volume nodes
12881 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12883 nbExisted++; // face already exists
12887 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12892 return ( nbFree == ( nbExisted + nbCreated ));
12897 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12899 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12901 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12904 //================================================================================
12906 * \brief Creates missing boundary elements
12907 * \param elements - elements whose boundary is to be checked
12908 * \param dimension - defines type of boundary elements to create
12909 * \param group - a group to store created boundary elements in
12910 * \param targetMesh - a mesh to store created boundary elements in
12911 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12912 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12913 * boundary elements will be copied into the targetMesh
12914 * \param toAddExistingBondary - if true, not only new but also pre-existing
12915 * boundary elements will be added into the new group
12916 * \param aroundElements - if true, elements will be created on boundary of given
12917 * elements else, on boundary of the whole mesh.
12918 * \return nb of added boundary elements
12920 //================================================================================
12922 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12923 Bnd_Dimension dimension,
12924 SMESH_Group* group/*=0*/,
12925 SMESH_Mesh* targetMesh/*=0*/,
12926 bool toCopyElements/*=false*/,
12927 bool toCopyExistingBoundary/*=false*/,
12928 bool toAddExistingBondary/*= false*/,
12929 bool aroundElements/*= false*/,
12930 bool toCreateAllElements/*= false*/)
12932 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12933 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12934 // hope that all elements are of the same type, do not check them all
12935 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12936 throw SALOME_Exception(LOCALIZED("wrong element type"));
12939 toCopyElements = toCopyExistingBoundary = false;
12941 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12942 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12943 int nbAddedBnd = 0;
12945 // editor adding present bnd elements and optionally holding elements to add to the group
12946 SMESH_MeshEditor* presentEditor;
12947 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12948 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12949 SMESH_MesherHelper helper( *myMesh );
12950 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12951 SMDS_VolumeTool vTool;
12952 TIDSortedElemSet avoidSet;
12953 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12956 typedef vector<const SMDS_MeshNode*> TConnectivity;
12957 TConnectivity tgtNodes;
12958 ElemFeatures elemKind( missType ), elemToCopy;
12960 vector<const SMDS_MeshElement*> presentBndElems;
12961 vector<TConnectivity> missingBndElems;
12962 vector<int> freeFacets;
12963 TConnectivity nodes, elemNodes;
12965 SMDS_ElemIteratorPtr eIt;
12966 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12967 else eIt = SMESHUtils::elemSetIterator( elements );
12969 while ( eIt->more() )
12971 const SMDS_MeshElement* elem = eIt->next();
12972 const int iQuad = elem->IsQuadratic();
12973 elemKind.SetQuad( iQuad );
12975 // ------------------------------------------------------------------------------------
12976 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12977 // ------------------------------------------------------------------------------------
12978 presentBndElems.clear();
12979 missingBndElems.clear();
12980 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12981 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12983 const SMDS_MeshElement* otherVol = 0;
12984 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12986 if ( !toCreateAllElements &&
12987 !vTool.IsFreeFace(iface, &otherVol) &&
12988 ( !aroundElements || elements.count( otherVol )))
12990 freeFacets.push_back( iface );
12992 if ( missType == SMDSAbs_Face )
12993 vTool.SetExternalNormal();
12994 for ( size_t i = 0; i < freeFacets.size(); ++i )
12996 int iface = freeFacets[i];
12997 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12998 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12999 if ( missType == SMDSAbs_Edge ) // boundary edges
13001 nodes.resize( 2+iQuad );
13002 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
13004 for ( size_t j = 0; j < nodes.size(); ++j )
13005 nodes[ j ] = nn[ i+j ];
13006 if ( const SMDS_MeshElement* edge =
13007 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
13008 presentBndElems.push_back( edge );
13010 missingBndElems.push_back( nodes );
13013 else // boundary face
13016 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
13017 nodes.push_back( nn[inode] ); // add corner nodes
13019 for ( inode = 1; inode < nbFaceNodes; inode += 2)
13020 nodes.push_back( nn[inode] ); // add medium nodes
13021 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
13023 nodes.push_back( vTool.GetNodes()[ iCenter ] );
13025 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
13026 SMDSAbs_Face, /*noMedium=*/false ))
13027 presentBndElems.push_back( f );
13029 missingBndElems.push_back( nodes );
13031 if ( targetMesh != myMesh )
13033 // add 1D elements on face boundary to be added to a new mesh
13034 const SMDS_MeshElement* edge;
13035 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
13038 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
13040 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
13041 if ( edge && avoidSet.insert( edge ).second )
13042 presentBndElems.push_back( edge );
13048 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
13050 avoidSet.clear(), avoidSet.insert( elem );
13051 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
13052 SMDS_MeshElement::iterator() );
13053 elemNodes.push_back( elemNodes[0] );
13054 nodes.resize( 2 + iQuad );
13055 const int nbLinks = elem->NbCornerNodes();
13056 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
13058 nodes[0] = elemNodes[iN];
13059 nodes[1] = elemNodes[iN+1+iQuad];
13060 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
13061 continue; // not free link
13063 if ( iQuad ) nodes[2] = elemNodes[iN+1];
13064 if ( const SMDS_MeshElement* edge =
13065 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
13066 presentBndElems.push_back( edge );
13068 missingBndElems.push_back( nodes );
13072 // ---------------------------------
13073 // 2. Add missing boundary elements
13074 // ---------------------------------
13075 if ( targetMesh != myMesh )
13076 // instead of making a map of nodes in this mesh and targetMesh,
13077 // we create nodes with same IDs.
13078 for ( size_t i = 0; i < missingBndElems.size(); ++i )
13080 TConnectivity& srcNodes = missingBndElems[i];
13081 tgtNodes.resize( srcNodes.size() );
13082 for ( inode = 0; inode < srcNodes.size(); ++inode )
13083 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
13084 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
13086 /*noMedium=*/false))
13088 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
13092 for ( size_t i = 0; i < missingBndElems.size(); ++i )
13094 TConnectivity& nodes = missingBndElems[ i ];
13095 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
13097 /*noMedium=*/false))
13099 SMDS_MeshElement* newElem =
13100 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
13101 nbAddedBnd += bool( newElem );
13103 // try to set a new element to a shape
13104 if ( myMesh->HasShapeToMesh() )
13107 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
13108 const size_t nbN = nodes.size() / (iQuad+1 );
13109 for ( inode = 0; inode < nbN && ok; ++inode )
13111 pair<int, TopAbs_ShapeEnum> i_stype =
13112 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
13113 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
13114 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
13116 if ( ok && mediumShapes.size() > 1 )
13118 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
13119 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
13120 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
13122 if (( ok = ( stype_i->first != stype_i_0.first )))
13123 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
13124 aMesh->IndexToShape( stype_i_0.second ));
13127 if ( ok && mediumShapes.begin()->first == missShapeType )
13128 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
13132 // ----------------------------------
13133 // 3. Copy present boundary elements
13134 // ----------------------------------
13135 if ( toCopyExistingBoundary )
13136 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13138 const SMDS_MeshElement* e = presentBndElems[i];
13139 tgtNodes.resize( e->NbNodes() );
13140 for ( inode = 0; inode < tgtNodes.size(); ++inode )
13141 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
13142 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
13144 else // store present elements to add them to a group
13145 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13147 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
13150 } // loop on given elements
13152 // ---------------------------------------------
13153 // 4. Fill group with boundary elements
13154 // ---------------------------------------------
13157 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
13158 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
13159 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
13161 tgtEditor.myLastCreatedElems.clear();
13162 tgtEditor2.myLastCreatedElems.clear();
13164 // -----------------------
13165 // 5. Copy given elements
13166 // -----------------------
13167 if ( toCopyElements && targetMesh != myMesh )
13169 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
13170 else eIt = SMESHUtils::elemSetIterator( elements );
13171 while (eIt->more())
13173 const SMDS_MeshElement* elem = eIt->next();
13174 tgtNodes.resize( elem->NbNodes() );
13175 for ( inode = 0; inode < tgtNodes.size(); ++inode )
13176 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
13177 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
13179 tgtEditor.myLastCreatedElems.clear();
13185 //================================================================================
13187 * \brief Copy node position and set \a to node on the same geometry
13189 //================================================================================
13191 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
13192 const SMDS_MeshNode* to )
13194 if ( !from || !to ) return;
13196 SMDS_PositionPtr pos = from->GetPosition();
13197 if ( !pos || from->getshapeId() < 1 ) return;
13199 switch ( pos->GetTypeOfPosition() )
13201 case SMDS_TOP_3DSPACE: break;
13203 case SMDS_TOP_FACE:
13205 SMDS_FacePositionPtr fPos = pos;
13206 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
13207 fPos->GetUParameter(), fPos->GetVParameter() );
13210 case SMDS_TOP_EDGE:
13212 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
13213 SMDS_EdgePositionPtr ePos = pos;
13214 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
13217 case SMDS_TOP_VERTEX:
13219 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
13222 case SMDS_TOP_UNSPEC: