1 // Copyright (C) 2007-2019 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
49 #include "utilities.h"
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_ListOfInteger.hxx>
66 #include <TopAbs_State.hxx>
68 #include <TopExp_Explorer.hxx>
69 #include <TopTools_ListIteratorOfListOfShape.hxx>
70 #include <TopTools_ListOfShape.hxx>
71 #include <TopTools_SequenceOfShape.hxx>
73 #include <TopoDS_Edge.hxx>
74 #include <TopoDS_Face.hxx>
75 #include <TopoDS_Solid.hxx>
81 #include <gp_Trsf.hxx>
95 #include <boost/tuple/tuple.hpp>
96 #include <boost/container/flat_set.hpp>
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
101 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
103 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
106 using namespace SMESH::Controls;
108 //=======================================================================
109 //function : SMESH_MeshEditor
111 //=======================================================================
113 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
114 :myMesh( theMesh ) // theMesh may be NULL
118 //================================================================================
120 * \brief Return mesh DS
122 //================================================================================
124 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
126 return myMesh->GetMeshDS();
130 //================================================================================
132 * \brief Clears myLastCreatedNodes and myLastCreatedElems
134 //================================================================================
136 void SMESH_MeshEditor::ClearLastCreated()
138 SMESHUtils::FreeVector( myLastCreatedElems );
139 SMESHUtils::FreeVector( myLastCreatedNodes );
142 //================================================================================
144 * \brief Initializes members by an existing element
145 * \param [in] elem - the source element
146 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
148 //================================================================================
150 SMESH_MeshEditor::ElemFeatures&
151 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
155 myType = elem->GetType();
156 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
158 myIsPoly = elem->IsPoly();
161 myIsQuad = elem->IsQuadratic();
162 if ( myType == SMDSAbs_Volume && !basicOnly )
164 vector<int> quant = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
165 myPolyhedQuantities.swap( quant );
169 else if ( myType == SMDSAbs_Ball && !basicOnly )
171 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
177 //=======================================================================
181 //=======================================================================
184 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
185 const ElemFeatures& features)
187 SMDS_MeshElement* e = 0;
188 int nbnode = node.size();
189 SMESHDS_Mesh* mesh = GetMeshDS();
190 const int ID = features.myID;
192 switch ( features.myType ) {
194 if ( !features.myIsPoly ) {
196 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
197 else e = mesh->AddFace (node[0], node[1], node[2] );
199 else if (nbnode == 4) {
200 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
201 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
203 else if (nbnode == 6) {
204 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
205 node[4], node[5], ID);
206 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
209 else if (nbnode == 7) {
210 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
211 node[4], node[5], node[6], ID);
212 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
213 node[4], node[5], node[6] );
215 else if (nbnode == 8) {
216 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
217 node[4], node[5], node[6], node[7], ID);
218 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
219 node[4], node[5], node[6], node[7] );
221 else if (nbnode == 9) {
222 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
223 node[4], node[5], node[6], node[7], node[8], ID);
224 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
225 node[4], node[5], node[6], node[7], node[8] );
228 else if ( !features.myIsQuad )
230 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
231 else e = mesh->AddPolygonalFace (node );
233 else if ( nbnode % 2 == 0 ) // just a protection
235 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
236 else e = mesh->AddQuadPolygonalFace (node );
241 if ( !features.myIsPoly ) {
243 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
244 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
246 else if (nbnode == 5) {
247 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
249 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
252 else if (nbnode == 6) {
253 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
254 node[4], node[5], ID);
255 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
258 else if (nbnode == 8) {
259 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
260 node[4], node[5], node[6], node[7], ID);
261 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
262 node[4], node[5], node[6], node[7] );
264 else if (nbnode == 10) {
265 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
266 node[4], node[5], node[6], node[7],
267 node[8], node[9], ID);
268 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
269 node[4], node[5], node[6], node[7],
272 else if (nbnode == 12) {
273 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
274 node[4], node[5], node[6], node[7],
275 node[8], node[9], node[10], node[11], ID);
276 else e = mesh->AddVolume (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] );
280 else if (nbnode == 13) {
281 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
282 node[4], node[5], node[6], node[7],
283 node[8], node[9], node[10],node[11],
285 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
286 node[4], node[5], node[6], node[7],
287 node[8], node[9], node[10],node[11],
290 else if (nbnode == 15) {
291 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
292 node[4], node[5], node[6], node[7],
293 node[8], node[9], node[10],node[11],
294 node[12],node[13],node[14],ID);
295 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
296 node[4], node[5], node[6], node[7],
297 node[8], node[9], node[10],node[11],
298 node[12],node[13],node[14] );
300 else if (nbnode == 20) {
301 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
302 node[4], node[5], node[6], node[7],
303 node[8], node[9], node[10],node[11],
304 node[12],node[13],node[14],node[15],
305 node[16],node[17],node[18],node[19],ID);
306 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
307 node[4], node[5], node[6], node[7],
308 node[8], node[9], node[10],node[11],
309 node[12],node[13],node[14],node[15],
310 node[16],node[17],node[18],node[19] );
312 else if (nbnode == 27) {
313 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
314 node[4], node[5], node[6], node[7],
315 node[8], node[9], node[10],node[11],
316 node[12],node[13],node[14],node[15],
317 node[16],node[17],node[18],node[19],
318 node[20],node[21],node[22],node[23],
319 node[24],node[25],node[26], ID);
320 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
321 node[4], node[5], node[6], node[7],
322 node[8], node[9], node[10],node[11],
323 node[12],node[13],node[14],node[15],
324 node[16],node[17],node[18],node[19],
325 node[20],node[21],node[22],node[23],
326 node[24],node[25],node[26] );
329 else if ( !features.myIsQuad )
331 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
332 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
336 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
337 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
343 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
344 else e = mesh->AddEdge (node[0], node[1] );
346 else if ( nbnode == 3 ) {
347 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
348 else e = mesh->AddEdge (node[0], node[1], node[2] );
352 case SMDSAbs_0DElement:
354 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
355 else e = mesh->Add0DElement (node[0] );
360 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
361 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
365 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
366 else e = mesh->AddBall (node[0], features.myBallDiameter );
371 if ( e ) myLastCreatedElems.push_back( e );
375 //=======================================================================
379 //=======================================================================
381 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
382 const ElemFeatures& features)
384 vector<const SMDS_MeshNode*> nodes;
385 nodes.reserve( nodeIDs.size() );
386 vector<int>::const_iterator id = nodeIDs.begin();
387 while ( id != nodeIDs.end() ) {
388 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
389 nodes.push_back( node );
393 return AddElement( nodes, features );
396 //=======================================================================
398 //purpose : Remove a node or an element.
399 // Modify a compute state of sub-meshes which become empty
400 //=======================================================================
402 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
407 SMESHDS_Mesh* aMesh = GetMeshDS();
408 set< SMESH_subMesh *> smmap;
411 list<int>::const_iterator it = theIDs.begin();
412 for ( ; it != theIDs.end(); it++ ) {
413 const SMDS_MeshElement * elem;
415 elem = aMesh->FindNode( *it );
417 elem = aMesh->FindElement( *it );
421 // Notify VERTEX sub-meshes about modification
423 const SMDS_MeshNode* node = cast2Node( elem );
424 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
425 if ( int aShapeID = node->getshapeId() )
426 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
429 // Find sub-meshes to notify about modification
430 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
431 // while ( nodeIt->more() ) {
432 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
433 // const SMDS_PositionPtr& aPosition = node->GetPosition();
434 // if ( aPosition.get() ) {
435 // if ( int aShapeID = aPosition->GetShapeId() ) {
436 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
437 // smmap.insert( sm );
444 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
446 aMesh->RemoveElement( elem );
450 // Notify sub-meshes about modification
451 if ( !smmap.empty() ) {
452 set< SMESH_subMesh *>::iterator smIt;
453 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
454 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
457 // // Check if the whole mesh becomes empty
458 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
459 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
464 //================================================================================
466 * \brief Create 0D elements on all nodes of the given object.
467 * \param elements - Elements on whose nodes to create 0D elements; if empty,
468 * the all mesh is treated
469 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
470 * \param duplicateElements - to add one more 0D element to a node or not
472 //================================================================================
474 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
475 TIDSortedElemSet& all0DElems,
476 const bool duplicateElements )
478 SMDS_ElemIteratorPtr elemIt;
479 if ( elements.empty() )
481 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
485 elemIt = SMESHUtils::elemSetIterator( elements );
488 while ( elemIt->more() )
490 const SMDS_MeshElement* e = elemIt->next();
491 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
492 while ( nodeIt->more() )
494 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
495 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
496 if ( duplicateElements || !it0D->more() )
498 myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
499 all0DElems.insert( myLastCreatedElems.back() );
501 while ( it0D->more() )
502 all0DElems.insert( it0D->next() );
507 //=======================================================================
508 //function : FindShape
509 //purpose : Return an index of the shape theElem is on
510 // or zero if a shape not found
511 //=======================================================================
513 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
517 SMESHDS_Mesh * aMesh = GetMeshDS();
518 if ( aMesh->ShapeToMesh().IsNull() )
521 int aShapeID = theElem->getshapeId();
525 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
526 if ( sm->Contains( theElem ))
529 if ( theElem->GetType() == SMDSAbs_Node ) {
530 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
533 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
536 TopoDS_Shape aShape; // the shape a node of theElem is on
537 if ( theElem->GetType() != SMDSAbs_Node )
539 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
540 while ( nodeIt->more() ) {
541 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
542 if ((aShapeID = node->getshapeId()) > 0) {
543 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
544 if ( sm->Contains( theElem ))
546 if ( aShape.IsNull() )
547 aShape = aMesh->IndexToShape( aShapeID );
553 // None of nodes is on a proper shape,
554 // find the shape among ancestors of aShape on which a node is
555 if ( !aShape.IsNull() ) {
556 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
557 for ( ; ancIt.More(); ancIt.Next() ) {
558 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
559 if ( sm && sm->Contains( theElem ))
560 return aMesh->ShapeToIndex( ancIt.Value() );
565 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
566 while ( const SMESHDS_SubMesh* sm = smIt->next() )
567 if ( sm->Contains( theElem ))
574 //=======================================================================
575 //function : IsMedium
577 //=======================================================================
579 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
580 const SMDSAbs_ElementType typeToCheck)
582 bool isMedium = false;
583 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
584 while (it->more() && !isMedium ) {
585 const SMDS_MeshElement* elem = it->next();
586 isMedium = elem->IsMediumNode(node);
591 //=======================================================================
592 //function : shiftNodesQuadTria
593 //purpose : Shift nodes in the array corresponded to quadratic triangle
594 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
595 //=======================================================================
597 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
599 const SMDS_MeshNode* nd1 = aNodes[0];
600 aNodes[0] = aNodes[1];
601 aNodes[1] = aNodes[2];
603 const SMDS_MeshNode* nd2 = aNodes[3];
604 aNodes[3] = aNodes[4];
605 aNodes[4] = aNodes[5];
609 //=======================================================================
610 //function : getNodesFromTwoTria
612 //=======================================================================
614 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
615 const SMDS_MeshElement * theTria2,
616 vector< const SMDS_MeshNode*>& N1,
617 vector< const SMDS_MeshNode*>& N2)
619 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
620 if ( N1.size() < 6 ) return false;
621 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
622 if ( N2.size() < 6 ) return false;
624 int sames[3] = {-1,-1,-1};
636 if(nbsames!=2) return false;
638 shiftNodesQuadTria(N1);
640 shiftNodesQuadTria(N1);
643 i = sames[0] + sames[1] + sames[2];
645 shiftNodesQuadTria(N2);
647 // now we receive following N1 and N2 (using numeration as in the image below)
648 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
649 // i.e. first nodes from both arrays form a new diagonal
653 //=======================================================================
654 //function : InverseDiag
655 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
656 // but having other common link.
657 // Return False if args are improper
658 //=======================================================================
660 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
661 const SMDS_MeshElement * theTria2 )
665 if ( !theTria1 || !theTria2 ||
666 !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
667 !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
668 theTria1->GetType() != SMDSAbs_Face ||
669 theTria2->GetType() != SMDSAbs_Face )
672 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
673 (theTria2->GetEntityType() == SMDSEntity_Triangle))
675 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
676 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
680 // put nodes in array and find out indices of the same ones
681 const SMDS_MeshNode* aNodes [6];
682 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
684 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
685 while ( it->more() ) {
686 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
688 if ( i > 2 ) // theTria2
689 // find same node of theTria1
690 for ( int j = 0; j < 3; j++ )
691 if ( aNodes[ i ] == aNodes[ j ]) {
700 return false; // theTria1 is not a triangle
701 it = theTria2->nodesIterator();
703 if ( i == 6 && it->more() )
704 return false; // theTria2 is not a triangle
707 // find indices of 1,2 and of A,B in theTria1
708 int iA = -1, iB = 0, i1 = 0, i2 = 0;
709 for ( i = 0; i < 6; i++ ) {
710 if ( sameInd [ i ] == -1 ) {
715 if ( iA >= 0) iB = i;
719 // nodes 1 and 2 should not be the same
720 if ( aNodes[ i1 ] == aNodes[ i2 ] )
724 aNodes[ iA ] = aNodes[ i2 ];
726 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
728 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
729 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
733 } // end if(F1 && F2)
735 // check case of quadratic faces
736 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
737 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
739 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
740 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
744 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
745 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
753 vector< const SMDS_MeshNode* > N1;
754 vector< const SMDS_MeshNode* > N2;
755 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
757 // now we receive following N1 and N2 (using numeration as above image)
758 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
759 // i.e. first nodes from both arrays determ new diagonal
761 vector< const SMDS_MeshNode*> N1new( N1.size() );
762 vector< const SMDS_MeshNode*> N2new( N2.size() );
763 N1new.back() = N1.back(); // central node of biquadratic
764 N2new.back() = N2.back();
765 N1new[0] = N1[0]; N2new[0] = N1[0];
766 N1new[1] = N2[0]; N2new[1] = N1[1];
767 N1new[2] = N2[1]; N2new[2] = N2[0];
768 N1new[3] = N1[4]; N2new[3] = N1[3];
769 N1new[4] = N2[3]; N2new[4] = N2[5];
770 N1new[5] = N1[5]; N2new[5] = N1[4];
771 // change nodes in faces
772 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
773 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
775 // move the central node of biquadratic triangle
776 SMESH_MesherHelper helper( *GetMesh() );
777 for ( int is2nd = 0; is2nd < 2; ++is2nd )
779 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
780 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
781 if ( nodes.size() < 7 )
783 helper.SetSubShape( tria->getshapeId() );
784 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
788 xyz = ( SMESH_NodeXYZ( nodes[3] ) +
789 SMESH_NodeXYZ( nodes[4] ) +
790 SMESH_NodeXYZ( nodes[5] )) / 3.;
795 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
796 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
797 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
799 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
800 xyz = S->Value( uv.X(), uv.Y() );
801 xyz.Transform( loc );
802 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
803 nodes[6]->getshapeId() > 0 )
804 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
806 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
811 //=======================================================================
812 //function : findTriangles
813 //purpose : find triangles sharing theNode1-theNode2 link
814 //=======================================================================
816 static bool findTriangles(const SMDS_MeshNode * theNode1,
817 const SMDS_MeshNode * theNode2,
818 const SMDS_MeshElement*& theTria1,
819 const SMDS_MeshElement*& theTria2)
821 if ( !theNode1 || !theNode2 ) return false;
823 theTria1 = theTria2 = 0;
825 set< const SMDS_MeshElement* > emap;
826 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
828 const SMDS_MeshElement* elem = it->next();
829 if ( elem->NbCornerNodes() == 3 )
832 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
834 const SMDS_MeshElement* elem = it->next();
835 if ( emap.count( elem )) {
843 // theTria1 must be element with minimum ID
844 if ( theTria2->GetID() < theTria1->GetID() )
845 std::swap( theTria2, theTria1 );
853 //=======================================================================
854 //function : InverseDiag
855 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
856 // with ones built on the same 4 nodes but having other common link.
857 // Return false if proper faces not found
858 //=======================================================================
860 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
861 const SMDS_MeshNode * theNode2)
865 const SMDS_MeshElement *tr1, *tr2;
866 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
869 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
870 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
873 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
874 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
876 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
877 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
881 // put nodes in array
882 // and find indices of 1,2 and of A in tr1 and of B in tr2
883 int i, iA1 = 0, i1 = 0;
884 const SMDS_MeshNode* aNodes1 [3];
885 SMDS_ElemIteratorPtr it;
886 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
887 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
888 if ( aNodes1[ i ] == theNode1 )
889 iA1 = i; // node A in tr1
890 else if ( aNodes1[ i ] != theNode2 )
894 const SMDS_MeshNode* aNodes2 [3];
895 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
896 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
897 if ( aNodes2[ i ] == theNode2 )
898 iB2 = i; // node B in tr2
899 else if ( aNodes2[ i ] != theNode1 )
903 // nodes 1 and 2 should not be the same
904 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
908 aNodes1[ iA1 ] = aNodes2[ i2 ];
910 aNodes2[ iB2 ] = aNodes1[ i1 ];
912 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
913 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
918 // check case of quadratic faces
919 return InverseDiag(tr1,tr2);
922 //=======================================================================
923 //function : getQuadrangleNodes
924 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
925 // fusion of triangles tr1 and tr2 having shared link on
926 // theNode1 and theNode2
927 //=======================================================================
929 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
930 const SMDS_MeshNode * theNode1,
931 const SMDS_MeshNode * theNode2,
932 const SMDS_MeshElement * tr1,
933 const SMDS_MeshElement * tr2 )
935 if( tr1->NbNodes() != tr2->NbNodes() )
937 // find the 4-th node to insert into tr1
938 const SMDS_MeshNode* n4 = 0;
939 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
941 while ( !n4 && i<3 ) {
942 const SMDS_MeshNode * n = cast2Node( it->next() );
944 bool isDiag = ( n == theNode1 || n == theNode2 );
948 // Make an array of nodes to be in a quadrangle
949 int iNode = 0, iFirstDiag = -1;
950 it = tr1->nodesIterator();
953 const SMDS_MeshNode * n = cast2Node( it->next() );
955 bool isDiag = ( n == theNode1 || n == theNode2 );
957 if ( iFirstDiag < 0 )
959 else if ( iNode - iFirstDiag == 1 )
960 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
962 else if ( n == n4 ) {
963 return false; // tr1 and tr2 should not have all the same nodes
965 theQuadNodes[ iNode++ ] = n;
967 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
968 theQuadNodes[ iNode ] = n4;
973 //=======================================================================
974 //function : DeleteDiag
975 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
976 // with a quadrangle built on the same 4 nodes.
977 // Return false if proper faces not found
978 //=======================================================================
980 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
981 const SMDS_MeshNode * theNode2)
985 const SMDS_MeshElement *tr1, *tr2;
986 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
989 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
990 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
993 SMESHDS_Mesh * aMesh = GetMeshDS();
995 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
996 (tr2->GetEntityType() == SMDSEntity_Triangle))
998 const SMDS_MeshNode* aNodes [ 4 ];
999 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1002 const SMDS_MeshElement* newElem = 0;
1003 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1004 myLastCreatedElems.push_back(newElem);
1005 AddToSameGroups( newElem, tr1, aMesh );
1006 int aShapeId = tr1->getshapeId();
1008 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1010 aMesh->RemoveElement( tr1 );
1011 aMesh->RemoveElement( tr2 );
1016 // check case of quadratic faces
1017 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1019 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1023 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1024 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1032 vector< const SMDS_MeshNode* > N1;
1033 vector< const SMDS_MeshNode* > N2;
1034 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1036 // now we receive following N1 and N2 (using numeration as above image)
1037 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1038 // i.e. first nodes from both arrays determ new diagonal
1040 const SMDS_MeshNode* aNodes[8];
1050 const SMDS_MeshElement* newElem = 0;
1051 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1052 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1053 myLastCreatedElems.push_back(newElem);
1054 AddToSameGroups( newElem, tr1, aMesh );
1055 int aShapeId = tr1->getshapeId();
1058 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1060 aMesh->RemoveElement( tr1 );
1061 aMesh->RemoveElement( tr2 );
1063 // remove middle node (9)
1064 GetMeshDS()->RemoveNode( N1[4] );
1069 //=======================================================================
1070 //function : Reorient
1071 //purpose : Reverse theElement orientation
1072 //=======================================================================
1074 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1080 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1081 if ( !it || !it->more() )
1084 const SMDSAbs_ElementType type = theElem->GetType();
1085 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1088 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1089 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1091 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1093 MESSAGE("Warning: bad volumic element");
1096 const int nbFaces = aPolyedre->NbFaces();
1097 vector<const SMDS_MeshNode *> poly_nodes;
1098 vector<int> quantities (nbFaces);
1100 // reverse each face of the polyedre
1101 for (int iface = 1; iface <= nbFaces; iface++) {
1102 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1103 quantities[iface - 1] = nbFaceNodes;
1105 for (inode = nbFaceNodes; inode >= 1; inode--) {
1106 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1107 poly_nodes.push_back(curNode);
1110 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1112 else // other elements
1114 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1115 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1116 if ( interlace.empty() )
1118 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1122 SMDS_MeshCell::applyInterlace( interlace, nodes );
1124 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1129 //================================================================================
1131 * \brief Reorient faces.
1132 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1133 * \param theDirection - desired direction of normal of \a theFace
1134 * \param theFace - one of \a theFaces that should be oriented according to
1135 * \a theDirection and whose orientation defines orientation of other faces
1136 * \return number of reoriented faces.
1138 //================================================================================
1140 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1141 const gp_Dir& theDirection,
1142 const SMDS_MeshElement * theFace)
1145 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1147 if ( theFaces.empty() )
1149 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
1150 while ( fIt->more() )
1151 theFaces.insert( theFaces.end(), fIt->next() );
1154 // orient theFace according to theDirection
1156 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1157 if ( normal * theDirection.XYZ() < 0 )
1158 nbReori += Reorient( theFace );
1160 // Orient other faces
1162 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1163 TIDSortedElemSet avoidSet;
1164 set< SMESH_TLink > checkedLinks;
1165 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1167 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1168 theFaces.erase( theFace );
1169 startFaces.insert( theFace );
1171 int nodeInd1, nodeInd2;
1172 const SMDS_MeshElement* otherFace;
1173 vector< const SMDS_MeshElement* > facesNearLink;
1174 vector< std::pair< int, int > > nodeIndsOfFace;
1176 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1177 while ( !startFaces.empty() )
1179 startFace = startFaces.begin();
1180 theFace = *startFace;
1181 startFaces.erase( startFace );
1182 if ( !visitedFaces.insert( theFace ).second )
1186 avoidSet.insert(theFace);
1188 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1190 const int nbNodes = theFace->NbCornerNodes();
1191 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1193 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1194 linkIt_isNew = checkedLinks.insert( link );
1195 if ( !linkIt_isNew.second )
1197 // link has already been checked and won't be encountered more
1198 // if the group (theFaces) is manifold
1199 //checkedLinks.erase( linkIt_isNew.first );
1203 facesNearLink.clear();
1204 nodeIndsOfFace.clear();
1205 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1207 &nodeInd1, &nodeInd2 )))
1208 if ( otherFace != theFace)
1210 facesNearLink.push_back( otherFace );
1211 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1212 avoidSet.insert( otherFace );
1214 if ( facesNearLink.size() > 1 )
1216 // NON-MANIFOLD mesh shell !
1217 // select a face most co-directed with theFace,
1218 // other faces won't be visited this time
1220 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1221 double proj, maxProj = -1;
1222 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1223 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1224 if (( proj = Abs( NF * NOF )) > maxProj ) {
1226 otherFace = facesNearLink[i];
1227 nodeInd1 = nodeIndsOfFace[i].first;
1228 nodeInd2 = nodeIndsOfFace[i].second;
1231 // not to visit rejected faces
1232 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1233 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1234 visitedFaces.insert( facesNearLink[i] );
1236 else if ( facesNearLink.size() == 1 )
1238 otherFace = facesNearLink[0];
1239 nodeInd1 = nodeIndsOfFace.back().first;
1240 nodeInd2 = nodeIndsOfFace.back().second;
1242 if ( otherFace && otherFace != theFace)
1244 // link must be reverse in otherFace if orientation to otherFace
1245 // is same as that of theFace
1246 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1248 nbReori += Reorient( otherFace );
1250 startFaces.insert( otherFace );
1253 std::swap( link.first, link.second ); // reverse the link
1259 //================================================================================
1261 * \brief Reorient faces basing on orientation of adjacent volumes.
1262 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1263 * \param theVolumes - reference volumes.
1264 * \param theOutsideNormal - to orient faces to have their normal
1265 * pointing either \a outside or \a inside the adjacent volumes.
1266 * \return number of reoriented faces.
1268 //================================================================================
1270 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1271 TIDSortedElemSet & theVolumes,
1272 const bool theOutsideNormal)
1276 SMDS_ElemIteratorPtr faceIt;
1277 if ( theFaces.empty() )
1278 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1280 faceIt = SMESHUtils::elemSetIterator( theFaces );
1282 vector< const SMDS_MeshNode* > faceNodes;
1283 TIDSortedElemSet checkedVolumes;
1284 set< const SMDS_MeshNode* > faceNodesSet;
1285 SMDS_VolumeTool volumeTool;
1287 while ( faceIt->more() ) // loop on given faces
1289 const SMDS_MeshElement* face = faceIt->next();
1290 if ( face->GetType() != SMDSAbs_Face )
1293 const size_t nbCornersNodes = face->NbCornerNodes();
1294 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1296 checkedVolumes.clear();
1297 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1298 while ( vIt->more() )
1300 const SMDS_MeshElement* volume = vIt->next();
1302 if ( !checkedVolumes.insert( volume ).second )
1304 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1307 // is volume adjacent?
1308 bool allNodesCommon = true;
1309 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1310 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1311 if ( !allNodesCommon )
1314 // get nodes of a corresponding volume facet
1315 faceNodesSet.clear();
1316 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1317 volumeTool.Set( volume );
1318 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1319 if ( facetID < 0 ) continue;
1320 volumeTool.SetExternalNormal();
1321 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1323 // compare order of faceNodes and facetNodes
1324 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1326 for ( int i = 0; i < 2; ++i )
1328 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1329 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1330 if ( faceNodes[ iN ] == n )
1336 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1337 if ( isOutside != theOutsideNormal )
1338 nbReori += Reorient( face );
1340 } // loop on given faces
1345 //=======================================================================
1346 //function : getBadRate
1348 //=======================================================================
1350 static double getBadRate (const SMDS_MeshElement* theElem,
1351 SMESH::Controls::NumericalFunctorPtr& theCrit)
1353 SMESH::Controls::TSequenceOfXYZ P;
1354 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1356 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1357 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1360 //=======================================================================
1361 //function : QuadToTri
1362 //purpose : Cut quadrangles into triangles.
1363 // theCrit is used to select a diagonal to cut
1364 //=======================================================================
1366 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1367 SMESH::Controls::NumericalFunctorPtr theCrit)
1371 if ( !theCrit.get() )
1374 SMESHDS_Mesh * aMesh = GetMeshDS();
1375 Handle(Geom_Surface) surface;
1376 SMESH_MesherHelper helper( *GetMesh() );
1378 myLastCreatedElems.reserve( theElems.size() * 2 );
1380 TIDSortedElemSet::iterator itElem;
1381 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1383 const SMDS_MeshElement* elem = *itElem;
1384 if ( !elem || elem->GetType() != SMDSAbs_Face )
1386 if ( elem->NbCornerNodes() != 4 )
1389 // retrieve element nodes
1390 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1392 // compare two sets of possible triangles
1393 double aBadRate1, aBadRate2; // to what extent a set is bad
1394 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1395 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1396 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1398 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1399 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1400 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1402 const int aShapeId = FindShape( elem );
1403 const SMDS_MeshElement* newElem1 = 0;
1404 const SMDS_MeshElement* newElem2 = 0;
1406 if ( !elem->IsQuadratic() ) // split linear quadrangle
1408 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1409 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1410 if ( aBadRate1 <= aBadRate2 ) {
1411 // tr1 + tr2 is better
1412 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1413 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1416 // tr3 + tr4 is better
1417 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1418 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1421 else // split quadratic quadrangle
1423 helper.SetIsQuadratic( true );
1424 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1426 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1427 if ( aNodes.size() == 9 )
1429 helper.SetIsBiQuadratic( true );
1430 if ( aBadRate1 <= aBadRate2 )
1431 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1433 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1435 // create a new element
1436 if ( aBadRate1 <= aBadRate2 ) {
1437 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1438 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1441 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1442 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1446 // care of a new element
1448 myLastCreatedElems.push_back(newElem1);
1449 myLastCreatedElems.push_back(newElem2);
1450 AddToSameGroups( newElem1, elem, aMesh );
1451 AddToSameGroups( newElem2, elem, aMesh );
1453 // put a new triangle on the same shape
1455 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1456 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1458 aMesh->RemoveElement( elem );
1463 //=======================================================================
1465 * \brief Split each of given quadrangles into 4 triangles.
1466 * \param theElems - The faces to be split. If empty all faces are split.
1468 //=======================================================================
1470 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1473 myLastCreatedElems.reserve( theElems.size() * 4 );
1475 SMESH_MesherHelper helper( *GetMesh() );
1476 helper.SetElementsOnShape( true );
1478 SMDS_ElemIteratorPtr faceIt;
1479 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1480 else faceIt = SMESHUtils::elemSetIterator( theElems );
1483 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1485 vector< const SMDS_MeshNode* > nodes;
1486 SMESHDS_SubMesh* subMeshDS = 0;
1488 Handle(Geom_Surface) surface;
1489 TopLoc_Location loc;
1491 while ( faceIt->more() )
1493 const SMDS_MeshElement* quad = faceIt->next();
1494 if ( !quad || quad->NbCornerNodes() != 4 )
1497 // get a surface the quad is on
1499 if ( quad->getshapeId() < 1 )
1502 helper.SetSubShape( 0 );
1505 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1507 helper.SetSubShape( quad->getshapeId() );
1508 if ( !helper.GetSubShape().IsNull() &&
1509 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1511 F = TopoDS::Face( helper.GetSubShape() );
1512 surface = BRep_Tool::Surface( F, loc );
1513 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1517 helper.SetSubShape( 0 );
1522 // create a central node
1524 const SMDS_MeshNode* nCentral;
1525 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1527 if ( nodes.size() == 9 )
1529 nCentral = nodes.back();
1536 for ( ; iN < nodes.size(); ++iN )
1537 xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1539 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1540 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1542 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1543 xyz[0], xyz[1], xyz[2], xyz[3],
1544 xyz[4], xyz[5], xyz[6], xyz[7] );
1548 for ( ; iN < nodes.size(); ++iN )
1549 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1551 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1552 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1554 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1555 uv[0], uv[1], uv[2], uv[3],
1556 uv[4], uv[5], uv[6], uv[7] );
1558 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1562 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1563 uv[8].X(), uv[8].Y() );
1564 myLastCreatedNodes.push_back( nCentral );
1567 // create 4 triangles
1569 helper.SetIsQuadratic ( nodes.size() > 4 );
1570 helper.SetIsBiQuadratic( nodes.size() == 9 );
1571 if ( helper.GetIsQuadratic() )
1572 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1574 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1576 for ( int i = 0; i < 4; ++i )
1578 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1581 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1582 myLastCreatedElems.push_back( tria );
1587 //=======================================================================
1588 //function : BestSplit
1589 //purpose : Find better diagonal for cutting.
1590 //=======================================================================
1592 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1593 SMESH::Controls::NumericalFunctorPtr theCrit)
1600 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1603 if( theQuad->NbNodes()==4 ||
1604 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1606 // retrieve element nodes
1607 const SMDS_MeshNode* aNodes [4];
1608 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1610 //while (itN->more())
1612 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1614 // compare two sets of possible triangles
1615 double aBadRate1, aBadRate2; // to what extent a set is bad
1616 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1617 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1618 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1620 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1621 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1622 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1623 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1624 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1625 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1626 return 1; // diagonal 1-3
1628 return 2; // diagonal 2-4
1635 // Methods of splitting volumes into tetra
1637 const int theHexTo5_1[5*4+1] =
1639 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1641 const int theHexTo5_2[5*4+1] =
1643 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1645 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1647 const int theHexTo6_1[6*4+1] =
1649 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
1651 const int theHexTo6_2[6*4+1] =
1653 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
1655 const int theHexTo6_3[6*4+1] =
1657 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
1659 const int theHexTo6_4[6*4+1] =
1661 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
1663 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1665 const int thePyraTo2_1[2*4+1] =
1667 0, 1, 2, 4, 0, 2, 3, 4, -1
1669 const int thePyraTo2_2[2*4+1] =
1671 1, 2, 3, 4, 1, 3, 0, 4, -1
1673 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1675 const int thePentaTo3_1[3*4+1] =
1677 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1679 const int thePentaTo3_2[3*4+1] =
1681 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1683 const int thePentaTo3_3[3*4+1] =
1685 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1687 const int thePentaTo3_4[3*4+1] =
1689 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1691 const int thePentaTo3_5[3*4+1] =
1693 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1695 const int thePentaTo3_6[3*4+1] =
1697 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1699 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1700 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1702 // Methods of splitting hexahedron into prisms
1704 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1706 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
1708 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1710 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
1712 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1714 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
1717 const int theHexTo2Prisms_BT_1[6*2+1] =
1719 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1721 const int theHexTo2Prisms_BT_2[6*2+1] =
1723 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1725 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1727 const int theHexTo2Prisms_LR_1[6*2+1] =
1729 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1731 const int theHexTo2Prisms_LR_2[6*2+1] =
1733 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1735 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1737 const int theHexTo2Prisms_FB_1[6*2+1] =
1739 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1741 const int theHexTo2Prisms_FB_2[6*2+1] =
1743 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1745 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1748 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1751 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1752 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1753 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1754 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1760 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1761 bool _baryNode; //!< additional node is to be created at cell barycenter
1762 bool _ownConn; //!< to delete _connectivity in destructor
1763 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1765 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1766 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1767 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1768 bool hasFacet( const TTriangleFacet& facet ) const
1770 if ( _nbCorners == 4 )
1772 const int* tetConn = _connectivity;
1773 for ( ; tetConn[0] >= 0; tetConn += 4 )
1774 if (( facet.contains( tetConn[0] ) +
1775 facet.contains( tetConn[1] ) +
1776 facet.contains( tetConn[2] ) +
1777 facet.contains( tetConn[3] )) == 3 )
1780 else // prism, _nbCorners == 6
1782 const int* prismConn = _connectivity;
1783 for ( ; prismConn[0] >= 0; prismConn += 6 )
1785 if (( facet.contains( prismConn[0] ) &&
1786 facet.contains( prismConn[1] ) &&
1787 facet.contains( prismConn[2] ))
1789 ( facet.contains( prismConn[3] ) &&
1790 facet.contains( prismConn[4] ) &&
1791 facet.contains( prismConn[5] )))
1799 //=======================================================================
1801 * \brief return TSplitMethod for the given element to split into tetrahedra
1803 //=======================================================================
1805 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1807 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1809 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1810 // an edge and a face barycenter; tertaherdons are based on triangles and
1811 // a volume barycenter
1812 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1814 // Find out how adjacent volumes are split
1816 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1817 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1818 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1820 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1821 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1822 if ( nbNodes < 4 ) continue;
1824 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1825 const int* nInd = vol.GetFaceNodesIndices( iF );
1828 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1829 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1830 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1831 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1835 int iCom = 0; // common node of triangle faces to split into
1836 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1838 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1839 nInd[ iQ * ( (iCom+1)%nbNodes )],
1840 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1841 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1842 nInd[ iQ * ( (iCom+2)%nbNodes )],
1843 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1844 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1846 triaSplits.push_back( t012 );
1847 triaSplits.push_back( t023 );
1852 if ( !triaSplits.empty() )
1853 hasAdjacentSplits = true;
1856 // Among variants of split method select one compliant with adjacent volumes
1858 TSplitMethod method;
1859 if ( !vol.Element()->IsPoly() && !is24TetMode )
1861 int nbVariants = 2, nbTet = 0;
1862 const int** connVariants = 0;
1863 switch ( vol.Element()->GetEntityType() )
1865 case SMDSEntity_Hexa:
1866 case SMDSEntity_Quad_Hexa:
1867 case SMDSEntity_TriQuad_Hexa:
1868 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1869 connVariants = theHexTo5, nbTet = 5;
1871 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1873 case SMDSEntity_Pyramid:
1874 case SMDSEntity_Quad_Pyramid:
1875 connVariants = thePyraTo2; nbTet = 2;
1877 case SMDSEntity_Penta:
1878 case SMDSEntity_Quad_Penta:
1879 case SMDSEntity_BiQuad_Penta:
1880 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1885 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1887 // check method compliancy with adjacent tetras,
1888 // all found splits must be among facets of tetras described by this method
1889 method = TSplitMethod( nbTet, connVariants[variant] );
1890 if ( hasAdjacentSplits && method._nbSplits > 0 )
1892 bool facetCreated = true;
1893 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1895 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1896 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1897 facetCreated = method.hasFacet( *facet );
1899 if ( !facetCreated )
1900 method = TSplitMethod(0); // incompatible method
1904 if ( method._nbSplits < 1 )
1906 // No standard method is applicable, use a generic solution:
1907 // each facet of a volume is split into triangles and
1908 // each of triangles and a volume barycenter form a tetrahedron.
1910 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1912 int* connectivity = new int[ maxTetConnSize + 1 ];
1913 method._connectivity = connectivity;
1914 method._ownConn = true;
1915 method._baryNode = !isHex27; // to create central node or not
1918 int baryCenInd = vol.NbNodes() - int( isHex27 );
1919 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1921 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1922 const int* nInd = vol.GetFaceNodesIndices( iF );
1923 // find common node of triangle facets of tetra to create
1924 int iCommon = 0; // index in linear numeration
1925 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1926 if ( !triaSplits.empty() )
1929 const TTriangleFacet* facet = &triaSplits.front();
1930 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1931 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1932 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1935 else if ( nbNodes > 3 && !is24TetMode )
1937 // find the best method of splitting into triangles by aspect ratio
1938 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1939 map< double, int > badness2iCommon;
1940 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1941 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1942 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1945 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1947 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1948 nodes[ iQ*((iLast-1)%nbNodes)],
1949 nodes[ iQ*((iLast )%nbNodes)]);
1950 badness += getBadRate( &tria, aspectRatio );
1952 badness2iCommon.insert( make_pair( badness, iCommon ));
1954 // use iCommon with lowest badness
1955 iCommon = badness2iCommon.begin()->second;
1957 if ( iCommon >= nbNodes )
1958 iCommon = 0; // something wrong
1960 // fill connectivity of tetrahedra based on a current face
1961 int nbTet = nbNodes - 2;
1962 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1967 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1968 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1972 method._faceBaryNode[ iF ] = 0;
1973 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1976 for ( int i = 0; i < nbTet; ++i )
1978 int i1 = i, i2 = (i+1) % nbNodes;
1979 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1980 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1981 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1982 connectivity[ connSize++ ] = faceBaryCenInd;
1983 connectivity[ connSize++ ] = baryCenInd;
1988 for ( int i = 0; i < nbTet; ++i )
1990 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1991 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1992 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1993 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1994 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1995 connectivity[ connSize++ ] = baryCenInd;
1998 method._nbSplits += nbTet;
2000 } // loop on volume faces
2002 connectivity[ connSize++ ] = -1;
2004 } // end of generic solution
2008 //=======================================================================
2010 * \brief return TSplitMethod to split haxhedron into prisms
2012 //=======================================================================
2014 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2015 const int methodFlags,
2016 const int facetToSplit)
2018 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2020 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2022 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2024 static TSplitMethod to4methods[4]; // order BT, LR, FB
2025 if ( to4methods[iF]._nbSplits == 0 )
2029 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2030 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2031 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2034 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2035 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2036 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2039 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2040 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2041 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2043 default: return to4methods[3];
2045 to4methods[iF]._nbSplits = 4;
2046 to4methods[iF]._nbCorners = 6;
2048 return to4methods[iF];
2050 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2052 TSplitMethod method;
2054 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2056 const int nbVariants = 2, nbSplits = 2;
2057 const int** connVariants = 0;
2059 case 0: connVariants = theHexTo2Prisms_BT; break;
2060 case 1: connVariants = theHexTo2Prisms_LR; break;
2061 case 2: connVariants = theHexTo2Prisms_FB; break;
2062 default: return method;
2065 // look for prisms adjacent via facetToSplit and an opposite one
2066 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2068 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2069 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2070 if ( nbNodes != 4 ) return method;
2072 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2073 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2074 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2076 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2078 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2083 // there are adjacent prism
2084 for ( int variant = 0; variant < nbVariants; ++variant )
2086 // check method compliancy with adjacent prisms,
2087 // the found prism facets must be among facets of prisms described by current method
2088 method._nbSplits = nbSplits;
2089 method._nbCorners = 6;
2090 method._connectivity = connVariants[ variant ];
2091 if ( method.hasFacet( *t ))
2096 // No adjacent prisms. Select a variant with a best aspect ratio.
2098 double badness[2] = { 0., 0. };
2099 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2100 const SMDS_MeshNode** nodes = vol.GetNodes();
2101 for ( int variant = 0; variant < nbVariants; ++variant )
2102 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2104 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2105 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2107 method._connectivity = connVariants[ variant ];
2108 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2109 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2110 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2112 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2115 badness[ variant ] += getBadRate( &tria, aspectRatio );
2117 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2119 method._nbSplits = nbSplits;
2120 method._nbCorners = 6;
2121 method._connectivity = connVariants[ iBetter ];
2126 //================================================================================
2128 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2130 //================================================================================
2132 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2133 const SMDSAbs_GeometryType geom ) const
2135 // find the tetrahedron including the three nodes of facet
2136 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2137 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2138 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2139 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2140 while ( volIt1->more() )
2142 const SMDS_MeshElement* v = volIt1->next();
2143 if ( v->GetGeomType() != geom )
2145 const int lastCornerInd = v->NbCornerNodes() - 1;
2146 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2147 continue; // medium node not allowed
2148 const int ind2 = v->GetNodeIndex( n2 );
2149 if ( ind2 < 0 || lastCornerInd < ind2 )
2151 const int ind3 = v->GetNodeIndex( n3 );
2152 if ( ind3 < 0 || lastCornerInd < ind3 )
2159 //=======================================================================
2161 * \brief A key of a face of volume
2163 //=======================================================================
2165 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2167 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2169 TIDSortedNodeSet sortedNodes;
2170 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2171 int nbNodes = vol.NbFaceNodes( iF );
2172 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2173 for ( int i = 0; i < nbNodes; i += iQ )
2174 sortedNodes.insert( fNodes[i] );
2175 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2176 first.first = (*(n++))->GetID();
2177 first.second = (*(n++))->GetID();
2178 second.first = (*(n++))->GetID();
2179 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2184 //=======================================================================
2185 //function : SplitVolumes
2186 //purpose : Split volume elements into tetrahedra or prisms.
2187 // If facet ID < 0, element is split into tetrahedra,
2188 // else a hexahedron is split into prisms so that the given facet is
2189 // split into triangles
2190 //=======================================================================
2192 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2193 const int theMethodFlags)
2195 SMDS_VolumeTool volTool;
2196 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2197 fHelper.ToFixNodeParameters( true );
2199 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2200 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2202 SMESH_SequenceOfElemPtr newNodes, newElems;
2204 // map face of volume to it's baricenrtic node
2205 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2207 vector<const SMDS_MeshElement* > splitVols;
2209 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2210 for ( ; elem2facet != theElems.end(); ++elem2facet )
2212 const SMDS_MeshElement* elem = elem2facet->first;
2213 const int facetToSplit = elem2facet->second;
2214 if ( elem->GetType() != SMDSAbs_Volume )
2216 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2217 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2220 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2222 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2223 getTetraSplitMethod( volTool, theMethodFlags ) :
2224 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2225 if ( splitMethod._nbSplits < 1 ) continue;
2227 // find submesh to add new tetras to
2228 if ( !subMesh || !subMesh->Contains( elem ))
2230 int shapeID = FindShape( elem );
2231 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2232 subMesh = GetMeshDS()->MeshElements( shapeID );
2235 if ( elem->IsQuadratic() )
2238 // add quadratic links to the helper
2239 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2241 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2242 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2243 for ( int iN = 0; iN < nbN; iN += iQ )
2244 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2246 helper.SetIsQuadratic( true );
2251 helper.SetIsQuadratic( false );
2253 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2254 volTool.GetNodes() + elem->NbNodes() );
2255 helper.SetElementsOnShape( true );
2256 if ( splitMethod._baryNode )
2258 // make a node at barycenter
2259 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2260 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2261 nodes.push_back( gcNode );
2262 newNodes.push_back( gcNode );
2264 if ( !splitMethod._faceBaryNode.empty() )
2266 // make or find baricentric nodes of faces
2267 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2268 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2270 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2271 volFace2BaryNode.insert
2272 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2275 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2276 newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2278 nodes.push_back( iF_n->second = f_n->second );
2283 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2284 const int* volConn = splitMethod._connectivity;
2285 if ( splitMethod._nbCorners == 4 ) // tetra
2286 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2287 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2288 nodes[ volConn[1] ],
2289 nodes[ volConn[2] ],
2290 nodes[ volConn[3] ]));
2292 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2293 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2294 nodes[ volConn[1] ],
2295 nodes[ volConn[2] ],
2296 nodes[ volConn[3] ],
2297 nodes[ volConn[4] ],
2298 nodes[ volConn[5] ]));
2300 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2302 // Split faces on sides of the split volume
2304 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2305 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2307 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2308 if ( nbNodes < 4 ) continue;
2310 // find an existing face
2311 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2312 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2313 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2314 /*noMedium=*/false))
2317 helper.SetElementsOnShape( false );
2318 vector< const SMDS_MeshElement* > triangles;
2320 // find submesh to add new triangles in
2321 if ( !fSubMesh || !fSubMesh->Contains( face ))
2323 int shapeID = FindShape( face );
2324 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2326 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2327 if ( iF_n != splitMethod._faceBaryNode.end() )
2329 const SMDS_MeshNode *baryNode = iF_n->second;
2330 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2332 const SMDS_MeshNode* n1 = fNodes[iN];
2333 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2334 const SMDS_MeshNode *n3 = baryNode;
2335 if ( !volTool.IsFaceExternal( iF ))
2337 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2339 if ( fSubMesh ) // update position of the bary node on geometry
2342 subMesh->RemoveNode( baryNode );
2343 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2344 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2345 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2347 fHelper.SetSubShape( s );
2348 gp_XY uv( 1e100, 1e100 );
2350 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2351 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2354 // node is too far from the surface
2355 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2356 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2357 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2364 // among possible triangles create ones described by split method
2365 const int* nInd = volTool.GetFaceNodesIndices( iF );
2366 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2367 int iCom = 0; // common node of triangle faces to split into
2368 list< TTriangleFacet > facets;
2369 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2371 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2372 nInd[ iQ * ( (iCom+1)%nbNodes )],
2373 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2374 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2375 nInd[ iQ * ( (iCom+2)%nbNodes )],
2376 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2377 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2379 facets.push_back( t012 );
2380 facets.push_back( t023 );
2381 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2382 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2383 nInd[ iQ * ((iLast-1)%nbNodes )],
2384 nInd[ iQ * ((iLast )%nbNodes )]));
2388 list< TTriangleFacet >::iterator facet = facets.begin();
2389 if ( facet == facets.end() )
2391 for ( ; facet != facets.end(); ++facet )
2393 if ( !volTool.IsFaceExternal( iF ))
2394 swap( facet->_n2, facet->_n3 );
2395 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2396 volNodes[ facet->_n2 ],
2397 volNodes[ facet->_n3 ]));
2400 for ( size_t i = 0; i < triangles.size(); ++i )
2402 if ( !triangles[ i ]) continue;
2404 fSubMesh->AddElement( triangles[ i ]);
2405 newElems.push_back( triangles[ i ]);
2407 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2408 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2410 } // while a face based on facet nodes exists
2411 } // loop on volume faces to split them into triangles
2413 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2415 if ( geomType == SMDSEntity_TriQuad_Hexa )
2417 // remove medium nodes that could become free
2418 for ( int i = 20; i < volTool.NbNodes(); ++i )
2419 if ( volNodes[i]->NbInverseElements() == 0 )
2420 GetMeshDS()->RemoveNode( volNodes[i] );
2422 } // loop on volumes to split
2424 myLastCreatedNodes = newNodes;
2425 myLastCreatedElems = newElems;
2428 //=======================================================================
2429 //function : GetHexaFacetsToSplit
2430 //purpose : For hexahedra that will be split into prisms, finds facets to
2431 // split into triangles. Only hexahedra adjacent to the one closest
2432 // to theFacetNormal.Location() are returned.
2433 //param [in,out] theHexas - the hexahedra
2434 //param [in] theFacetNormal - facet normal
2435 //param [out] theFacets - the hexahedra and found facet IDs
2436 //=======================================================================
2438 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2439 const gp_Ax1& theFacetNormal,
2440 TFacetOfElem & theFacets)
2442 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2444 // Find a hexa closest to the location of theFacetNormal
2446 const SMDS_MeshElement* startHex;
2448 // get SMDS_ElemIteratorPtr on theHexas
2449 typedef const SMDS_MeshElement* TValue;
2450 typedef TIDSortedElemSet::iterator TSetIterator;
2451 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2452 typedef SMDS_MeshElement::GeomFilter TFilter;
2453 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2454 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2455 ( new TElemSetIter( theHexas.begin(),
2457 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2459 SMESH_ElementSearcher* searcher =
2460 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2462 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2467 throw SALOME_Exception( THIS_METHOD "startHex not found");
2470 // Select a facet of startHex by theFacetNormal
2472 SMDS_VolumeTool vTool( startHex );
2473 double norm[3], dot, maxDot = 0;
2475 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2476 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2478 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2486 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2488 // Fill theFacets starting from facetID of startHex
2490 // facets used for searching of volumes adjacent to already treated ones
2491 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2492 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2493 TFacetMap facetsToCheck;
2495 set<const SMDS_MeshNode*> facetNodes;
2496 const SMDS_MeshElement* curHex;
2498 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2502 // move in two directions from startHex via facetID
2503 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2506 int curFacet = facetID;
2507 if ( is2nd ) // do not treat startHex twice
2509 vTool.Set( curHex );
2510 if ( vTool.IsFreeFace( curFacet, &curHex ))
2516 vTool.GetFaceNodes( curFacet, facetNodes );
2517 vTool.Set( curHex );
2518 curFacet = vTool.GetFaceIndex( facetNodes );
2523 // store a facet to split
2524 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2526 theFacets.insert( make_pair( curHex, -1 ));
2529 if ( !allHex && !theHexas.count( curHex ))
2532 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2533 theFacets.insert( make_pair( curHex, curFacet ));
2534 if ( !facetIt2isNew.second )
2537 // remember not-to-split facets in facetsToCheck
2538 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2539 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2541 if ( iF == curFacet && iF == oppFacet )
2543 TVolumeFaceKey facetKey ( vTool, iF );
2544 TElemFacets elemFacet( facetIt2isNew.first, iF );
2545 pair< TFacetMap::iterator, bool > it2isnew =
2546 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2547 if ( !it2isnew.second )
2548 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2550 // pass to a volume adjacent via oppFacet
2551 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2557 // get a new curFacet
2558 vTool.GetFaceNodes( oppFacet, facetNodes );
2559 vTool.Set( curHex );
2560 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2563 } // move in two directions from startHex via facetID
2565 // Find a new startHex by facetsToCheck
2569 TFacetMap::iterator fIt = facetsToCheck.begin();
2570 while ( !startHex && fIt != facetsToCheck.end() )
2572 const TElemFacets& elemFacets = fIt->second;
2573 const SMDS_MeshElement* hex = elemFacets.first->first;
2574 int splitFacet = elemFacets.first->second;
2575 int lateralFacet = elemFacets.second;
2576 facetsToCheck.erase( fIt );
2577 fIt = facetsToCheck.begin();
2580 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2581 curHex->GetGeomType() != SMDSGeom_HEXA )
2583 if ( !allHex && !theHexas.count( curHex ))
2588 // find a facet of startHex to split
2590 set<const SMDS_MeshNode*> lateralNodes;
2591 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2592 vTool.GetFaceNodes( splitFacet, facetNodes );
2593 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2594 vTool.Set( startHex );
2595 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2597 // look for a facet of startHex having common nodes with facetNodes
2598 // but not lateralFacet
2599 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2601 if ( iF == lateralFacet )
2603 int nbCommonNodes = 0;
2604 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2605 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2606 nbCommonNodes += facetNodes.count( nn[ iN ]);
2608 if ( nbCommonNodes >= 2 )
2615 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2617 } // while ( startHex )
2624 //================================================================================
2626 * \brief Selects nodes of several elements according to a given interlace
2627 * \param [in] srcNodes - nodes to select from
2628 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2629 * \param [in] interlace - indices of nodes for all elements
2630 * \param [in] nbElems - nb of elements
2631 * \param [in] nbNodes - nb of nodes in each element
2632 * \param [in] mesh - the mesh
2633 * \param [out] elemQueue - a list to push elements found by the selected nodes
2634 * \param [in] type - type of elements to look for
2636 //================================================================================
2638 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2639 vector< const SMDS_MeshNode* >* tgtNodesVec,
2640 const int* interlace,
2643 SMESHDS_Mesh* mesh = 0,
2644 list< const SMDS_MeshElement* >* elemQueue=0,
2645 SMDSAbs_ElementType type=SMDSAbs_All)
2647 for ( int iE = 0; iE < nbElems; ++iE )
2649 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2650 const int* select = & interlace[iE*nbNodes];
2651 elemNodes.resize( nbNodes );
2652 for ( int iN = 0; iN < nbNodes; ++iN )
2653 elemNodes[iN] = srcNodes[ select[ iN ]];
2655 const SMDS_MeshElement* e;
2657 for ( int iE = 0; iE < nbElems; ++iE )
2658 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2659 elemQueue->push_back( e );
2663 //=======================================================================
2665 * Split bi-quadratic elements into linear ones without creation of additional nodes
2666 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2667 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2668 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2669 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2670 * will be split in order to keep the mesh conformal.
2671 * \param elems - elements to split
2673 //=======================================================================
2675 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2677 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2678 vector<const SMDS_MeshElement* > splitElems;
2679 list< const SMDS_MeshElement* > elemQueue;
2680 list< const SMDS_MeshElement* >::iterator elemIt;
2682 SMESHDS_Mesh * mesh = GetMeshDS();
2683 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2684 int nbElems, nbNodes;
2686 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2687 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2690 elemQueue.push_back( *elemSetIt );
2691 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2693 const SMDS_MeshElement* elem = *elemIt;
2694 switch( elem->GetEntityType() )
2696 case SMDSEntity_TriQuad_Hexa: // HEX27
2698 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2699 nbElems = nbNodes = 8;
2700 elemType = & hexaType;
2702 // get nodes for new elements
2703 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2704 { 1,9,20,8, 17,22,26,21 },
2705 { 2,10,20,9, 18,23,26,22 },
2706 { 3,11,20,10, 19,24,26,23 },
2707 { 16,21,26,24, 4,12,25,15 },
2708 { 17,22,26,21, 5,13,25,12 },
2709 { 18,23,26,22, 6,14,25,13 },
2710 { 19,24,26,23, 7,15,25,14 }};
2711 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2713 // add boundary faces to elemQueue
2714 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2715 { 4,5,6,7, 12,13,14,15, 25 },
2716 { 0,1,5,4, 8,17,12,16, 21 },
2717 { 1,2,6,5, 9,18,13,17, 22 },
2718 { 2,3,7,6, 10,19,14,18, 23 },
2719 { 3,0,4,7, 11,16,15,19, 24 }};
2720 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2722 // add boundary segments to elemQueue
2723 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2724 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2725 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2726 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2729 case SMDSEntity_BiQuad_Triangle: // TRIA7
2731 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2734 elemType = & quadType;
2736 // get nodes for new elements
2737 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2738 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2740 // add boundary segments to elemQueue
2741 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2742 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2745 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2747 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2750 elemType = & quadType;
2752 // get nodes for new elements
2753 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2754 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2756 // add boundary segments to elemQueue
2757 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2758 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2761 case SMDSEntity_Quad_Edge:
2763 if ( elemIt == elemQueue.begin() )
2764 continue; // an elem is in theElems
2765 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2768 elemType = & segType;
2770 // get nodes for new elements
2771 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2772 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2776 } // switch( elem->GetEntityType() )
2778 // Create new elements
2780 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2784 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2785 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2786 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2787 //elemType->SetID( -1 );
2789 for ( int iE = 0; iE < nbElems; ++iE )
2790 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2793 ReplaceElemInGroups( elem, splitElems, mesh );
2796 for ( size_t i = 0; i < splitElems.size(); ++i )
2797 subMesh->AddElement( splitElems[i] );
2802 //=======================================================================
2803 //function : AddToSameGroups
2804 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2805 //=======================================================================
2807 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2808 const SMDS_MeshElement* elemInGroups,
2809 SMESHDS_Mesh * aMesh)
2811 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2812 if (!groups.empty()) {
2813 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2814 for ( ; grIt != groups.end(); grIt++ ) {
2815 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2816 if ( group && group->Contains( elemInGroups ))
2817 group->SMDSGroup().Add( elemToAdd );
2823 //=======================================================================
2824 //function : RemoveElemFromGroups
2825 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2826 //=======================================================================
2827 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2828 SMESHDS_Mesh * aMesh)
2830 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2831 if (!groups.empty())
2833 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2834 for (; GrIt != groups.end(); GrIt++)
2836 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2837 if (!grp || grp->IsEmpty()) continue;
2838 grp->SMDSGroup().Remove(removeelem);
2843 //================================================================================
2845 * \brief Replace elemToRm by elemToAdd in the all groups
2847 //================================================================================
2849 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2850 const SMDS_MeshElement* elemToAdd,
2851 SMESHDS_Mesh * aMesh)
2853 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2854 if (!groups.empty()) {
2855 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2856 for ( ; grIt != groups.end(); grIt++ ) {
2857 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2858 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2859 group->SMDSGroup().Add( elemToAdd );
2864 //================================================================================
2866 * \brief Replace elemToRm by elemToAdd in the all groups
2868 //================================================================================
2870 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2871 const vector<const SMDS_MeshElement*>& elemToAdd,
2872 SMESHDS_Mesh * aMesh)
2874 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2875 if (!groups.empty())
2877 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2878 for ( ; grIt != groups.end(); grIt++ ) {
2879 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2880 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2881 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2882 group->SMDSGroup().Add( elemToAdd[ i ] );
2887 //=======================================================================
2888 //function : QuadToTri
2889 //purpose : Cut quadrangles into triangles.
2890 // theCrit is used to select a diagonal to cut
2891 //=======================================================================
2893 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2894 const bool the13Diag)
2897 myLastCreatedElems.reserve( theElems.size() * 2 );
2899 SMESHDS_Mesh * aMesh = GetMeshDS();
2900 Handle(Geom_Surface) surface;
2901 SMESH_MesherHelper helper( *GetMesh() );
2903 TIDSortedElemSet::iterator itElem;
2904 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2906 const SMDS_MeshElement* elem = *itElem;
2907 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2910 if ( elem->NbNodes() == 4 ) {
2911 // retrieve element nodes
2912 const SMDS_MeshNode* aNodes [4];
2913 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2915 while ( itN->more() )
2916 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2918 int aShapeId = FindShape( elem );
2919 const SMDS_MeshElement* newElem1 = 0;
2920 const SMDS_MeshElement* newElem2 = 0;
2922 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2923 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2926 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2927 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2929 myLastCreatedElems.push_back(newElem1);
2930 myLastCreatedElems.push_back(newElem2);
2931 // put a new triangle on the same shape and add to the same groups
2934 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2935 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2937 AddToSameGroups( newElem1, elem, aMesh );
2938 AddToSameGroups( newElem2, elem, aMesh );
2939 aMesh->RemoveElement( elem );
2942 // Quadratic quadrangle
2944 else if ( elem->NbNodes() >= 8 )
2946 // get surface elem is on
2947 int aShapeId = FindShape( elem );
2948 if ( aShapeId != helper.GetSubShapeID() ) {
2952 shape = aMesh->IndexToShape( aShapeId );
2953 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2954 TopoDS_Face face = TopoDS::Face( shape );
2955 surface = BRep_Tool::Surface( face );
2956 if ( !surface.IsNull() )
2957 helper.SetSubShape( shape );
2961 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
2962 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2963 for ( int i = 0; itN->more(); ++i )
2964 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2966 const SMDS_MeshNode* centrNode = aNodes[8];
2967 if ( centrNode == 0 )
2969 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2970 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
2972 myLastCreatedNodes.push_back(centrNode);
2975 // create a new element
2976 const SMDS_MeshElement* newElem1 = 0;
2977 const SMDS_MeshElement* newElem2 = 0;
2979 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2980 aNodes[6], aNodes[7], centrNode );
2981 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2982 centrNode, aNodes[4], aNodes[5] );
2985 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2986 aNodes[7], aNodes[4], centrNode );
2987 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2988 centrNode, aNodes[5], aNodes[6] );
2990 myLastCreatedElems.push_back(newElem1);
2991 myLastCreatedElems.push_back(newElem2);
2992 // put a new triangle on the same shape and add to the same groups
2995 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2996 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2998 AddToSameGroups( newElem1, elem, aMesh );
2999 AddToSameGroups( newElem2, elem, aMesh );
3000 aMesh->RemoveElement( elem );
3007 //=======================================================================
3008 //function : getAngle
3010 //=======================================================================
3012 double getAngle(const SMDS_MeshElement * tr1,
3013 const SMDS_MeshElement * tr2,
3014 const SMDS_MeshNode * n1,
3015 const SMDS_MeshNode * n2)
3017 double angle = 2. * M_PI; // bad angle
3020 SMESH::Controls::TSequenceOfXYZ P1, P2;
3021 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3022 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3025 if(!tr1->IsQuadratic())
3026 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3028 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3029 if ( N1.SquareMagnitude() <= gp::Resolution() )
3031 if(!tr2->IsQuadratic())
3032 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3034 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3035 if ( N2.SquareMagnitude() <= gp::Resolution() )
3038 // find the first diagonal node n1 in the triangles:
3039 // take in account a diagonal link orientation
3040 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3041 for ( int t = 0; t < 2; t++ ) {
3042 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3043 int i = 0, iDiag = -1;
3044 while ( it->more()) {
3045 const SMDS_MeshElement *n = it->next();
3046 if ( n == n1 || n == n2 ) {
3050 if ( i - iDiag == 1 )
3051 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3060 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3063 angle = N1.Angle( N2 );
3068 // =================================================
3069 // class generating a unique ID for a pair of nodes
3070 // and able to return nodes by that ID
3071 // =================================================
3075 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3076 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3079 long GetLinkID (const SMDS_MeshNode * n1,
3080 const SMDS_MeshNode * n2) const
3082 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3085 bool GetNodes (const long theLinkID,
3086 const SMDS_MeshNode* & theNode1,
3087 const SMDS_MeshNode* & theNode2) const
3089 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3090 if ( !theNode1 ) return false;
3091 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3092 if ( !theNode2 ) return false;
3098 const SMESHDS_Mesh* myMesh;
3103 //=======================================================================
3104 //function : TriToQuad
3105 //purpose : Fuse neighbour triangles into quadrangles.
3106 // theCrit is used to select a neighbour to fuse with.
3107 // theMaxAngle is a max angle between element normals at which
3108 // fusion is still performed.
3109 //=======================================================================
3111 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3112 SMESH::Controls::NumericalFunctorPtr theCrit,
3113 const double theMaxAngle)
3116 myLastCreatedElems.reserve( theElems.size() / 2 );
3118 if ( !theCrit.get() )
3121 SMESHDS_Mesh * aMesh = GetMeshDS();
3123 // Prepare data for algo: build
3124 // 1. map of elements with their linkIDs
3125 // 2. map of linkIDs with their elements
3127 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3128 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3129 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3130 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3132 TIDSortedElemSet::iterator itElem;
3133 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3135 const SMDS_MeshElement* elem = *itElem;
3136 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3137 bool IsTria = ( elem->NbCornerNodes()==3 );
3138 if (!IsTria) continue;
3140 // retrieve element nodes
3141 const SMDS_MeshNode* aNodes [4];
3142 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3145 aNodes[ i++ ] = itN->next();
3146 aNodes[ 3 ] = aNodes[ 0 ];
3149 for ( i = 0; i < 3; i++ ) {
3150 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3151 // check if elements sharing a link can be fused
3152 itLE = mapLi_listEl.find( link );
3153 if ( itLE != mapLi_listEl.end() ) {
3154 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3156 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3157 //if ( FindShape( elem ) != FindShape( elem2 ))
3158 // continue; // do not fuse triangles laying on different shapes
3159 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3160 continue; // avoid making badly shaped quads
3161 (*itLE).second.push_back( elem );
3164 mapLi_listEl[ link ].push_back( elem );
3166 mapEl_setLi [ elem ].insert( link );
3169 // Clean the maps from the links shared by a sole element, ie
3170 // links to which only one element is bound in mapLi_listEl
3172 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3173 int nbElems = (*itLE).second.size();
3174 if ( nbElems < 2 ) {
3175 const SMDS_MeshElement* elem = (*itLE).second.front();
3176 SMESH_TLink link = (*itLE).first;
3177 mapEl_setLi[ elem ].erase( link );
3178 if ( mapEl_setLi[ elem ].empty() )
3179 mapEl_setLi.erase( elem );
3183 // Algo: fuse triangles into quadrangles
3185 while ( ! mapEl_setLi.empty() ) {
3186 // Look for the start element:
3187 // the element having the least nb of shared links
3188 const SMDS_MeshElement* startElem = 0;
3190 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3191 int nbLinks = (*itEL).second.size();
3192 if ( nbLinks < minNbLinks ) {
3193 startElem = (*itEL).first;
3194 minNbLinks = nbLinks;
3195 if ( minNbLinks == 1 )
3200 // search elements to fuse starting from startElem or links of elements
3201 // fused earlyer - startLinks
3202 list< SMESH_TLink > startLinks;
3203 while ( startElem || !startLinks.empty() ) {
3204 while ( !startElem && !startLinks.empty() ) {
3205 // Get an element to start, by a link
3206 SMESH_TLink linkId = startLinks.front();
3207 startLinks.pop_front();
3208 itLE = mapLi_listEl.find( linkId );
3209 if ( itLE != mapLi_listEl.end() ) {
3210 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3211 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3212 for ( ; itE != listElem.end() ; itE++ )
3213 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3215 mapLi_listEl.erase( itLE );
3220 // Get candidates to be fused
3221 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3222 const SMESH_TLink *link12 = 0, *link13 = 0;
3224 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3225 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3226 ASSERT( !setLi.empty() );
3227 set< SMESH_TLink >::iterator itLi;
3228 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3230 const SMESH_TLink & link = (*itLi);
3231 itLE = mapLi_listEl.find( link );
3232 if ( itLE == mapLi_listEl.end() )
3235 const SMDS_MeshElement* elem = (*itLE).second.front();
3237 elem = (*itLE).second.back();
3238 mapLi_listEl.erase( itLE );
3239 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3250 // add other links of elem to list of links to re-start from
3251 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3252 set< SMESH_TLink >::iterator it;
3253 for ( it = links.begin(); it != links.end(); it++ ) {
3254 const SMESH_TLink& link2 = (*it);
3255 if ( link2 != link )
3256 startLinks.push_back( link2 );
3260 // Get nodes of possible quadrangles
3261 const SMDS_MeshNode *n12 [4], *n13 [4];
3262 bool Ok12 = false, Ok13 = false;
3263 const SMDS_MeshNode *linkNode1, *linkNode2;
3265 linkNode1 = link12->first;
3266 linkNode2 = link12->second;
3267 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3271 linkNode1 = link13->first;
3272 linkNode2 = link13->second;
3273 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3277 // Choose a pair to fuse
3278 if ( Ok12 && Ok13 ) {
3279 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3280 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3281 double aBadRate12 = getBadRate( &quad12, theCrit );
3282 double aBadRate13 = getBadRate( &quad13, theCrit );
3283 if ( aBadRate13 < aBadRate12 )
3290 // and remove fused elems and remove links from the maps
3291 mapEl_setLi.erase( tr1 );
3294 mapEl_setLi.erase( tr2 );
3295 mapLi_listEl.erase( *link12 );
3296 if ( tr1->NbNodes() == 3 )
3298 const SMDS_MeshElement* newElem = 0;
3299 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3300 myLastCreatedElems.push_back(newElem);
3301 AddToSameGroups( newElem, tr1, aMesh );
3302 int aShapeId = tr1->getshapeId();
3304 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3305 aMesh->RemoveElement( tr1 );
3306 aMesh->RemoveElement( tr2 );
3309 vector< const SMDS_MeshNode* > N1;
3310 vector< const SMDS_MeshNode* > N2;
3311 getNodesFromTwoTria(tr1,tr2,N1,N2);
3312 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3313 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3314 // i.e. first nodes from both arrays form a new diagonal
3315 const SMDS_MeshNode* aNodes[8];
3324 const SMDS_MeshElement* newElem = 0;
3325 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3326 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3327 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3329 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3330 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3331 myLastCreatedElems.push_back(newElem);
3332 AddToSameGroups( newElem, tr1, aMesh );
3333 int aShapeId = tr1->getshapeId();
3335 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3336 aMesh->RemoveElement( tr1 );
3337 aMesh->RemoveElement( tr2 );
3338 // remove middle node (9)
3339 if ( N1[4]->NbInverseElements() == 0 )
3340 aMesh->RemoveNode( N1[4] );
3341 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3342 aMesh->RemoveNode( N1[6] );
3343 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3344 aMesh->RemoveNode( N2[6] );
3349 mapEl_setLi.erase( tr3 );
3350 mapLi_listEl.erase( *link13 );
3351 if ( tr1->NbNodes() == 3 ) {
3352 const SMDS_MeshElement* newElem = 0;
3353 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3354 myLastCreatedElems.push_back(newElem);
3355 AddToSameGroups( newElem, tr1, aMesh );
3356 int aShapeId = tr1->getshapeId();
3358 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3359 aMesh->RemoveElement( tr1 );
3360 aMesh->RemoveElement( tr3 );
3363 vector< const SMDS_MeshNode* > N1;
3364 vector< const SMDS_MeshNode* > N2;
3365 getNodesFromTwoTria(tr1,tr3,N1,N2);
3366 // now we receive following N1 and N2 (using numeration as above image)
3367 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3368 // i.e. first nodes from both arrays form a new diagonal
3369 const SMDS_MeshNode* aNodes[8];
3378 const SMDS_MeshElement* newElem = 0;
3379 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3380 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3381 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3383 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3384 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3385 myLastCreatedElems.push_back(newElem);
3386 AddToSameGroups( newElem, tr1, aMesh );
3387 int aShapeId = tr1->getshapeId();
3389 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3390 aMesh->RemoveElement( tr1 );
3391 aMesh->RemoveElement( tr3 );
3392 // remove middle node (9)
3393 if ( N1[4]->NbInverseElements() == 0 )
3394 aMesh->RemoveNode( N1[4] );
3395 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3396 aMesh->RemoveNode( N1[6] );
3397 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3398 aMesh->RemoveNode( N2[6] );
3402 // Next element to fuse: the rejected one
3404 startElem = Ok12 ? tr3 : tr2;
3406 } // if ( startElem )
3407 } // while ( startElem || !startLinks.empty() )
3408 } // while ( ! mapEl_setLi.empty() )
3413 //================================================================================
3415 * \brief Return nodes linked to the given one
3416 * \param theNode - the node
3417 * \param linkedNodes - the found nodes
3418 * \param type - the type of elements to check
3420 * Medium nodes are ignored
3422 //================================================================================
3424 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3425 TIDSortedElemSet & linkedNodes,
3426 SMDSAbs_ElementType type )
3428 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3429 while ( elemIt->more() )
3431 const SMDS_MeshElement* elem = elemIt->next();
3432 if(elem->GetType() == SMDSAbs_0DElement)
3435 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3436 if ( elem->GetType() == SMDSAbs_Volume )
3438 SMDS_VolumeTool vol( elem );
3439 while ( nodeIt->more() ) {
3440 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3441 if ( theNode != n && vol.IsLinked( theNode, n ))
3442 linkedNodes.insert( n );
3447 for ( int i = 0; nodeIt->more(); ++i ) {
3448 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3449 if ( n == theNode ) {
3450 int iBefore = i - 1;
3452 if ( elem->IsQuadratic() ) {
3453 int nb = elem->NbNodes() / 2;
3454 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3455 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3457 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3458 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3465 //=======================================================================
3466 //function : laplacianSmooth
3467 //purpose : pulls theNode toward the center of surrounding nodes directly
3468 // connected to that node along an element edge
3469 //=======================================================================
3471 void laplacianSmooth(const SMDS_MeshNode* theNode,
3472 const Handle(Geom_Surface)& theSurface,
3473 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3475 // find surrounding nodes
3477 TIDSortedElemSet nodeSet;
3478 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3480 // compute new coodrs
3482 double coord[] = { 0., 0., 0. };
3483 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3484 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3485 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3486 if ( theSurface.IsNull() ) { // smooth in 3D
3487 coord[0] += node->X();
3488 coord[1] += node->Y();
3489 coord[2] += node->Z();
3491 else { // smooth in 2D
3492 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3493 gp_XY* uv = theUVMap[ node ];
3494 coord[0] += uv->X();
3495 coord[1] += uv->Y();
3498 int nbNodes = nodeSet.size();
3501 coord[0] /= nbNodes;
3502 coord[1] /= nbNodes;
3504 if ( !theSurface.IsNull() ) {
3505 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3506 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3507 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3513 coord[2] /= nbNodes;
3517 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3520 //=======================================================================
3521 //function : centroidalSmooth
3522 //purpose : pulls theNode toward the element-area-weighted centroid of the
3523 // surrounding elements
3524 //=======================================================================
3526 void centroidalSmooth(const SMDS_MeshNode* theNode,
3527 const Handle(Geom_Surface)& theSurface,
3528 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3530 gp_XYZ aNewXYZ(0.,0.,0.);
3531 SMESH::Controls::Area anAreaFunc;
3532 double totalArea = 0.;
3537 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3538 while ( elemIt->more() )
3540 const SMDS_MeshElement* elem = elemIt->next();
3543 gp_XYZ elemCenter(0.,0.,0.);
3544 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3545 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3546 int nn = elem->NbNodes();
3547 if(elem->IsQuadratic()) nn = nn/2;
3549 //while ( itN->more() ) {
3551 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3553 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3554 aNodePoints.push_back( aP );
3555 if ( !theSurface.IsNull() ) { // smooth in 2D
3556 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3557 gp_XY* uv = theUVMap[ aNode ];
3558 aP.SetCoord( uv->X(), uv->Y(), 0. );
3562 double elemArea = anAreaFunc.GetValue( aNodePoints );
3563 totalArea += elemArea;
3565 aNewXYZ += elemCenter * elemArea;
3567 aNewXYZ /= totalArea;
3568 if ( !theSurface.IsNull() ) {
3569 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3570 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3575 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3578 //=======================================================================
3579 //function : getClosestUV
3580 //purpose : return UV of closest projection
3581 //=======================================================================
3583 static bool getClosestUV (Extrema_GenExtPS& projector,
3584 const gp_Pnt& point,
3587 projector.Perform( point );
3588 if ( projector.IsDone() ) {
3589 double u, v, minVal = DBL_MAX;
3590 for ( int i = projector.NbExt(); i > 0; i-- )
3591 if ( projector.SquareDistance( i ) < minVal ) {
3592 minVal = projector.SquareDistance( i );
3593 projector.Point( i ).Parameter( u, v );
3595 result.SetCoord( u, v );
3601 //=======================================================================
3603 //purpose : Smooth theElements during theNbIterations or until a worst
3604 // element has aspect ratio <= theTgtAspectRatio.
3605 // Aspect Ratio varies in range [1.0, inf].
3606 // If theElements is empty, the whole mesh is smoothed.
3607 // theFixedNodes contains additionally fixed nodes. Nodes built
3608 // on edges and boundary nodes are always fixed.
3609 //=======================================================================
3611 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3612 set<const SMDS_MeshNode*> & theFixedNodes,
3613 const SmoothMethod theSmoothMethod,
3614 const int theNbIterations,
3615 double theTgtAspectRatio,
3620 if ( theTgtAspectRatio < 1.0 )
3621 theTgtAspectRatio = 1.0;
3623 const double disttol = 1.e-16;
3625 SMESH::Controls::AspectRatio aQualityFunc;
3627 SMESHDS_Mesh* aMesh = GetMeshDS();
3629 if ( theElems.empty() ) {
3630 // add all faces to theElems
3631 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3632 while ( fIt->more() ) {
3633 const SMDS_MeshElement* face = fIt->next();
3634 theElems.insert( theElems.end(), face );
3637 // get all face ids theElems are on
3638 set< int > faceIdSet;
3639 TIDSortedElemSet::iterator itElem;
3641 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3642 int fId = FindShape( *itElem );
3643 // check that corresponding submesh exists and a shape is face
3645 faceIdSet.find( fId ) == faceIdSet.end() &&
3646 aMesh->MeshElements( fId )) {
3647 TopoDS_Shape F = aMesh->IndexToShape( fId );
3648 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3649 faceIdSet.insert( fId );
3652 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3654 // ===============================================
3655 // smooth elements on each TopoDS_Face separately
3656 // ===============================================
3658 SMESH_MesherHelper helper( *GetMesh() );
3660 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3661 for ( ; fId != faceIdSet.rend(); ++fId )
3663 // get face surface and submesh
3664 Handle(Geom_Surface) surface;
3665 SMESHDS_SubMesh* faceSubMesh = 0;
3668 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3669 bool isUPeriodic = false, isVPeriodic = false;
3672 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3673 surface = BRep_Tool::Surface( face );
3674 faceSubMesh = aMesh->MeshElements( *fId );
3675 fToler2 = BRep_Tool::Tolerance( face );
3676 fToler2 *= fToler2 * 10.;
3677 isUPeriodic = surface->IsUPeriodic();
3678 // if ( isUPeriodic )
3679 // surface->UPeriod();
3680 isVPeriodic = surface->IsVPeriodic();
3681 // if ( isVPeriodic )
3682 // surface->VPeriod();
3683 surface->Bounds( u1, u2, v1, v2 );
3684 helper.SetSubShape( face );
3686 // ---------------------------------------------------------
3687 // for elements on a face, find movable and fixed nodes and
3688 // compute UV for them
3689 // ---------------------------------------------------------
3690 bool checkBoundaryNodes = false;
3691 bool isQuadratic = false;
3692 set<const SMDS_MeshNode*> setMovableNodes;
3693 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3694 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3695 list< const SMDS_MeshElement* > elemsOnFace;
3697 Extrema_GenExtPS projector;
3698 GeomAdaptor_Surface surfAdaptor;
3699 if ( !surface.IsNull() ) {
3700 surfAdaptor.Load( surface );
3701 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3703 int nbElemOnFace = 0;
3704 itElem = theElems.begin();
3705 // loop on not yet smoothed elements: look for elems on a face
3706 while ( itElem != theElems.end() )
3708 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3709 break; // all elements found
3711 const SMDS_MeshElement* elem = *itElem;
3712 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3713 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3717 elemsOnFace.push_back( elem );
3718 theElems.erase( itElem++ );
3722 isQuadratic = elem->IsQuadratic();
3724 // get movable nodes of elem
3725 const SMDS_MeshNode* node;
3726 SMDS_TypeOfPosition posType;
3727 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3728 int nn = 0, nbn = elem->NbNodes();
3729 if(elem->IsQuadratic())
3731 while ( nn++ < nbn ) {
3732 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3733 const SMDS_PositionPtr& pos = node->GetPosition();
3734 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3735 if (posType != SMDS_TOP_EDGE &&
3736 posType != SMDS_TOP_VERTEX &&
3737 theFixedNodes.find( node ) == theFixedNodes.end())
3739 // check if all faces around the node are on faceSubMesh
3740 // because a node on edge may be bound to face
3742 if ( faceSubMesh ) {
3743 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3744 while ( eIt->more() && all ) {
3745 const SMDS_MeshElement* e = eIt->next();
3746 all = faceSubMesh->Contains( e );
3750 setMovableNodes.insert( node );
3752 checkBoundaryNodes = true;
3754 if ( posType == SMDS_TOP_3DSPACE )
3755 checkBoundaryNodes = true;
3758 if ( surface.IsNull() )
3761 // get nodes to check UV
3762 list< const SMDS_MeshNode* > uvCheckNodes;
3763 const SMDS_MeshNode* nodeInFace = 0;
3764 itN = elem->nodesIterator();
3765 nn = 0; nbn = elem->NbNodes();
3766 if(elem->IsQuadratic())
3768 while ( nn++ < nbn ) {
3769 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3770 if ( node->GetPosition()->GetDim() == 2 )
3772 if ( uvMap.find( node ) == uvMap.end() )
3773 uvCheckNodes.push_back( node );
3774 // add nodes of elems sharing node
3775 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3776 // while ( eIt->more() ) {
3777 // const SMDS_MeshElement* e = eIt->next();
3778 // if ( e != elem ) {
3779 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3780 // while ( nIt->more() ) {
3781 // const SMDS_MeshNode* n =
3782 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3783 // if ( uvMap.find( n ) == uvMap.end() )
3784 // uvCheckNodes.push_back( n );
3790 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3791 for ( ; n != uvCheckNodes.end(); ++n ) {
3794 const SMDS_PositionPtr& pos = node->GetPosition();
3795 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3799 bool toCheck = true;
3800 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3802 // compute not existing UV
3803 bool project = ( posType == SMDS_TOP_3DSPACE );
3804 // double dist1 = DBL_MAX, dist2 = 0;
3805 // if ( posType != SMDS_TOP_3DSPACE ) {
3806 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3807 // project = dist1 > fToler2;
3809 if ( project ) { // compute new UV
3811 gp_Pnt pNode = SMESH_NodeXYZ( node );
3812 if ( !getClosestUV( projector, pNode, newUV )) {
3813 MESSAGE("Node Projection Failed " << node);
3817 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3819 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3821 // if ( posType != SMDS_TOP_3DSPACE )
3822 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3823 // if ( dist2 < dist1 )
3827 // store UV in the map
3828 listUV.push_back( uv );
3829 uvMap.insert( make_pair( node, &listUV.back() ));
3831 } // loop on not yet smoothed elements
3833 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3834 checkBoundaryNodes = true;
3836 // fix nodes on mesh boundary
3838 if ( checkBoundaryNodes ) {
3839 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3840 map< SMESH_TLink, int >::iterator link_nb;
3841 // put all elements links to linkNbMap
3842 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3843 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3844 const SMDS_MeshElement* elem = (*elemIt);
3845 int nbn = elem->NbCornerNodes();
3846 // loop on elem links: insert them in linkNbMap
3847 for ( int iN = 0; iN < nbn; ++iN ) {
3848 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3849 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3850 SMESH_TLink link( n1, n2 );
3851 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3855 // remove nodes that are in links encountered only once from setMovableNodes
3856 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3857 if ( link_nb->second == 1 ) {
3858 setMovableNodes.erase( link_nb->first.node1() );
3859 setMovableNodes.erase( link_nb->first.node2() );
3864 // -----------------------------------------------------
3865 // for nodes on seam edge, compute one more UV ( uvMap2 );
3866 // find movable nodes linked to nodes on seam and which
3867 // are to be smoothed using the second UV ( uvMap2 )
3868 // -----------------------------------------------------
3870 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3871 if ( !surface.IsNull() ) {
3872 TopExp_Explorer eExp( face, TopAbs_EDGE );
3873 for ( ; eExp.More(); eExp.Next() ) {
3874 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3875 if ( !BRep_Tool::IsClosed( edge, face ))
3877 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3878 if ( !sm ) continue;
3879 // find out which parameter varies for a node on seam
3882 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3883 if ( pcurve.IsNull() ) continue;
3884 uv1 = pcurve->Value( f );
3886 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3887 if ( pcurve.IsNull() ) continue;
3888 uv2 = pcurve->Value( f );
3889 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3891 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3892 std::swap( uv1, uv2 );
3893 // get nodes on seam and its vertices
3894 list< const SMDS_MeshNode* > seamNodes;
3895 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3896 while ( nSeamIt->more() ) {
3897 const SMDS_MeshNode* node = nSeamIt->next();
3898 if ( !isQuadratic || !IsMedium( node ))
3899 seamNodes.push_back( node );
3901 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3902 for ( ; vExp.More(); vExp.Next() ) {
3903 sm = aMesh->MeshElements( vExp.Current() );
3905 nSeamIt = sm->GetNodes();
3906 while ( nSeamIt->more() )
3907 seamNodes.push_back( nSeamIt->next() );
3910 // loop on nodes on seam
3911 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3912 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3913 const SMDS_MeshNode* nSeam = *noSeIt;
3914 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3915 if ( n_uv == uvMap.end() )
3918 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3919 // set the second UV
3920 listUV.push_back( *n_uv->second );
3921 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3922 if ( uvMap2.empty() )
3923 uvMap2 = uvMap; // copy the uvMap contents
3924 uvMap2[ nSeam ] = &listUV.back();
3926 // collect movable nodes linked to ones on seam in nodesNearSeam
3927 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3928 while ( eIt->more() ) {
3929 const SMDS_MeshElement* e = eIt->next();
3930 int nbUseMap1 = 0, nbUseMap2 = 0;
3931 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3932 int nn = 0, nbn = e->NbNodes();
3933 if(e->IsQuadratic()) nbn = nbn/2;
3934 while ( nn++ < nbn )
3936 const SMDS_MeshNode* n =
3937 static_cast<const SMDS_MeshNode*>( nIt->next() );
3939 setMovableNodes.find( n ) == setMovableNodes.end() )
3941 // add only nodes being closer to uv2 than to uv1
3942 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3943 // 0.5 * ( n->Y() + nSeam->Y() ),
3944 // 0.5 * ( n->Z() + nSeam->Z() ));
3946 // getClosestUV( projector, pMid, uv );
3947 double x = uvMap[ n ]->Coord( iPar );
3948 if ( Abs( uv1.Coord( iPar ) - x ) >
3949 Abs( uv2.Coord( iPar ) - x )) {
3950 nodesNearSeam.insert( n );
3956 // for centroidalSmooth all element nodes must
3957 // be on one side of a seam
3958 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3959 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3961 while ( nn++ < nbn ) {
3962 const SMDS_MeshNode* n =
3963 static_cast<const SMDS_MeshNode*>( nIt->next() );
3964 setMovableNodes.erase( n );
3968 } // loop on nodes on seam
3969 } // loop on edge of a face
3970 } // if ( !face.IsNull() )
3972 if ( setMovableNodes.empty() ) {
3973 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3974 continue; // goto next face
3982 double maxRatio = -1., maxDisplacement = -1.;
3983 set<const SMDS_MeshNode*>::iterator nodeToMove;
3984 for ( it = 0; it < theNbIterations; it++ ) {
3985 maxDisplacement = 0.;
3986 nodeToMove = setMovableNodes.begin();
3987 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3988 const SMDS_MeshNode* node = (*nodeToMove);
3989 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3992 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3993 if ( theSmoothMethod == LAPLACIAN )
3994 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3996 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3998 // node displacement
3999 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4000 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4001 if ( aDispl > maxDisplacement )
4002 maxDisplacement = aDispl;
4004 // no node movement => exit
4005 //if ( maxDisplacement < 1.e-16 ) {
4006 if ( maxDisplacement < disttol ) {
4007 MESSAGE("-- no node movement --");
4011 // check elements quality
4013 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4014 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4015 const SMDS_MeshElement* elem = (*elemIt);
4016 if ( !elem || elem->GetType() != SMDSAbs_Face )
4018 SMESH::Controls::TSequenceOfXYZ aPoints;
4019 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4020 double aValue = aQualityFunc.GetValue( aPoints );
4021 if ( aValue > maxRatio )
4025 if ( maxRatio <= theTgtAspectRatio ) {
4026 //MESSAGE("-- quality achieved --");
4029 if (it+1 == theNbIterations) {
4030 //MESSAGE("-- Iteration limit exceeded --");
4032 } // smoothing iterations
4034 // MESSAGE(" Face id: " << *fId <<
4035 // " Nb iterstions: " << it <<
4036 // " Displacement: " << maxDisplacement <<
4037 // " Aspect Ratio " << maxRatio);
4039 // ---------------------------------------
4040 // new nodes positions are computed,
4041 // record movement in DS and set new UV
4042 // ---------------------------------------
4043 nodeToMove = setMovableNodes.begin();
4044 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4045 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4046 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4047 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4048 if ( node_uv != uvMap.end() ) {
4049 gp_XY* uv = node_uv->second;
4051 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4055 // move medium nodes of quadratic elements
4058 vector<const SMDS_MeshNode*> nodes;
4060 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4061 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4063 const SMDS_MeshElement* QF = *elemIt;
4064 if ( QF->IsQuadratic() )
4066 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4067 SMDS_MeshElement::iterator() );
4068 nodes.push_back( nodes[0] );
4070 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4072 if ( !surface.IsNull() )
4074 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4075 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4076 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4077 xyz = surface->Value( uv.X(), uv.Y() );
4080 xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4082 if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4083 // we have to move a medium node
4084 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4090 } // loop on face ids
4096 //=======================================================================
4097 //function : isReverse
4098 //purpose : Return true if normal of prevNodes is not co-directied with
4099 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4100 // iNotSame is where prevNodes and nextNodes are different.
4101 // If result is true then future volume orientation is OK
4102 //=======================================================================
4104 bool isReverse(const SMDS_MeshElement* face,
4105 const vector<const SMDS_MeshNode*>& prevNodes,
4106 const vector<const SMDS_MeshNode*>& nextNodes,
4110 SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4111 SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4112 gp_XYZ extrDir( pN - pP ), faceNorm;
4113 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4115 return faceNorm * extrDir < 0.0;
4118 //================================================================================
4120 * \brief Assure that theElemSets[0] holds elements, not nodes
4122 //================================================================================
4124 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4126 if ( !theElemSets[0].empty() &&
4127 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4129 std::swap( theElemSets[0], theElemSets[1] );
4131 else if ( !theElemSets[1].empty() &&
4132 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4134 std::swap( theElemSets[0], theElemSets[1] );
4139 //=======================================================================
4141 * \brief Create elements by sweeping an element
4142 * \param elem - element to sweep
4143 * \param newNodesItVec - nodes generated from each node of the element
4144 * \param newElems - generated elements
4145 * \param nbSteps - number of sweeping steps
4146 * \param srcElements - to append elem for each generated element
4148 //=======================================================================
4150 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4151 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4152 list<const SMDS_MeshElement*>& newElems,
4153 const size_t nbSteps,
4154 SMESH_SequenceOfElemPtr& srcElements)
4156 SMESHDS_Mesh* aMesh = GetMeshDS();
4158 const int nbNodes = elem->NbNodes();
4159 const int nbCorners = elem->NbCornerNodes();
4160 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4161 polyhedron creation !!! */
4162 // Loop on elem nodes:
4163 // find new nodes and detect same nodes indices
4164 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4165 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4166 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4167 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4169 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4170 vector<int> sames(nbNodes);
4171 vector<bool> isSingleNode(nbNodes);
4173 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4174 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4175 const SMDS_MeshNode* node = nnIt->first;
4176 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4177 if ( listNewNodes.empty() )
4180 itNN [ iNode ] = listNewNodes.begin();
4181 prevNod[ iNode ] = node;
4182 nextNod[ iNode ] = listNewNodes.front();
4184 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4185 corner node of linear */
4186 if ( prevNod[ iNode ] != nextNod [ iNode ])
4187 nbDouble += !isSingleNode[iNode];
4189 if( iNode < nbCorners ) { // check corners only
4190 if ( prevNod[ iNode ] == nextNod [ iNode ])
4191 sames[nbSame++] = iNode;
4193 iNotSameNode = iNode;
4197 if ( nbSame == nbNodes || nbSame > 2) {
4198 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4202 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4204 // fix nodes order to have bottom normal external
4205 if ( baseType == SMDSEntity_Polygon )
4207 std::reverse( itNN.begin(), itNN.end() );
4208 std::reverse( prevNod.begin(), prevNod.end() );
4209 std::reverse( midlNod.begin(), midlNod.end() );
4210 std::reverse( nextNod.begin(), nextNod.end() );
4211 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4215 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4216 SMDS_MeshCell::applyInterlace( ind, itNN );
4217 SMDS_MeshCell::applyInterlace( ind, prevNod );
4218 SMDS_MeshCell::applyInterlace( ind, nextNod );
4219 SMDS_MeshCell::applyInterlace( ind, midlNod );
4220 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4223 sames[nbSame] = iNotSameNode;
4224 for ( int j = 0; j <= nbSame; ++j )
4225 for ( size_t i = 0; i < ind.size(); ++i )
4226 if ( ind[i] == sames[j] )
4231 iNotSameNode = sames[nbSame];
4235 else if ( elem->GetType() == SMDSAbs_Edge )
4237 // orient a new face same as adjacent one
4239 const SMDS_MeshElement* e;
4240 TIDSortedElemSet dummy;
4241 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4242 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4243 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4245 // there is an adjacent face, check order of nodes in it
4246 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4249 std::swap( itNN[0], itNN[1] );
4250 std::swap( prevNod[0], prevNod[1] );
4251 std::swap( nextNod[0], nextNod[1] );
4252 std::swap( isSingleNode[0], isSingleNode[1] );
4254 sames[0] = 1 - sames[0];
4255 iNotSameNode = 1 - iNotSameNode;
4260 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4262 iSameNode = sames[ nbSame-1 ];
4263 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4264 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4265 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4268 if ( baseType == SMDSEntity_Polygon )
4270 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4271 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4273 else if ( baseType == SMDSEntity_Quad_Polygon )
4275 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4276 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4279 // make new elements
4280 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4283 for ( iNode = 0; iNode < nbNodes; iNode++ )
4285 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4286 nextNod[ iNode ] = *itNN[ iNode ]++;
4289 SMDS_MeshElement* aNewElem = 0;
4290 /*if(!elem->IsPoly())*/ {
4291 switch ( baseType ) {
4293 case SMDSEntity_Node: { // sweep NODE
4294 if ( nbSame == 0 ) {
4295 if ( isSingleNode[0] )
4296 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4298 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4304 case SMDSEntity_Edge: { // sweep EDGE
4305 if ( nbDouble == 0 )
4307 if ( nbSame == 0 ) // ---> quadrangle
4308 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4309 nextNod[ 1 ], nextNod[ 0 ] );
4310 else // ---> triangle
4311 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4312 nextNod[ iNotSameNode ] );
4314 else // ---> polygon
4316 vector<const SMDS_MeshNode*> poly_nodes;
4317 poly_nodes.push_back( prevNod[0] );
4318 poly_nodes.push_back( prevNod[1] );
4319 if ( prevNod[1] != nextNod[1] )
4321 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4322 poly_nodes.push_back( nextNod[1] );
4324 if ( prevNod[0] != nextNod[0] )
4326 poly_nodes.push_back( nextNod[0] );
4327 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4329 switch ( poly_nodes.size() ) {
4331 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4334 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4335 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4338 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4343 case SMDSEntity_Triangle: // TRIANGLE --->
4345 if ( nbDouble > 0 ) break;
4346 if ( nbSame == 0 ) // ---> pentahedron
4347 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4348 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4350 else if ( nbSame == 1 ) // ---> pyramid
4351 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4352 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4353 nextNod[ iSameNode ]);
4355 else // 2 same nodes: ---> tetrahedron
4356 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4357 nextNod[ iNotSameNode ]);
4360 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4364 if ( nbDouble+nbSame == 2 )
4366 if(nbSame==0) { // ---> quadratic quadrangle
4367 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4368 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4370 else { //(nbSame==1) // ---> quadratic triangle
4372 return; // medium node on axis
4374 else if(sames[0]==0)
4375 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4376 prevNod[2], midlNod[1], nextNod[2] );
4378 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4379 prevNod[2], nextNod[2], midlNod[0]);
4382 else if ( nbDouble == 3 )
4384 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4385 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4386 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4393 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4394 if ( nbDouble > 0 ) break;
4396 if ( nbSame == 0 ) // ---> hexahedron
4397 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4398 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4400 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4401 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4402 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4403 nextNod[ iSameNode ]);
4404 newElems.push_back( aNewElem );
4405 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4406 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4407 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4409 else if ( nbSame == 2 ) { // ---> pentahedron
4410 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4411 // iBeforeSame is same too
4412 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4413 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4414 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4416 // iAfterSame is same too
4417 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4418 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4419 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4423 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4424 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4425 if ( nbDouble+nbSame != 3 ) break;
4427 // ---> pentahedron with 15 nodes
4428 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4429 nextNod[0], nextNod[1], nextNod[2],
4430 prevNod[3], prevNod[4], prevNod[5],
4431 nextNod[3], nextNod[4], nextNod[5],
4432 midlNod[0], midlNod[1], midlNod[2]);
4434 else if(nbSame==1) {
4435 // ---> 2d order pyramid of 13 nodes
4436 int apex = iSameNode;
4437 int i0 = ( apex + 1 ) % nbCorners;
4438 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4442 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4443 nextNod[i0], nextNod[i1], prevNod[apex],
4444 prevNod[i01], midlNod[i0],
4445 nextNod[i01], midlNod[i1],
4446 prevNod[i1a], prevNod[i0a],
4447 nextNod[i0a], nextNod[i1a]);
4449 else if(nbSame==2) {
4450 // ---> 2d order tetrahedron of 10 nodes
4451 int n1 = iNotSameNode;
4452 int n2 = ( n1 + 1 ) % nbCorners;
4453 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4457 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4458 prevNod[n12], prevNod[n23], prevNod[n31],
4459 midlNod[n1], nextNod[n12], nextNod[n31]);
4463 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4465 if ( nbDouble != 4 ) break;
4466 // ---> hexahedron with 20 nodes
4467 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4468 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4469 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4470 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4471 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4473 else if(nbSame==1) {
4474 // ---> pyramid + pentahedron - can not be created since it is needed
4475 // additional middle node at the center of face
4476 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4479 else if( nbSame == 2 ) {
4480 if ( nbDouble != 2 ) break;
4481 // ---> 2d order Pentahedron with 15 nodes
4483 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4484 // iBeforeSame is same too
4491 // iAfterSame is same too
4501 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4502 prevNod[n4], prevNod[n5], nextNod[n5],
4503 prevNod[n12], midlNod[n2], nextNod[n12],
4504 prevNod[n45], midlNod[n5], nextNod[n45],
4505 prevNod[n14], prevNod[n25], nextNod[n25]);
4509 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4511 if( nbSame == 0 && nbDouble == 9 ) {
4512 // ---> tri-quadratic hexahedron with 27 nodes
4513 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4514 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4515 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4516 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4517 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4518 prevNod[8], // bottom center
4519 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4520 nextNod[8], // top center
4521 midlNod[8]);// elem center
4529 case SMDSEntity_Polygon: { // sweep POLYGON
4531 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4532 // ---> hexagonal prism
4533 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4534 prevNod[3], prevNod[4], prevNod[5],
4535 nextNod[0], nextNod[1], nextNod[2],
4536 nextNod[3], nextNod[4], nextNod[5]);
4540 case SMDSEntity_Ball:
4545 } // switch ( baseType )
4548 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4550 if ( baseType != SMDSEntity_Polygon )
4552 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4553 SMDS_MeshCell::applyInterlace( ind, prevNod );
4554 SMDS_MeshCell::applyInterlace( ind, nextNod );
4555 SMDS_MeshCell::applyInterlace( ind, midlNod );
4556 SMDS_MeshCell::applyInterlace( ind, itNN );
4557 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4558 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4560 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4561 vector<int> quantities (nbNodes + 2);
4562 polyedre_nodes.clear();
4566 for (int inode = 0; inode < nbNodes; inode++)
4567 polyedre_nodes.push_back( prevNod[inode] );
4568 quantities.push_back( nbNodes );
4571 polyedre_nodes.push_back( nextNod[0] );
4572 for (int inode = nbNodes; inode-1; --inode )
4573 polyedre_nodes.push_back( nextNod[inode-1] );
4574 quantities.push_back( nbNodes );
4582 const int iQuad = elem->IsQuadratic();
4583 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4585 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4586 int inextface = (iface+1+iQuad) % nbNodes;
4587 int imid = (iface+1) % nbNodes;
4588 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4589 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4590 polyedre_nodes.push_back( prevNod[iface] ); // 1
4591 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4593 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4594 polyedre_nodes.push_back( nextNod[iface] ); // 2
4596 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4597 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4599 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4600 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4602 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4603 if ( nbFaceNodes > 2 )
4604 quantities.push_back( nbFaceNodes );
4605 else // degenerated face
4606 polyedre_nodes.resize( prevNbNodes );
4608 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4610 } // try to create a polyherdal prism
4613 newElems.push_back( aNewElem );
4614 myLastCreatedElems.push_back(aNewElem);
4615 srcElements.push_back( elem );
4618 // set new prev nodes
4619 for ( iNode = 0; iNode < nbNodes; iNode++ )
4620 prevNod[ iNode ] = nextNod[ iNode ];
4625 //=======================================================================
4627 * \brief Create 1D and 2D elements around swept elements
4628 * \param mapNewNodes - source nodes and ones generated from them
4629 * \param newElemsMap - source elements and ones generated from them
4630 * \param elemNewNodesMap - nodes generated from each node of each element
4631 * \param elemSet - all swept elements
4632 * \param nbSteps - number of sweeping steps
4633 * \param srcElements - to append elem for each generated element
4635 //=======================================================================
4637 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4638 TTElemOfElemListMap & newElemsMap,
4639 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4640 TIDSortedElemSet& elemSet,
4642 SMESH_SequenceOfElemPtr& srcElements)
4644 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4645 SMESHDS_Mesh* aMesh = GetMeshDS();
4647 // Find nodes belonging to only one initial element - sweep them into edges.
4649 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4650 for ( ; nList != mapNewNodes.end(); nList++ )
4652 const SMDS_MeshNode* node =
4653 static_cast<const SMDS_MeshNode*>( nList->first );
4654 if ( newElemsMap.count( node ))
4655 continue; // node was extruded into edge
4656 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4657 int nbInitElems = 0;
4658 const SMDS_MeshElement* el = 0;
4659 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4660 while ( eIt->more() && nbInitElems < 2 ) {
4661 const SMDS_MeshElement* e = eIt->next();
4662 SMDSAbs_ElementType type = e->GetType();
4663 if ( type == SMDSAbs_Volume ||
4667 if ( type > highType ) {
4674 if ( nbInitElems == 1 ) {
4675 bool NotCreateEdge = el && el->IsMediumNode(node);
4676 if(!NotCreateEdge) {
4677 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4678 list<const SMDS_MeshElement*> newEdges;
4679 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4684 // Make a ceiling for each element ie an equal element of last new nodes.
4685 // Find free links of faces - make edges and sweep them into faces.
4687 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4689 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4690 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4691 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4693 const SMDS_MeshElement* elem = itElem->first;
4694 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4696 if(itElem->second.size()==0) continue;
4698 const bool isQuadratic = elem->IsQuadratic();
4700 if ( elem->GetType() == SMDSAbs_Edge ) {
4701 // create a ceiling edge
4702 if ( !isQuadratic ) {
4703 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4704 vecNewNodes[ 1 ]->second.back())) {
4705 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4706 vecNewNodes[ 1 ]->second.back()));
4707 srcElements.push_back( elem );
4711 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4712 vecNewNodes[ 1 ]->second.back(),
4713 vecNewNodes[ 2 ]->second.back())) {
4714 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4715 vecNewNodes[ 1 ]->second.back(),
4716 vecNewNodes[ 2 ]->second.back()));
4717 srcElements.push_back( elem );
4721 if ( elem->GetType() != SMDSAbs_Face )
4724 bool hasFreeLinks = false;
4726 TIDSortedElemSet avoidSet;
4727 avoidSet.insert( elem );
4729 set<const SMDS_MeshNode*> aFaceLastNodes;
4730 int iNode, nbNodes = vecNewNodes.size();
4731 if ( !isQuadratic ) {
4732 // loop on the face nodes
4733 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4734 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4735 // look for free links of the face
4736 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4737 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4738 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4739 // check if a link n1-n2 is free
4740 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4741 hasFreeLinks = true;
4742 // make a new edge and a ceiling for a new edge
4743 const SMDS_MeshElement* edge;
4744 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4745 myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4746 srcElements.push_back( myLastCreatedElems.back() );
4748 n1 = vecNewNodes[ iNode ]->second.back();
4749 n2 = vecNewNodes[ iNext ]->second.back();
4750 if ( !aMesh->FindEdge( n1, n2 )) {
4751 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4752 srcElements.push_back( edge );
4757 else { // elem is quadratic face
4758 int nbn = nbNodes/2;
4759 for ( iNode = 0; iNode < nbn; iNode++ ) {
4760 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4761 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4762 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4763 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4764 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4765 // check if a link is free
4766 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4767 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4768 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4769 hasFreeLinks = true;
4770 // make an edge and a ceiling for a new edge
4772 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4773 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4774 srcElements.push_back( elem );
4776 n1 = vecNewNodes[ iNode ]->second.back();
4777 n2 = vecNewNodes[ iNext ]->second.back();
4778 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4779 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4780 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4781 srcElements.push_back( elem );
4785 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4786 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4790 // sweep free links into faces
4792 if ( hasFreeLinks ) {
4793 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4794 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4796 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4797 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4798 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4799 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4800 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4802 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4803 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4804 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4806 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4807 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4808 std::advance( v, volNb );
4809 // find indices of free faces of a volume and their source edges
4810 list< int > freeInd;
4811 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4812 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4813 int iF, nbF = vTool.NbFaces();
4814 for ( iF = 0; iF < nbF; iF ++ ) {
4815 if ( vTool.IsFreeFace( iF ) &&
4816 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4817 initNodeSet != faceNodeSet) // except an initial face
4819 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4821 if ( faceNodeSet == initNodeSetNoCenter )
4823 freeInd.push_back( iF );
4824 // find source edge of a free face iF
4825 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4826 vector<const SMDS_MeshNode*>::iterator lastCommom;
4827 commonNodes.resize( nbNodes, 0 );
4828 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4829 initNodeSet.begin(), initNodeSet.end(),
4830 commonNodes.begin());
4831 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4832 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4834 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4836 if ( !srcEdges.back() )
4838 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4839 << iF << " of volume #" << vTool.ID() << endl;
4844 if ( freeInd.empty() )
4847 // create wall faces for all steps;
4848 // if such a face has been already created by sweep of edge,
4849 // assure that its orientation is OK
4850 for ( int iStep = 0; iStep < nbSteps; iStep++ )
4852 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4853 vTool.SetExternalNormal();
4854 const int nextShift = vTool.IsForward() ? +1 : -1;
4855 list< int >::iterator ind = freeInd.begin();
4856 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4857 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4859 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4860 int nbn = vTool.NbFaceNodes( *ind );
4861 const SMDS_MeshElement * f = 0;
4862 if ( nbn == 3 ) ///// triangle
4864 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4866 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4868 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4870 nodes[ 1 + nextShift ] };
4872 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4874 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4878 else if ( nbn == 4 ) ///// quadrangle
4880 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4882 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4884 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4885 nodes[ 2 ], nodes[ 2+nextShift ] };
4887 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4889 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4890 newOrder[ 2 ], newOrder[ 3 ]));
4893 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4895 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4897 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4899 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4901 nodes[2 + 2*nextShift],
4902 nodes[3 - 2*nextShift],
4904 nodes[3 + 2*nextShift]};
4906 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4908 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4916 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4918 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4919 nodes[1], nodes[3], nodes[5], nodes[7] );
4921 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4923 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4924 nodes[4 - 2*nextShift],
4926 nodes[4 + 2*nextShift],
4928 nodes[5 - 2*nextShift],
4930 nodes[5 + 2*nextShift] };
4932 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4934 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4935 newOrder[ 2 ], newOrder[ 3 ],
4936 newOrder[ 4 ], newOrder[ 5 ],
4937 newOrder[ 6 ], newOrder[ 7 ]));
4940 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4942 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4943 SMDSAbs_Face, /*noMedium=*/false);
4945 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4947 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4948 nodes[4 - 2*nextShift],
4950 nodes[4 + 2*nextShift],
4952 nodes[5 - 2*nextShift],
4954 nodes[5 + 2*nextShift],
4957 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4959 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4960 newOrder[ 2 ], newOrder[ 3 ],
4961 newOrder[ 4 ], newOrder[ 5 ],
4962 newOrder[ 6 ], newOrder[ 7 ],
4966 else //////// polygon
4968 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4969 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4971 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4973 if ( !vTool.IsForward() )
4974 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4976 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4978 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
4982 while ( srcElements.size() < myLastCreatedElems.size() )
4983 srcElements.push_back( *srcEdge );
4985 } // loop on free faces
4987 // go to the next volume
4989 while ( iVol++ < nbVolumesByStep ) v++;
4992 } // loop on volumes of one step
4993 } // sweep free links into faces
4995 // Make a ceiling face with a normal external to a volume
4997 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
4998 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4999 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5001 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5002 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5003 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5007 lastVol.SetExternalNormal();
5008 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5009 const int nbn = lastVol.NbFaceNodes( iF );
5010 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5011 if ( !hasFreeLinks ||
5012 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5014 const vector<int>& interlace =
5015 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5016 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5018 AddElement( nodeVec, anyFace.Init( elem ));
5020 while ( srcElements.size() < myLastCreatedElems.size() )
5021 srcElements.push_back( elem );
5024 } // loop on swept elements
5027 //=======================================================================
5028 //function : RotationSweep
5030 //=======================================================================
5032 SMESH_MeshEditor::PGroupIDs
5033 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5034 const gp_Ax1& theAxis,
5035 const double theAngle,
5036 const int theNbSteps,
5037 const double theTol,
5038 const bool theMakeGroups,
5039 const bool theMakeWalls)
5043 setElemsFirst( theElemSets );
5044 myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5045 myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5047 // source elements for each generated one
5048 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5049 srcElems.reserve( theElemSets[0].size() );
5050 srcNodes.reserve( theElemSets[1].size() );
5053 aTrsf.SetRotation( theAxis, theAngle );
5055 aTrsf2.SetRotation( theAxis, theAngle/2. );
5057 gp_Lin aLine( theAxis );
5058 double aSqTol = theTol * theTol;
5060 SMESHDS_Mesh* aMesh = GetMeshDS();
5062 TNodeOfNodeListMap mapNewNodes;
5063 TElemOfVecOfNnlmiMap mapElemNewNodes;
5064 TTElemOfElemListMap newElemsMap;
5066 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5067 myMesh->NbFaces(ORDER_QUADRATIC) +
5068 myMesh->NbVolumes(ORDER_QUADRATIC) );
5069 // loop on theElemSets
5070 TIDSortedElemSet::iterator itElem;
5071 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5073 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5074 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5075 const SMDS_MeshElement* elem = *itElem;
5076 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5078 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5079 newNodesItVec.reserve( elem->NbNodes() );
5081 // loop on elem nodes
5082 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5083 while ( itN->more() )
5085 const SMDS_MeshNode* node = cast2Node( itN->next() );
5087 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5089 aXYZ.Coord( coord[0], coord[1], coord[2] );
5090 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5092 // check if a node has been already sweeped
5093 TNodeOfNodeListMapItr nIt =
5094 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5095 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5096 if ( listNewNodes.empty() )
5098 // check if we are to create medium nodes between corner ones
5099 bool needMediumNodes = false;
5100 if ( isQuadraticMesh )
5102 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5103 while (it->more() && !needMediumNodes )
5105 const SMDS_MeshElement* invElem = it->next();
5106 if ( invElem != elem && !theElems.count( invElem )) continue;
5107 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5108 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5109 needMediumNodes = true;
5114 const SMDS_MeshNode * newNode = node;
5115 for ( int i = 0; i < theNbSteps; i++ ) {
5117 if ( needMediumNodes ) // create a medium node
5119 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5120 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5121 myLastCreatedNodes.push_back(newNode);
5122 srcNodes.push_back( node );
5123 listNewNodes.push_back( newNode );
5124 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5127 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5129 // create a corner node
5130 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5131 myLastCreatedNodes.push_back(newNode);
5132 srcNodes.push_back( node );
5133 listNewNodes.push_back( newNode );
5136 listNewNodes.push_back( newNode );
5137 // if ( needMediumNodes )
5138 // listNewNodes.push_back( newNode );
5142 newNodesItVec.push_back( nIt );
5144 // make new elements
5145 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5150 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5152 PGroupIDs newGroupIDs;
5153 if ( theMakeGroups )
5154 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5159 //=======================================================================
5160 //function : ExtrusParam
5161 //purpose : standard construction
5162 //=======================================================================
5164 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5165 const int theNbSteps,
5166 const std::list<double>& theScales,
5167 const std::list<double>& theAngles,
5168 const gp_XYZ* theBasePoint,
5170 const double theTolerance):
5172 myBaseP( Precision::Infinite(), 0, 0 ),
5173 myFlags( theFlags ),
5174 myTolerance( theTolerance ),
5175 myElemsToUse( NULL )
5177 mySteps = new TColStd_HSequenceOfReal;
5178 const double stepSize = theStep.Magnitude();
5179 for (int i=1; i<=theNbSteps; i++ )
5180 mySteps->Append( stepSize );
5182 if ( !theScales.empty() )
5184 if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5185 linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5187 // add medium scales
5188 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5189 myScales.reserve( theNbSteps * 2 );
5190 myScales.push_back( 0.5 * ( *s1 + 1. ));
5191 myScales.push_back( *s1 );
5192 for ( ; s2 != theScales.end(); s1 = s2++ )
5194 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5195 myScales.push_back( *s2 );
5199 if ( !theAngles.empty() )
5201 std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5202 if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5203 linearAngleVariation( theNbSteps, angles );
5205 // accumulate angles
5208 std::list<double>::iterator a1 = angles.begin(), a2;
5209 for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5214 while ( nbAngles++ < theNbSteps )
5215 angles.push_back( angles.back() );
5217 // add medium angles
5218 a2 = angles.begin(), a1 = a2++;
5219 myAngles.push_back( 0.5 * *a1 );
5220 myAngles.push_back( *a1 );
5221 for ( ; a2 != angles.end(); a1 = a2++ )
5223 myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5224 myAngles.push_back( *a2 );
5230 myBaseP = *theBasePoint;
5233 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5234 ( theTolerance > 0 ))
5236 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5240 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5244 //=======================================================================
5245 //function : ExtrusParam
5246 //purpose : steps are given explicitly
5247 //=======================================================================
5249 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5250 Handle(TColStd_HSequenceOfReal) theSteps,
5252 const double theTolerance):
5254 mySteps( theSteps ),
5255 myFlags( theFlags ),
5256 myTolerance( theTolerance ),
5257 myElemsToUse( NULL )
5259 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5260 ( theTolerance > 0 ))
5262 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5266 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5270 //=======================================================================
5271 //function : ExtrusParam
5272 //purpose : for extrusion by normal
5273 //=======================================================================
5275 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5276 const int theNbSteps,
5280 mySteps( new TColStd_HSequenceOfReal ),
5281 myFlags( theFlags ),
5283 myElemsToUse( NULL )
5285 for (int i = 0; i < theNbSteps; i++ )
5286 mySteps->Append( theStepSize );
5290 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5294 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5298 //=======================================================================
5299 //function : ExtrusParam
5300 //purpose : for extrusion along path
5301 //=======================================================================
5303 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5304 const gp_Pnt* theBasePoint,
5305 const std::list<double>& theScales,
5306 const bool theMakeGroups )
5307 : myBaseP( Precision::Infinite(), 0, 0 ),
5308 myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5309 myPathPoints( thePoints )
5313 myBaseP = theBasePoint->XYZ();
5316 if ( !theScales.empty() )
5318 // add medium scales
5319 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5320 myScales.reserve( thePoints.size() * 2 );
5321 myScales.push_back( 0.5 * ( 1. + *s1 ));
5322 myScales.push_back( *s1 );
5323 for ( ; s2 != theScales.end(); s1 = s2++ )
5325 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5326 myScales.push_back( *s2 );
5330 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5333 //=======================================================================
5334 //function : ExtrusParam::SetElementsToUse
5335 //purpose : stores elements to use for extrusion by normal, depending on
5336 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5337 // define myBaseP for scaling
5338 //=======================================================================
5340 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5341 const TIDSortedElemSet& nodes )
5343 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5345 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5347 myBaseP.SetCoord( 0.,0.,0. );
5348 TIDSortedElemSet newNodes;
5350 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5351 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5353 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5354 TIDSortedElemSet::const_iterator itElem = elements.begin();
5355 for ( ; itElem != elements.end(); itElem++ )
5357 const SMDS_MeshElement* elem = *itElem;
5358 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5359 while ( itN->more() ) {
5360 const SMDS_MeshElement* node = itN->next();
5361 if ( newNodes.insert( node ).second )
5362 myBaseP += SMESH_NodeXYZ( node );
5366 myBaseP /= newNodes.size();
5370 //=======================================================================
5371 //function : ExtrusParam::beginStepIter
5372 //purpose : prepare iteration on steps
5373 //=======================================================================
5375 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5377 myWithMediumNodes = withMediumNodes;
5381 //=======================================================================
5382 //function : ExtrusParam::moreSteps
5383 //purpose : are there more steps?
5384 //=======================================================================
5386 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5388 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5390 //=======================================================================
5391 //function : ExtrusParam::nextStep
5392 //purpose : returns the next step
5393 //=======================================================================
5395 double SMESH_MeshEditor::ExtrusParam::nextStep()
5398 if ( !myCurSteps.empty() )
5400 res = myCurSteps.back();
5401 myCurSteps.pop_back();
5403 else if ( myNextStep <= mySteps->Length() )
5405 myCurSteps.push_back( mySteps->Value( myNextStep ));
5407 if ( myWithMediumNodes )
5409 myCurSteps.back() /= 2.;
5410 myCurSteps.push_back( myCurSteps.back() );
5417 //=======================================================================
5418 //function : ExtrusParam::makeNodesByDir
5419 //purpose : create nodes for standard extrusion
5420 //=======================================================================
5422 int SMESH_MeshEditor::ExtrusParam::
5423 makeNodesByDir( SMESHDS_Mesh* mesh,
5424 const SMDS_MeshNode* srcNode,
5425 std::list<const SMDS_MeshNode*> & newNodes,
5426 const bool makeMediumNodes)
5428 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5431 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5433 p += myDir.XYZ() * nextStep();
5434 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5435 newNodes.push_back( newNode );
5438 if ( !myScales.empty() || !myAngles.empty() )
5440 gp_XYZ center = myBaseP;
5441 gp_Ax1 ratationAxis( center, myDir );
5444 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5445 size_t i = !makeMediumNodes;
5446 for ( beginStepIter( makeMediumNodes );
5448 ++nIt, i += 1 + !makeMediumNodes )
5450 center += myDir.XYZ() * nextStep();
5452 gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5454 if ( i < myScales.size() )
5456 xyz = ( myScales[i] * ( xyz - center )) + center;
5459 if ( !myAngles.empty() )
5461 rotation.SetRotation( ratationAxis, myAngles[i] );
5462 rotation.Transforms( xyz );
5466 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5474 //=======================================================================
5475 //function : ExtrusParam::makeNodesByDirAndSew
5476 //purpose : create nodes for standard extrusion with sewing
5477 //=======================================================================
5479 int SMESH_MeshEditor::ExtrusParam::
5480 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5481 const SMDS_MeshNode* srcNode,
5482 std::list<const SMDS_MeshNode*> & newNodes,
5483 const bool makeMediumNodes)
5485 gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5488 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5490 P1 += myDir.XYZ() * nextStep();
5492 // try to search in sequence of existing nodes
5493 // if myNodes.size()>0 we 'nave to use given sequence
5494 // else - use all nodes of mesh
5495 const SMDS_MeshNode * node = 0;
5496 if ( myNodes.Length() > 0 )
5498 for ( int i = 1; i <= myNodes.Length(); i++ )
5500 SMESH_NodeXYZ P2 = myNodes.Value(i);
5501 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5503 node = myNodes.Value(i);
5510 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5513 SMESH_NodeXYZ P2 = itn->next();
5514 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5523 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5525 newNodes.push_back( node );
5532 //=======================================================================
5533 //function : ExtrusParam::makeNodesByNormal2D
5534 //purpose : create nodes for extrusion using normals of faces
5535 //=======================================================================
5537 int SMESH_MeshEditor::ExtrusParam::
5538 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5539 const SMDS_MeshNode* srcNode,
5540 std::list<const SMDS_MeshNode*> & newNodes,
5541 const bool makeMediumNodes)
5543 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5545 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5547 // get normals to faces sharing srcNode
5548 vector< gp_XYZ > norms, baryCenters;
5549 gp_XYZ norm, avgNorm( 0,0,0 );
5550 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5551 while ( faceIt->more() )
5553 const SMDS_MeshElement* face = faceIt->next();
5554 if ( myElemsToUse && !myElemsToUse->count( face ))
5556 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5558 norms.push_back( norm );
5560 if ( !alongAvgNorm )
5564 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5565 bc += SMESH_NodeXYZ( nIt->next() );
5566 baryCenters.push_back( bc / nbN );
5571 if ( norms.empty() ) return 0;
5573 double normSize = avgNorm.Modulus();
5574 if ( normSize < std::numeric_limits<double>::min() )
5577 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5580 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5583 avgNorm /= normSize;
5586 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5589 double stepSize = nextStep();
5591 if ( norms.size() > 1 )
5593 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5595 // translate plane of a face
5596 baryCenters[ iF ] += norms[ iF ] * stepSize;
5598 // find point of intersection of the face plane located at baryCenters[ iF ]
5599 // and avgNorm located at pNew
5600 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5601 double dot = ( norms[ iF ] * avgNorm );
5602 if ( dot < std::numeric_limits<double>::min() )
5603 dot = stepSize * 1e-3;
5604 double step = -( norms[ iF ] * pNew + d ) / dot;
5605 pNew += step * avgNorm;
5610 pNew += stepSize * avgNorm;
5614 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5615 newNodes.push_back( newNode );
5620 //=======================================================================
5621 //function : ExtrusParam::makeNodesByNormal1D
5622 //purpose : create nodes for extrusion using normals of edges
5623 //=======================================================================
5625 int SMESH_MeshEditor::ExtrusParam::
5626 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5627 const SMDS_MeshNode* srcNode,
5628 std::list<const SMDS_MeshNode*> & newNodes,
5629 const bool makeMediumNodes)
5631 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5635 //=======================================================================
5636 //function : ExtrusParam::makeNodesAlongTrack
5637 //purpose : create nodes for extrusion along path
5638 //=======================================================================
5640 int SMESH_MeshEditor::ExtrusParam::
5641 makeNodesAlongTrack( SMESHDS_Mesh* mesh,
5642 const SMDS_MeshNode* srcNode,
5643 std::list<const SMDS_MeshNode*> & newNodes,
5644 const bool makeMediumNodes)
5646 const Standard_Real aTolAng=1.e-4;
5648 gp_Pnt aV0x = myBaseP;
5649 gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
5651 const PathPoint& aPP0 = myPathPoints[0];
5652 gp_Pnt aP0x = aPP0.myPnt;
5653 gp_Dir aDT0x= aPP0.myTgt;
5655 std::vector< gp_Pnt > centers;
5656 centers.reserve( NbSteps() * 2 );
5658 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5660 for ( size_t j = 1; j < myPathPoints.size(); ++j )
5662 const PathPoint& aPP = myPathPoints[j];
5663 const gp_Pnt& aP1x = aPP.myPnt;
5664 const gp_Dir& aDT1x = aPP.myTgt;
5667 gp_Vec aV01x( aP0x, aP1x );
5668 aTrsf.SetTranslation( aV01x );
5669 gp_Pnt aV1x = aV0x.Transformed( aTrsf );
5670 gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
5672 // rotation 1 [ T1,T0 ]
5673 Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
5674 if ( fabs( aAngleT1T0 ) > aTolAng )
5676 gp_Dir aDT1T0 = aDT1x ^ aDT0x;
5677 aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
5679 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5683 if ( aPP.myAngle != 0. )
5685 aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
5686 aPN1 = aPN1.Transformed( aTrsfRot );
5690 if ( makeMediumNodes )
5692 // create additional node
5693 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
5694 const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
5695 newNodes.push_back( newNode );
5698 const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5699 newNodes.push_back( newNode );
5701 centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
5702 centers.push_back( aV1x );
5711 if ( !myScales.empty() )
5714 std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
5715 for ( size_t i = !makeMediumNodes;
5716 i < myScales.size() && node != newNodes.end();
5717 i += ( 1 + !makeMediumNodes ), ++node )
5719 aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
5720 gp_Pnt aN = SMESH_NodeXYZ( *node );
5721 gp_Pnt aP = aN.Transformed( aTrsfScale );
5722 mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
5726 return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
5729 //=======================================================================
5730 //function : ExtrusionSweep
5732 //=======================================================================
5734 SMESH_MeshEditor::PGroupIDs
5735 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5736 const gp_Vec& theStep,
5737 const int theNbSteps,
5738 TTElemOfElemListMap& newElemsMap,
5740 const double theTolerance)
5742 std::list<double> dummy;
5743 ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
5744 theFlags, theTolerance );
5745 return ExtrusionSweep( theElems, aParams, newElemsMap );
5751 //=======================================================================
5752 //function : getOriFactor
5753 //purpose : Return -1 or 1 depending on if order of given nodes corresponds to
5754 // edge curve orientation
5755 //=======================================================================
5757 double getOriFactor( const TopoDS_Edge& edge,
5758 const SMDS_MeshNode* n1,
5759 const SMDS_MeshNode* n2,
5760 SMESH_MesherHelper& helper)
5762 double u1 = helper.GetNodeU( edge, n1, n2 );
5763 double u2 = helper.GetNodeU( edge, n2, n1 );
5764 return u1 < u2 ? 1. : -1.;
5768 //=======================================================================
5769 //function : ExtrusionSweep
5771 //=======================================================================
5773 SMESH_MeshEditor::PGroupIDs
5774 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5775 ExtrusParam& theParams,
5776 TTElemOfElemListMap& newElemsMap)
5780 setElemsFirst( theElemSets );
5781 myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5782 myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5784 // source elements for each generated one
5785 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5786 srcElems.reserve( theElemSets[0].size() );
5787 srcNodes.reserve( theElemSets[1].size() );
5789 const int nbSteps = theParams.NbSteps();
5790 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5792 TNodeOfNodeListMap mapNewNodes;
5793 TElemOfVecOfNnlmiMap mapElemNewNodes;
5795 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5796 myMesh->NbFaces(ORDER_QUADRATIC) +
5797 myMesh->NbVolumes(ORDER_QUADRATIC) );
5799 TIDSortedElemSet::iterator itElem;
5800 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5802 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5803 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5805 // check element type
5806 const SMDS_MeshElement* elem = *itElem;
5807 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5810 const size_t nbNodes = elem->NbNodes();
5811 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5812 newNodesItVec.reserve( nbNodes );
5814 // loop on elem nodes
5815 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
5816 while ( itN->more() )
5818 // check if a node has been already sweeped
5819 const SMDS_MeshNode* node = itN->next();
5820 TNodeOfNodeListMap::iterator nIt =
5821 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5822 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5823 if ( listNewNodes.empty() )
5827 // check if we are to create medium nodes between corner ones
5828 bool needMediumNodes = false;
5829 if ( isQuadraticMesh )
5831 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5832 while (it->more() && !needMediumNodes )
5834 const SMDS_MeshElement* invElem = it->next();
5835 if ( invElem != elem && !theElems.count( invElem )) continue;
5836 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5837 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5838 needMediumNodes = true;
5841 // create nodes for all steps
5842 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5844 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5845 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5847 myLastCreatedNodes.push_back( *newNodesIt );
5848 srcNodes.push_back( node );
5853 break; // newNodesItVec will be shorter than nbNodes
5856 newNodesItVec.push_back( nIt );
5858 // make new elements
5859 if ( newNodesItVec.size() == nbNodes )
5860 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5864 if ( theParams.ToMakeBoundary() ) {
5865 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5867 PGroupIDs newGroupIDs;
5868 if ( theParams.ToMakeGroups() )
5869 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5874 //=======================================================================
5875 //function : ExtrusionAlongTrack
5877 //=======================================================================
5878 SMESH_MeshEditor::Extrusion_Error
5879 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5880 SMESH_Mesh* theTrackMesh,
5881 SMDS_ElemIteratorPtr theTrackIterator,
5882 const SMDS_MeshNode* theN1,
5883 std::list<double>& theAngles,
5884 const bool theAngleVariation,
5885 std::list<double>& theScales,
5886 const bool theScaleVariation,
5887 const gp_Pnt* theRefPoint,
5888 const bool theMakeGroups)
5893 if ( theElements[0].empty() && theElements[1].empty() )
5894 return EXTR_NO_ELEMENTS;
5896 ASSERT( theTrackMesh );
5897 if ( ! theTrackIterator || !theTrackIterator->more() )
5898 return EXTR_NO_ELEMENTS;
5900 // 2. Get ordered nodes
5901 SMESH_MeshAlgos::TElemGroupVector branchEdges;
5902 SMESH_MeshAlgos::TNodeGroupVector branchNods;
5903 SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
5904 if ( branchEdges.empty() )
5905 return EXTR_PATH_NOT_EDGE;
5907 if ( branchEdges.size() > 1 )
5908 return EXTR_BAD_PATH_SHAPE;
5910 std::vector< const SMDS_MeshNode* >& pathNodes = branchNods[0];
5911 std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
5912 if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
5913 return EXTR_BAD_STARTING_NODE;
5915 if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
5917 // add medium nodes to pathNodes
5918 std::vector< const SMDS_MeshNode* > pathNodes2;
5919 std::vector< const SMDS_MeshElement* > pathEdges2;
5920 pathNodes2.reserve( pathNodes.size() * 2 );
5921 pathEdges2.reserve( pathEdges.size() * 2 );
5922 for ( size_t i = 0; i < pathEdges.size(); ++i )
5924 pathNodes2.push_back( pathNodes[i] );
5925 pathEdges2.push_back( pathEdges[i] );
5926 if ( pathEdges[i]->IsQuadratic() )
5928 pathNodes2.push_back( pathEdges[i]->GetNode(2) );
5929 pathEdges2.push_back( pathEdges[i] );
5932 pathNodes2.push_back( pathNodes.back() );
5933 pathEdges.swap( pathEdges2 );
5934 pathNodes.swap( pathNodes2 );
5937 // 3. Get path data at pathNodes
5939 std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
5941 if ( theAngleVariation )
5942 linearAngleVariation( points.size()-1, theAngles );
5943 if ( theScaleVariation )
5944 linearScaleVariation( points.size()-1, theScales );
5946 theAngles.push_front( 0 ); // for the 1st point that is not transformed
5947 std::list<double>::iterator angle = theAngles.begin();
5949 SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
5951 std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
5952 std::map< int, double >::iterator id2factor;
5953 SMESH_MesherHelper pathHelper( *theTrackMesh );
5954 gp_Pnt p; gp_Vec tangent;
5955 const double tol2 = gp::Resolution() * gp::Resolution();
5957 for ( size_t i = 0; i < pathNodes.size(); ++i )
5959 ExtrusParam::PathPoint & point = points[ i ];
5961 point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
5963 if ( angle != theAngles.end() )
5964 point.myAngle = *angle++;
5966 tangent.SetCoord( 0,0,0 );
5967 const int shapeID = pathNodes[ i ]->GetShapeID();
5968 const TopoDS_Shape& shape = pathMeshDS->IndexToShape( shapeID );
5969 TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
5970 switch ( shapeType )
5974 TopoDS_Edge edge = TopoDS::Edge( shape );
5975 id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
5976 if ( id2factor->second == 0 )
5978 if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
5979 else id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
5981 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
5982 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
5983 curve->D1( u, p, tangent );
5984 tangent *= id2factor->second;
5990 PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
5991 while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
5993 int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
5994 for ( int di = -1; di <= 0; ++di )
5997 if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
5999 TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6000 id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6001 if ( id2factor->second == 0 )
6004 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6006 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6008 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6009 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6011 curve->D1( u, p, du );
6012 double size2 = du.SquareMagnitude();
6013 if ( du.SquareMagnitude() > tol2 )
6015 tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6027 for ( int di = -1; di <= 1; di += 2 )
6030 if ( j < pathNodes.size() )
6032 gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6033 double size2 = dir.SquareMagnitude();
6035 tangent += dir.Divided( Sqrt( size2 )) * di;
6039 } // switch ( shapeType )
6041 if ( tangent.SquareMagnitude() < tol2 )
6042 return EXTR_CANT_GET_TANGENT;
6044 point.myTgt = tangent;
6046 } // loop on pathNodes
6049 ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6050 TTElemOfElemListMap newElemsMap;
6052 ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6057 //=======================================================================
6058 //function : linearAngleVariation
6059 //purpose : spread values over nbSteps
6060 //=======================================================================
6062 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6063 list<double>& Angles)
6065 int nbAngles = Angles.size();
6066 if( nbSteps > nbAngles && nbAngles > 0 )
6068 vector<double> theAngles(nbAngles);
6069 theAngles.assign( Angles.begin(), Angles.end() );
6072 double rAn2St = double( nbAngles ) / double( nbSteps );
6073 double angPrev = 0, angle;
6074 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6076 double angCur = rAn2St * ( iSt+1 );
6077 double angCurFloor = floor( angCur );
6078 double angPrevFloor = floor( angPrev );
6079 if ( angPrevFloor == angCurFloor )
6080 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6082 int iP = int( angPrevFloor );
6083 double angPrevCeil = ceil(angPrev);
6084 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6086 int iC = int( angCurFloor );
6087 if ( iC < nbAngles )
6088 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6090 iP = int( angPrevCeil );
6092 angle += theAngles[ iC ];
6094 res.push_back(angle);
6101 //=======================================================================
6102 //function : linearScaleVariation
6103 //purpose : spread values over nbSteps
6104 //=======================================================================
6106 void SMESH_MeshEditor::linearScaleVariation(const int theNbSteps,
6107 std::list<double>& theScales)
6109 int nbScales = theScales.size();
6110 std::vector<double> myScales;
6111 myScales.reserve( theNbSteps );
6112 std::list<double>::const_iterator scale = theScales.begin();
6113 double prevScale = 1.0;
6114 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6116 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6117 int stDelta = Max( 1, iStep - myScales.size());
6118 double scDelta = ( *scale - prevScale ) / stDelta;
6119 for ( int iStep = 0; iStep < stDelta; ++iStep )
6121 myScales.push_back( prevScale + scDelta );
6122 prevScale = myScales.back();
6126 theScales.assign( myScales.begin(), myScales.end() );
6129 //================================================================================
6131 * \brief Move or copy theElements applying theTrsf to their nodes
6132 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6133 * \param theTrsf - transformation to apply
6134 * \param theCopy - if true, create translated copies of theElems
6135 * \param theMakeGroups - if true and theCopy, create translated groups
6136 * \param theTargetMesh - mesh to copy translated elements into
6137 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6139 //================================================================================
6141 SMESH_MeshEditor::PGroupIDs
6142 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6143 const gp_Trsf& theTrsf,
6145 const bool theMakeGroups,
6146 SMESH_Mesh* theTargetMesh)
6149 myLastCreatedElems.reserve( theElems.size() );
6151 bool needReverse = false;
6152 string groupPostfix;
6153 switch ( theTrsf.Form() ) {
6156 groupPostfix = "mirrored";
6159 groupPostfix = "mirrored";
6163 groupPostfix = "mirrored";
6166 groupPostfix = "rotated";
6168 case gp_Translation:
6169 groupPostfix = "translated";
6172 groupPostfix = "scaled";
6174 case gp_CompoundTrsf: // different scale by axis
6175 groupPostfix = "scaled";
6178 needReverse = false;
6179 groupPostfix = "transformed";
6182 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6183 SMESHDS_Mesh* aMesh = GetMeshDS();
6185 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6186 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6187 SMESH_MeshEditor::ElemFeatures elemType;
6189 // map old node to new one
6190 TNodeNodeMap nodeMap;
6192 // elements sharing moved nodes; those of them which have all
6193 // nodes mirrored but are not in theElems are to be reversed
6194 TIDSortedElemSet inverseElemSet;
6196 // source elements for each generated one
6197 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6199 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6200 TIDSortedElemSet orphanNode;
6202 if ( theElems.empty() ) // transform the whole mesh
6205 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6206 while ( eIt->more() ) theElems.insert( eIt->next() );
6208 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6209 while ( nIt->more() )
6211 const SMDS_MeshNode* node = nIt->next();
6212 if ( node->NbInverseElements() == 0)
6213 orphanNode.insert( node );
6217 // loop on elements to transform nodes : first orphan nodes then elems
6218 TIDSortedElemSet::iterator itElem;
6219 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6220 for (int i=0; i<2; i++)
6221 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6223 const SMDS_MeshElement* elem = *itElem;
6227 // loop on elem nodes
6229 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6230 while ( itN->more() )
6232 const SMDS_MeshNode* node = cast2Node( itN->next() );
6233 // check if a node has been already transformed
6234 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6235 nodeMap.insert( make_pair ( node, node ));
6236 if ( !n2n_isnew.second )
6239 node->GetXYZ( coord );
6240 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6241 if ( theTargetMesh ) {
6242 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6243 n2n_isnew.first->second = newNode;
6244 myLastCreatedNodes.push_back(newNode);
6245 srcNodes.push_back( node );
6247 else if ( theCopy ) {
6248 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6249 n2n_isnew.first->second = newNode;
6250 myLastCreatedNodes.push_back(newNode);
6251 srcNodes.push_back( node );
6254 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6255 // node position on shape becomes invalid
6256 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6257 ( SMDS_SpacePosition::originSpacePosition() );
6260 // keep inverse elements
6261 if ( !theCopy && !theTargetMesh && needReverse ) {
6262 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6263 while ( invElemIt->more() ) {
6264 const SMDS_MeshElement* iel = invElemIt->next();
6265 inverseElemSet.insert( iel );
6269 } // loop on elems in { &orphanNode, &theElems };
6271 // either create new elements or reverse mirrored ones
6272 if ( !theCopy && !needReverse && !theTargetMesh )
6275 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6277 // Replicate or reverse elements
6279 std::vector<int> iForw;
6280 vector<const SMDS_MeshNode*> nodes;
6281 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6283 const SMDS_MeshElement* elem = *itElem;
6284 if ( !elem ) continue;
6286 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6287 size_t nbNodes = elem->NbNodes();
6288 if ( geomType == SMDSGeom_NONE ) continue; // node
6290 nodes.resize( nbNodes );
6292 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6294 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6298 bool allTransformed = true;
6299 int nbFaces = aPolyedre->NbFaces();
6300 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6302 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6303 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6305 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6306 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6307 if ( nodeMapIt == nodeMap.end() )
6308 allTransformed = false; // not all nodes transformed
6310 nodes.push_back((*nodeMapIt).second);
6312 if ( needReverse && allTransformed )
6313 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6315 if ( !allTransformed )
6316 continue; // not all nodes transformed
6318 else // ----------------------- the rest element types
6320 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6321 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6322 const vector<int>& i = needReverse ? iRev : iForw;
6324 // find transformed nodes
6326 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6327 while ( itN->more() ) {
6328 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6329 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6330 if ( nodeMapIt == nodeMap.end() )
6331 break; // not all nodes transformed
6332 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6334 if ( iNode != nbNodes )
6335 continue; // not all nodes transformed
6339 // copy in this or a new mesh
6340 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6341 srcElems.push_back( elem );
6344 // reverse element as it was reversed by transformation
6346 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6349 } // loop on elements
6351 if ( editor && editor != this )
6352 myLastCreatedElems.swap( editor->myLastCreatedElems );
6354 PGroupIDs newGroupIDs;
6356 if ( ( theMakeGroups && theCopy ) ||
6357 ( theMakeGroups && theTargetMesh ) )
6358 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6363 //================================================================================
6365 * \brief Make an offset mesh from a source 2D mesh
6366 * \param [in] theElements - source faces
6367 * \param [in] theValue - offset value
6368 * \param [out] theTgtMesh - a mesh to add offset elements to
6369 * \param [in] theMakeGroups - to generate groups
6370 * \return PGroupIDs - IDs of created groups. NULL means failure
6372 //================================================================================
6374 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6375 const double theValue,
6376 SMESH_Mesh* theTgtMesh,
6377 const bool theMakeGroups,
6378 const bool theCopyElements,
6379 const bool theFixSelfIntersection)
6381 SMESHDS_Mesh* meshDS = GetMeshDS();
6382 SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6383 SMESH_MeshEditor tgtEditor( theTgtMesh );
6385 SMDS_ElemIteratorPtr eIt;
6386 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6387 else eIt = SMESHUtils::elemSetIterator( theElements );
6389 SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6390 SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6391 std::unique_ptr< SMDS_Mesh > offsetMesh
6392 ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6393 theFixSelfIntersection,
6394 new2OldFaces, new2OldNodes ));
6395 if ( offsetMesh->NbElements() == 0 )
6396 return PGroupIDs(); // MakeOffset() failed
6399 if ( theTgtMesh == myMesh && !theCopyElements )
6401 // clear the source elements
6402 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6403 else eIt = SMESHUtils::elemSetIterator( theElements );
6404 while ( eIt->more() )
6405 meshDS->RemoveFreeElement( eIt->next(), 0 );
6408 // offsetMesh->Modified();
6409 // offsetMesh->CompactMesh(); // make IDs start from 1
6411 // source elements for each generated one
6412 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6413 srcElems.reserve( new2OldFaces.size() );
6414 srcNodes.reserve( new2OldNodes.size() );
6417 myLastCreatedElems.reserve( new2OldFaces.size() );
6418 myLastCreatedNodes.reserve( new2OldNodes.size() );
6420 // copy offsetMesh to theTgtMesh
6422 int idShift = meshDS->MaxNodeID();
6423 for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6424 if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6427 if ( n->NbInverseElements() > 0 )
6430 const SMDS_MeshNode* n2 =
6431 tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6432 myLastCreatedNodes.push_back( n2 );
6433 srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6437 ElemFeatures elemType;
6438 for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6439 if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6442 elemType.myNodes.clear();
6443 for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6445 const SMDS_MeshNode* n2 = nIt->next();
6446 elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6448 tgtEditor.AddElement( elemType.myNodes, elemType );
6449 srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6452 myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6454 PGroupIDs newGroupIDs;
6455 if ( theMakeGroups )
6456 newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6458 newGroupIDs.reset( new std::list< int > );
6463 //=======================================================================
6465 * \brief Create groups of elements made during transformation
6466 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6467 * \param elemGens - elements making corresponding myLastCreatedElems
6468 * \param postfix - to push_back to names of new groups
6469 * \param targetMesh - mesh to create groups in
6470 * \param topPresent - is there are "top" elements that are created by sweeping
6472 //=======================================================================
6474 SMESH_MeshEditor::PGroupIDs
6475 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6476 const SMESH_SequenceOfElemPtr& elemGens,
6477 const std::string& postfix,
6478 SMESH_Mesh* targetMesh,
6479 const bool topPresent)
6481 PGroupIDs newGroupIDs( new list<int> );
6482 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6484 // Sort existing groups by types and collect their names
6486 // containers to store an old group and generated new ones;
6487 // 1st new group is for result elems of different type than a source one;
6488 // 2nd new group is for same type result elems ("top" group at extrusion)
6490 using boost::make_tuple;
6491 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6492 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6493 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6495 set< string > groupNames;
6497 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6498 if ( !groupIt->more() ) return newGroupIDs;
6500 int newGroupID = mesh->GetGroupIds().back()+1;
6501 while ( groupIt->more() )
6503 SMESH_Group * group = groupIt->next();
6504 if ( !group ) continue;
6505 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6506 if ( !groupDS || groupDS->IsEmpty() ) continue;
6507 groupNames.insert ( group->GetName() );
6508 groupDS->SetStoreName( group->GetName() );
6509 const SMDSAbs_ElementType type = groupDS->GetType();
6510 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6511 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6512 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6513 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6516 // Loop on nodes and elements to add them in new groups
6518 vector< const SMDS_MeshElement* > resultElems;
6519 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6521 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6522 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6523 if ( gens.size() != elems.size() )
6524 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6526 // loop on created elements
6527 for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6529 const SMDS_MeshElement* sourceElem = gens[ iElem ];
6530 if ( !sourceElem ) {
6531 MESSAGE("generateGroups(): NULL source element");
6534 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6535 if ( groupsOldNew.empty() ) { // no groups of this type at all
6536 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6537 ++iElem; // skip all elements made by sourceElem
6540 // collect all elements made by the iElem-th sourceElem
6541 resultElems.clear();
6542 if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6543 if ( resElem != sourceElem )
6544 resultElems.push_back( resElem );
6545 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6546 if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6547 if ( resElem != sourceElem )
6548 resultElems.push_back( resElem );
6550 const SMDS_MeshElement* topElem = 0;
6551 if ( isNodes ) // there must be a top element
6553 topElem = resultElems.back();
6554 resultElems.pop_back();
6558 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6559 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6560 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6562 topElem = *resElemIt;
6563 *resElemIt = 0; // erase *resElemIt
6567 // add resultElems to groups originted from ones the sourceElem belongs to
6568 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6569 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6571 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6572 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6574 // fill in a new group
6575 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6576 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6577 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6579 newGroup.Add( *resElemIt );
6581 // fill a "top" group
6584 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6585 newTopGroup.Add( topElem );
6589 } // loop on created elements
6590 }// loop on nodes and elements
6592 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6594 list<int> topGrouIds;
6595 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6597 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6598 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6599 orderedOldNewGroups[i]->get<2>() };
6600 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6602 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6603 if ( newGroupDS->IsEmpty() )
6605 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6610 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6613 const bool isTop = ( topPresent &&
6614 newGroupDS->GetType() == oldGroupDS->GetType() &&
6617 string name = oldGroupDS->GetStoreName();
6618 { // remove trailing whitespaces (issue 22599)
6619 size_t size = name.size();
6620 while ( size > 1 && isspace( name[ size-1 ]))
6622 if ( size != name.size() )
6624 name.resize( size );
6625 oldGroupDS->SetStoreName( name.c_str() );
6628 if ( !targetMesh ) {
6629 string suffix = ( isTop ? "top": postfix.c_str() );
6633 while ( !groupNames.insert( name ).second ) // name exists
6634 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6639 newGroupDS->SetStoreName( name.c_str() );
6641 // make a SMESH_Groups
6642 mesh->AddGroup( newGroupDS );
6644 topGrouIds.push_back( newGroupDS->GetID() );
6646 newGroupIDs->push_back( newGroupDS->GetID() );
6650 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6655 //================================================================================
6657 * * \brief Return list of group of nodes close to each other within theTolerance
6658 * * Search among theNodes or in the whole mesh if theNodes is empty using
6659 * * an Octree algorithm
6660 * \param [in,out] theNodes - the nodes to treat
6661 * \param [in] theTolerance - the tolerance
6662 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
6663 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
6664 * corner and medium nodes in separate groups
6666 //================================================================================
6668 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6669 const double theTolerance,
6670 TListOfListOfNodes & theGroupsOfNodes,
6671 bool theSeparateCornersAndMedium)
6675 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
6676 myMesh->NbFaces ( ORDER_QUADRATIC ) +
6677 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
6678 theSeparateCornersAndMedium = false;
6680 TIDSortedNodeSet& corners = theNodes;
6681 TIDSortedNodeSet medium;
6683 if ( theNodes.empty() ) // get all nodes in the mesh
6685 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
6686 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
6687 if ( theSeparateCornersAndMedium )
6688 while ( nIt->more() )
6690 const SMDS_MeshNode* n = nIt->next();
6691 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
6692 nodeSet->insert( nodeSet->end(), n );
6695 while ( nIt->more() )
6696 theNodes.insert( theNodes.end(), nIt->next() );
6698 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
6700 TIDSortedNodeSet::iterator nIt = corners.begin();
6701 while ( nIt != corners.end() )
6702 if ( SMESH_MesherHelper::IsMedium( *nIt ))
6704 medium.insert( medium.end(), *nIt );
6705 corners.erase( nIt++ );
6713 if ( !corners.empty() )
6714 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
6715 if ( !medium.empty() )
6716 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
6719 //=======================================================================
6720 //function : SimplifyFace
6721 //purpose : split a chain of nodes into several closed chains
6722 //=======================================================================
6724 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6725 vector<const SMDS_MeshNode *>& poly_nodes,
6726 vector<int>& quantities) const
6728 int nbNodes = faceNodes.size();
6729 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
6733 size_t prevNbQuant = quantities.size();
6735 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
6736 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
6737 map< const SMDS_MeshNode*, int >::iterator nInd;
6739 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
6740 simpleNodes.push_back( faceNodes[0] );
6741 for ( int iCur = 1; iCur < nbNodes; iCur++ )
6743 if ( faceNodes[ iCur ] != simpleNodes.back() )
6745 int index = simpleNodes.size();
6746 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
6747 int prevIndex = nInd->second;
6748 if ( prevIndex < index )
6751 int loopLen = index - prevIndex;
6754 // store the sub-loop
6755 quantities.push_back( loopLen );
6756 for ( int i = prevIndex; i < index; i++ )
6757 poly_nodes.push_back( simpleNodes[ i ]);
6759 simpleNodes.resize( prevIndex+1 );
6763 simpleNodes.push_back( faceNodes[ iCur ]);
6768 if ( simpleNodes.size() > 2 )
6770 quantities.push_back( simpleNodes.size() );
6771 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
6774 return quantities.size() - prevNbQuant;
6777 //=======================================================================
6778 //function : MergeNodes
6779 //purpose : In each group, the cdr of nodes are substituted by the first one
6781 //=======================================================================
6783 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
6784 const bool theAvoidMakingHoles)
6788 SMESHDS_Mesh* mesh = GetMeshDS();
6790 TNodeNodeMap nodeNodeMap; // node to replace - new node
6791 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6792 list< int > rmElemIds, rmNodeIds;
6793 vector< ElemFeatures > newElemDefs;
6795 // Fill nodeNodeMap and elems
6797 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6798 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
6800 list<const SMDS_MeshNode*>& nodes = *grIt;
6801 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6802 const SMDS_MeshNode* nToKeep = *nIt;
6803 for ( ++nIt; nIt != nodes.end(); nIt++ )
6805 const SMDS_MeshNode* nToRemove = *nIt;
6806 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
6807 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6808 while ( invElemIt->more() ) {
6809 const SMDS_MeshElement* elem = invElemIt->next();
6815 // Apply recursive replacements (BUG 0020185)
6816 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
6817 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
6819 const SMDS_MeshNode* nToKeep = nnIt->second;
6820 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
6821 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
6823 nToKeep = nnIt_i->second;
6824 nnIt->second = nToKeep;
6825 nnIt_i = nodeNodeMap.find( nToKeep );
6829 if ( theAvoidMakingHoles )
6831 // find elements whose topology changes
6833 vector<const SMDS_MeshElement*> pbElems;
6834 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6835 for ( ; eIt != elems.end(); ++eIt )
6837 const SMDS_MeshElement* elem = *eIt;
6838 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6839 while ( itN->more() )
6841 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6842 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6843 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
6845 // several nodes of elem stick
6846 pbElems.push_back( elem );
6851 // exclude from merge nodes causing spoiling element
6852 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
6854 bool nodesExcluded = false;
6855 for ( size_t i = 0; i < pbElems.size(); ++i )
6857 size_t prevNbMergeNodes = nodeNodeMap.size();
6858 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
6859 prevNbMergeNodes < nodeNodeMap.size() )
6860 nodesExcluded = true;
6862 if ( !nodesExcluded )
6867 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
6869 const SMDS_MeshNode* nToRemove = nnIt->first;
6870 const SMDS_MeshNode* nToKeep = nnIt->second;
6871 if ( nToRemove != nToKeep )
6873 rmNodeIds.push_back( nToRemove->GetID() );
6874 AddToSameGroups( nToKeep, nToRemove, mesh );
6875 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
6876 // w/o creating node in place of merged ones.
6877 SMDS_PositionPtr pos = nToRemove->GetPosition();
6878 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6879 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6880 sm->SetIsAlwaysComputed( true );
6884 // Change element nodes or remove an element
6886 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6887 for ( ; eIt != elems.end(); eIt++ )
6889 const SMDS_MeshElement* elem = *eIt;
6890 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
6892 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
6894 rmElemIds.push_back( elem->GetID() );
6896 for ( size_t i = 0; i < newElemDefs.size(); ++i )
6898 if ( i > 0 || !mesh->ChangeElementNodes( elem,
6899 & newElemDefs[i].myNodes[0],
6900 newElemDefs[i].myNodes.size() ))
6904 newElemDefs[i].SetID( elem->GetID() );
6905 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6906 if ( !keepElem ) rmElemIds.pop_back();
6910 newElemDefs[i].SetID( -1 );
6912 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
6913 if ( sm && newElem )
6914 sm->AddElement( newElem );
6915 if ( elem != newElem )
6916 ReplaceElemInGroups( elem, newElem, mesh );
6921 // Remove bad elements, then equal nodes (order important)
6922 Remove( rmElemIds, /*isNodes=*/false );
6923 Remove( rmNodeIds, /*isNodes=*/true );
6928 //=======================================================================
6929 //function : applyMerge
6930 //purpose : Compute new connectivity of an element after merging nodes
6931 // \param [in] elems - the element
6932 // \param [out] newElemDefs - definition(s) of result element(s)
6933 // \param [inout] nodeNodeMap - nodes to merge
6934 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
6935 // after merging (but not degenerated), removes nodes causing
6936 // the invalidity from \a nodeNodeMap.
6937 // \return bool - true if the element should be removed
6938 //=======================================================================
6940 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
6941 vector< ElemFeatures >& newElemDefs,
6942 TNodeNodeMap& nodeNodeMap,
6943 const bool avoidMakingHoles )
6945 bool toRemove = false; // to remove elem
6946 int nbResElems = 1; // nb new elements
6948 newElemDefs.resize(nbResElems);
6949 newElemDefs[0].Init( elem );
6950 newElemDefs[0].myNodes.clear();
6952 set<const SMDS_MeshNode*> nodeSet;
6953 vector< const SMDS_MeshNode*> curNodes;
6954 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
6957 const int nbNodes = elem->NbNodes();
6958 SMDSAbs_EntityType entity = elem->GetEntityType();
6960 curNodes.resize( nbNodes );
6961 uniqueNodes.resize( nbNodes );
6962 iRepl.resize( nbNodes );
6963 int iUnique = 0, iCur = 0, nbRepl = 0;
6965 // Get new seq of nodes
6967 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6968 while ( itN->more() )
6970 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6972 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6973 if ( nnIt != nodeNodeMap.end() ) {
6976 curNodes[ iCur ] = n;
6977 bool isUnique = nodeSet.insert( n ).second;
6979 uniqueNodes[ iUnique++ ] = n;
6981 iRepl[ nbRepl++ ] = iCur;
6985 // Analyse element topology after replacement
6987 int nbUniqueNodes = nodeSet.size();
6988 if ( nbNodes != nbUniqueNodes ) // some nodes stick
6993 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
6995 // if corner nodes stick, remove medium nodes between them from uniqueNodes
6996 int nbCorners = nbNodes / 2;
6997 for ( int iCur = 0; iCur < nbCorners; ++iCur )
6999 int iNext = ( iCur + 1 ) % nbCorners;
7000 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7002 int iMedium = iCur + nbCorners;
7003 vector< const SMDS_MeshNode* >::iterator i =
7004 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7006 curNodes[ iMedium ]);
7007 if ( i != uniqueNodes.end() )
7010 for ( ; i+1 != uniqueNodes.end(); ++i )
7019 case SMDSEntity_Polygon:
7020 case SMDSEntity_Quad_Polygon: // Polygon
7022 ElemFeatures* elemType = & newElemDefs[0];
7023 const bool isQuad = elemType->myIsQuad;
7025 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7026 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7028 // a polygon can divide into several elements
7029 vector<const SMDS_MeshNode *> polygons_nodes;
7030 vector<int> quantities;
7031 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7032 newElemDefs.resize( nbResElems );
7033 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7035 ElemFeatures* elemType = & newElemDefs[iface];
7036 if ( iface ) elemType->Init( elem );
7038 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7039 int nbNewNodes = quantities[iface];
7040 face_nodes.assign( polygons_nodes.begin() + inode,
7041 polygons_nodes.begin() + inode + nbNewNodes );
7042 inode += nbNewNodes;
7043 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7045 bool isValid = ( nbNewNodes % 2 == 0 );
7046 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7047 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7048 elemType->SetQuad( isValid );
7049 if ( isValid ) // put medium nodes after corners
7050 SMDS_MeshCell::applyInterlaceRev
7051 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7052 nbNewNodes ), face_nodes );
7054 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7056 nbUniqueNodes = newElemDefs[0].myNodes.size();
7060 case SMDSEntity_Polyhedra: // Polyhedral volume
7062 if ( nbUniqueNodes >= 4 )
7064 // each face has to be analyzed in order to check volume validity
7065 if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7067 int nbFaces = aPolyedre->NbFaces();
7069 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7070 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7071 vector<const SMDS_MeshNode *> faceNodes;
7075 for (int iface = 1; iface <= nbFaces; iface++)
7077 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7078 faceNodes.resize( nbFaceNodes );
7079 for (int inode = 1; inode <= nbFaceNodes; inode++)
7081 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7082 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7083 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7084 faceNode = (*nnIt).second;
7085 faceNodes[inode - 1] = faceNode;
7087 SimplifyFace(faceNodes, poly_nodes, quantities);
7090 if ( quantities.size() > 3 )
7092 // TODO: remove coincident faces
7094 nbUniqueNodes = newElemDefs[0].myNodes.size();
7102 // TODO not all the possible cases are solved. Find something more generic?
7103 case SMDSEntity_Edge: //////// EDGE
7104 case SMDSEntity_Triangle: //// TRIANGLE
7105 case SMDSEntity_Quad_Triangle:
7106 case SMDSEntity_Tetra:
7107 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7111 case SMDSEntity_Quad_Edge:
7115 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7117 if ( nbUniqueNodes < 3 )
7119 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7120 toRemove = true; // opposite nodes stick
7125 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7134 if ( nbUniqueNodes == 6 &&
7136 ( nbRepl == 1 || iRepl[1] >= 4 ))
7142 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7151 if ( nbUniqueNodes == 7 &&
7153 ( nbRepl == 1 || iRepl[1] != 8 ))
7159 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7161 if ( nbUniqueNodes == 4 ) {
7162 // ---------------------------------> tetrahedron
7163 if ( curNodes[3] == curNodes[4] &&
7164 curNodes[3] == curNodes[5] ) {
7168 else if ( curNodes[0] == curNodes[1] &&
7169 curNodes[0] == curNodes[2] ) {
7170 // bottom nodes stick: set a top before
7171 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7172 uniqueNodes[ 0 ] = curNodes [ 5 ];
7173 uniqueNodes[ 1 ] = curNodes [ 4 ];
7174 uniqueNodes[ 2 ] = curNodes [ 3 ];
7177 else if (( curNodes[0] == curNodes[3] ) +
7178 ( curNodes[1] == curNodes[4] ) +
7179 ( curNodes[2] == curNodes[5] ) == 2 ) {
7180 // a lateral face turns into a line
7184 else if ( nbUniqueNodes == 5 ) {
7185 // PENTAHEDRON --------------------> pyramid
7186 if ( curNodes[0] == curNodes[3] )
7188 uniqueNodes[ 0 ] = curNodes[ 1 ];
7189 uniqueNodes[ 1 ] = curNodes[ 4 ];
7190 uniqueNodes[ 2 ] = curNodes[ 5 ];
7191 uniqueNodes[ 3 ] = curNodes[ 2 ];
7192 uniqueNodes[ 4 ] = curNodes[ 0 ];
7195 if ( curNodes[1] == curNodes[4] )
7197 uniqueNodes[ 0 ] = curNodes[ 0 ];
7198 uniqueNodes[ 1 ] = curNodes[ 2 ];
7199 uniqueNodes[ 2 ] = curNodes[ 5 ];
7200 uniqueNodes[ 3 ] = curNodes[ 3 ];
7201 uniqueNodes[ 4 ] = curNodes[ 1 ];
7204 if ( curNodes[2] == curNodes[5] )
7206 uniqueNodes[ 0 ] = curNodes[ 0 ];
7207 uniqueNodes[ 1 ] = curNodes[ 3 ];
7208 uniqueNodes[ 2 ] = curNodes[ 4 ];
7209 uniqueNodes[ 3 ] = curNodes[ 1 ];
7210 uniqueNodes[ 4 ] = curNodes[ 2 ];
7216 case SMDSEntity_Hexa:
7218 //////////////////////////////////// HEXAHEDRON
7219 SMDS_VolumeTool hexa (elem);
7220 hexa.SetExternalNormal();
7221 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7222 //////////////////////// HEX ---> tetrahedron
7223 for ( int iFace = 0; iFace < 6; iFace++ ) {
7224 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7225 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7226 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7227 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7228 // one face turns into a point ...
7229 int pickInd = ind[ 0 ];
7230 int iOppFace = hexa.GetOppFaceIndex( iFace );
7231 ind = hexa.GetFaceNodesIndices( iOppFace );
7233 uniqueNodes.clear();
7234 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7235 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7238 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7240 if ( nbStick == 1 ) {
7241 // ... and the opposite one - into a triangle.
7243 uniqueNodes.push_back( curNodes[ pickInd ]);
7250 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7251 //////////////////////// HEX ---> prism
7252 int nbTria = 0, iTria[3];
7253 const int *ind; // indices of face nodes
7254 // look for triangular faces
7255 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7256 ind = hexa.GetFaceNodesIndices( iFace );
7257 TIDSortedNodeSet faceNodes;
7258 for ( iCur = 0; iCur < 4; iCur++ )
7259 faceNodes.insert( curNodes[ind[iCur]] );
7260 if ( faceNodes.size() == 3 )
7261 iTria[ nbTria++ ] = iFace;
7263 // check if triangles are opposite
7264 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7266 // set nodes of the bottom triangle
7267 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7269 for ( iCur = 0; iCur < 4; iCur++ )
7270 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7271 indB.push_back( ind[iCur] );
7272 if ( !hexa.IsForward() )
7273 std::swap( indB[0], indB[2] );
7274 for ( iCur = 0; iCur < 3; iCur++ )
7275 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7276 // set nodes of the top triangle
7277 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7278 for ( iCur = 0; iCur < 3; ++iCur )
7279 for ( int j = 0; j < 4; ++j )
7280 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7282 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7289 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7290 //////////////////// HEXAHEDRON ---> pyramid
7291 for ( int iFace = 0; iFace < 6; iFace++ ) {
7292 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7293 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7294 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7295 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7296 // one face turns into a point ...
7297 int iOppFace = hexa.GetOppFaceIndex( iFace );
7298 ind = hexa.GetFaceNodesIndices( iOppFace );
7299 uniqueNodes.clear();
7300 for ( iCur = 0; iCur < 4; iCur++ ) {
7301 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7304 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7306 if ( uniqueNodes.size() == 4 ) {
7307 // ... and the opposite one is a quadrangle
7309 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7310 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7318 if ( toRemove && nbUniqueNodes > 4 ) {
7319 ////////////////// HEXAHEDRON ---> polyhedron
7320 hexa.SetExternalNormal();
7321 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7322 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7323 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7324 quantities.reserve( 6 ); quantities.clear();
7325 for ( int iFace = 0; iFace < 6; iFace++ )
7327 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7328 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7329 curNodes[ind[1]] == curNodes[ind[3]] )
7332 break; // opposite nodes stick
7335 for ( iCur = 0; iCur < 4; iCur++ )
7337 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7338 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7340 if ( nodeSet.size() < 3 )
7341 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7343 quantities.push_back( nodeSet.size() );
7345 if ( quantities.size() >= 4 )
7348 nbUniqueNodes = poly_nodes.size();
7349 newElemDefs[0].SetPoly(true);
7353 } // case HEXAHEDRON
7358 } // switch ( entity )
7360 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7362 // erase from nodeNodeMap nodes whose merge spoils elem
7363 vector< const SMDS_MeshNode* > noMergeNodes;
7364 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7365 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7366 nodeNodeMap.erase( noMergeNodes[i] );
7369 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7371 uniqueNodes.resize( nbUniqueNodes );
7373 if ( !toRemove && nbResElems == 0 )
7376 newElemDefs.resize( nbResElems );
7382 // ========================================================
7383 // class : ComparableElement
7384 // purpose : allow comparing elements basing on their nodes
7385 // ========================================================
7387 class ComparableElement : public boost::container::flat_set< int >
7389 typedef boost::container::flat_set< int > int_set;
7391 const SMDS_MeshElement* myElem;
7393 mutable int myGroupID;
7397 ComparableElement( const SMDS_MeshElement* theElem ):
7398 myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7400 this->reserve( theElem->NbNodes() );
7401 for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7403 int id = nodeIt->next()->GetID();
7409 const SMDS_MeshElement* GetElem() const { return myElem; }
7411 int& GroupID() const { return myGroupID; }
7412 //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7414 ComparableElement( const ComparableElement& theSource ) // move copy
7416 ComparableElement& src = const_cast< ComparableElement& >( theSource );
7417 (int_set&) (*this ) = boost::move( src );
7418 myElem = src.myElem;
7419 mySumID = src.mySumID;
7420 myGroupID = src.myGroupID;
7423 static int HashCode(const ComparableElement& se, int limit )
7425 return ::HashCode( se.mySumID, limit );
7427 static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7429 return ( se1 == se2 );
7434 //=======================================================================
7435 //function : FindEqualElements
7436 //purpose : Return list of group of elements built on the same nodes.
7437 // Search among theElements or in the whole mesh if theElements is empty
7438 //=======================================================================
7440 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet & theElements,
7441 TListOfListOfElementsID & theGroupsOfElementsID )
7445 SMDS_ElemIteratorPtr elemIt;
7446 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7447 else elemIt = SMESHUtils::elemSetIterator( theElements );
7449 typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7450 typedef std::list<int> TGroupOfElems;
7451 TMapOfElements mapOfElements;
7452 std::vector< TGroupOfElems > arrayOfGroups;
7453 TGroupOfElems groupOfElems;
7455 while ( elemIt->more() )
7457 const SMDS_MeshElement* curElem = elemIt->next();
7458 ComparableElement compElem = curElem;
7460 const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7461 if ( elemInSet.GetElem() != curElem ) // coincident elem
7463 int& iG = elemInSet.GroupID();
7466 iG = arrayOfGroups.size();
7467 arrayOfGroups.push_back( groupOfElems );
7468 arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7470 arrayOfGroups[ iG ].push_back( curElem->GetID() );
7474 groupOfElems.clear();
7475 std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7476 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7478 if ( groupIt->size() > 1 ) {
7479 //groupOfElems.sort(); -- theElements are sorted already
7480 theGroupsOfElementsID.emplace_back( *groupIt );
7485 //=======================================================================
7486 //function : MergeElements
7487 //purpose : In each given group, substitute all elements by the first one.
7488 //=======================================================================
7490 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7494 typedef list<int> TListOfIDs;
7495 TListOfIDs rmElemIds; // IDs of elems to remove
7497 SMESHDS_Mesh* aMesh = GetMeshDS();
7499 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7500 while ( groupsIt != theGroupsOfElementsID.end() ) {
7501 TListOfIDs& aGroupOfElemID = *groupsIt;
7502 aGroupOfElemID.sort();
7503 int elemIDToKeep = aGroupOfElemID.front();
7504 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7505 aGroupOfElemID.pop_front();
7506 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7507 while ( idIt != aGroupOfElemID.end() ) {
7508 int elemIDToRemove = *idIt;
7509 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7510 // add the kept element in groups of removed one (PAL15188)
7511 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7512 rmElemIds.push_back( elemIDToRemove );
7518 Remove( rmElemIds, false );
7521 //=======================================================================
7522 //function : MergeEqualElements
7523 //purpose : Remove all but one of elements built on the same nodes.
7524 //=======================================================================
7526 void SMESH_MeshEditor::MergeEqualElements()
7528 TIDSortedElemSet aMeshElements; /* empty input ==
7529 to merge equal elements in the whole mesh */
7530 TListOfListOfElementsID aGroupsOfElementsID;
7531 FindEqualElements( aMeshElements, aGroupsOfElementsID );
7532 MergeElements( aGroupsOfElementsID );
7535 //=======================================================================
7536 //function : findAdjacentFace
7538 //=======================================================================
7540 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7541 const SMDS_MeshNode* n2,
7542 const SMDS_MeshElement* elem)
7544 TIDSortedElemSet elemSet, avoidSet;
7546 avoidSet.insert ( elem );
7547 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7550 //=======================================================================
7551 //function : findSegment
7552 //purpose : Return a mesh segment by two nodes one of which can be medium
7553 //=======================================================================
7555 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7556 const SMDS_MeshNode* n2)
7558 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7559 while ( it->more() )
7561 const SMDS_MeshElement* seg = it->next();
7562 if ( seg->GetNodeIndex( n2 ) >= 0 )
7568 //=======================================================================
7569 //function : FindFreeBorder
7571 //=======================================================================
7573 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7575 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7576 const SMDS_MeshNode* theSecondNode,
7577 const SMDS_MeshNode* theLastNode,
7578 list< const SMDS_MeshNode* > & theNodes,
7579 list< const SMDS_MeshElement* >& theFaces)
7581 if ( !theFirstNode || !theSecondNode )
7583 // find border face between theFirstNode and theSecondNode
7584 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7588 theFaces.push_back( curElem );
7589 theNodes.push_back( theFirstNode );
7590 theNodes.push_back( theSecondNode );
7592 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7593 //TIDSortedElemSet foundElems;
7594 bool needTheLast = ( theLastNode != 0 );
7596 vector<const SMDS_MeshNode*> nodes;
7598 while ( nStart != theLastNode ) {
7599 if ( nStart == theFirstNode )
7600 return !needTheLast;
7602 // find all free border faces sharing nStart
7604 list< const SMDS_MeshElement* > curElemList;
7605 list< const SMDS_MeshNode* > nStartList;
7606 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7607 while ( invElemIt->more() ) {
7608 const SMDS_MeshElement* e = invElemIt->next();
7609 //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
7612 nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7613 SMDS_MeshElement::iterator() );
7614 nodes.push_back( nodes[ 0 ]);
7617 int iNode = 0, nbNodes = nodes.size() - 1;
7618 for ( iNode = 0; iNode < nbNodes; iNode++ )
7619 if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7620 ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7621 ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
7623 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
7624 curElemList.push_back( e );
7628 // analyse the found
7630 int nbNewBorders = curElemList.size();
7631 if ( nbNewBorders == 0 ) {
7632 // no free border furthermore
7633 return !needTheLast;
7635 else if ( nbNewBorders == 1 ) {
7636 // one more element found
7638 nStart = nStartList.front();
7639 curElem = curElemList.front();
7640 theFaces.push_back( curElem );
7641 theNodes.push_back( nStart );
7644 // several continuations found
7645 list< const SMDS_MeshElement* >::iterator curElemIt;
7646 list< const SMDS_MeshNode* >::iterator nStartIt;
7647 // check if one of them reached the last node
7648 if ( needTheLast ) {
7649 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7650 curElemIt!= curElemList.end();
7651 curElemIt++, nStartIt++ )
7652 if ( *nStartIt == theLastNode ) {
7653 theFaces.push_back( *curElemIt );
7654 theNodes.push_back( *nStartIt );
7658 // find the best free border by the continuations
7659 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7660 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7661 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7662 curElemIt!= curElemList.end();
7663 curElemIt++, nStartIt++ )
7665 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7666 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7667 // find one more free border
7668 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7672 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7673 // choice: clear a worse one
7674 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7675 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7676 contNodes[ iWorse ].clear();
7677 contFaces[ iWorse ].clear();
7680 if ( contNodes[0].empty() && contNodes[1].empty() )
7683 // push_back the best free border
7684 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7685 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7686 //theNodes.pop_back(); // remove nIgnore
7687 theNodes.pop_back(); // remove nStart
7688 //theFaces.pop_back(); // remove curElem
7689 theNodes.splice( theNodes.end(), *cNL );
7690 theFaces.splice( theFaces.end(), *cFL );
7693 } // several continuations found
7694 } // while ( nStart != theLastNode )
7699 //=======================================================================
7700 //function : CheckFreeBorderNodes
7701 //purpose : Return true if the tree nodes are on a free border
7702 //=======================================================================
7704 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7705 const SMDS_MeshNode* theNode2,
7706 const SMDS_MeshNode* theNode3)
7708 list< const SMDS_MeshNode* > nodes;
7709 list< const SMDS_MeshElement* > faces;
7710 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7713 //=======================================================================
7714 //function : SewFreeBorder
7716 //warning : for border-to-side sewing theSideSecondNode is considered as
7717 // the last side node and theSideThirdNode is not used
7718 //=======================================================================
7720 SMESH_MeshEditor::Sew_Error
7721 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7722 const SMDS_MeshNode* theBordSecondNode,
7723 const SMDS_MeshNode* theBordLastNode,
7724 const SMDS_MeshNode* theSideFirstNode,
7725 const SMDS_MeshNode* theSideSecondNode,
7726 const SMDS_MeshNode* theSideThirdNode,
7727 const bool theSideIsFreeBorder,
7728 const bool toCreatePolygons,
7729 const bool toCreatePolyedrs)
7733 Sew_Error aResult = SEW_OK;
7735 // ====================================
7736 // find side nodes and elements
7737 // ====================================
7739 list< const SMDS_MeshNode* > nSide[ 2 ];
7740 list< const SMDS_MeshElement* > eSide[ 2 ];
7741 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7742 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7746 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7747 nSide[0], eSide[0])) {
7748 MESSAGE(" Free Border 1 not found " );
7749 aResult = SEW_BORDER1_NOT_FOUND;
7751 if (theSideIsFreeBorder) {
7754 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7755 nSide[1], eSide[1])) {
7756 MESSAGE(" Free Border 2 not found " );
7757 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7760 if ( aResult != SEW_OK )
7763 if (!theSideIsFreeBorder) {
7767 // -------------------------------------------------------------------------
7769 // 1. If nodes to merge are not coincident, move nodes of the free border
7770 // from the coord sys defined by the direction from the first to last
7771 // nodes of the border to the correspondent sys of the side 2
7772 // 2. On the side 2, find the links most co-directed with the correspondent
7773 // links of the free border
7774 // -------------------------------------------------------------------------
7776 // 1. Since sewing may break if there are volumes to split on the side 2,
7777 // we won't move nodes but just compute new coordinates for them
7778 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7779 TNodeXYZMap nBordXYZ;
7780 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7781 list< const SMDS_MeshNode* >::iterator nBordIt;
7783 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7784 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7785 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7786 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7787 double tol2 = 1.e-8;
7788 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7789 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7790 // Need node movement.
7792 // find X and Z axes to create trsf
7793 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7795 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7797 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7800 gp_Ax3 toBordAx( Pb1, Zb, X );
7801 gp_Ax3 fromSideAx( Ps1, Zs, X );
7802 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7804 gp_Trsf toBordSys, fromSide2Sys;
7805 toBordSys.SetTransformation( toBordAx );
7806 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7807 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7810 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7811 const SMDS_MeshNode* n = *nBordIt;
7812 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7813 toBordSys.Transforms( xyz );
7814 fromSide2Sys.Transforms( xyz );
7815 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7819 // just insert nodes XYZ in the nBordXYZ map
7820 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7821 const SMDS_MeshNode* n = *nBordIt;
7822 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7826 // 2. On the side 2, find the links most co-directed with the correspondent
7827 // links of the free border
7829 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7830 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7831 sideNodes.push_back( theSideFirstNode );
7833 bool hasVolumes = false;
7834 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7835 set<long> foundSideLinkIDs, checkedLinkIDs;
7836 SMDS_VolumeTool volume;
7837 //const SMDS_MeshNode* faceNodes[ 4 ];
7839 const SMDS_MeshNode* sideNode;
7840 const SMDS_MeshElement* sideElem = 0;
7841 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7842 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7843 nBordIt = bordNodes.begin();
7845 // border node position and border link direction to compare with
7846 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7847 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7848 // choose next side node by link direction or by closeness to
7849 // the current border node:
7850 bool searchByDir = ( *nBordIt != theBordLastNode );
7852 // find the next node on the Side 2
7854 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7856 checkedLinkIDs.clear();
7857 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7859 // loop on inverse elements of current node (prevSideNode) on the Side 2
7860 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7861 while ( invElemIt->more() )
7863 const SMDS_MeshElement* elem = invElemIt->next();
7864 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7865 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7866 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7867 bool isVolume = volume.Set( elem );
7868 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7869 if ( isVolume ) // --volume
7871 else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7872 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7873 SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
7874 while ( nIt->more() ) {
7875 nodes[ iNode ] = cast2Node( nIt->next() );
7876 if ( nodes[ iNode++ ] == prevSideNode )
7877 iPrevNode = iNode - 1;
7879 // there are 2 links to check
7884 // loop on links, to be precise, on the second node of links
7885 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7886 const SMDS_MeshNode* n = nodes[ iNode ];
7888 if ( !volume.IsLinked( n, prevSideNode ))
7892 if ( iNode ) // a node before prevSideNode
7893 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7894 else // a node after prevSideNode
7895 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7897 // check if this link was already used
7898 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7899 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7900 if (!isJustChecked &&
7901 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7903 // test a link geometrically
7904 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7905 bool linkIsBetter = false;
7906 double dot = 0.0, dist = 0.0;
7907 if ( searchByDir ) { // choose most co-directed link
7908 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7909 linkIsBetter = ( dot > maxDot );
7911 else { // choose link with the node closest to bordPos
7912 dist = ( nextXYZ - bordPos ).SquareModulus();
7913 linkIsBetter = ( dist < minDist );
7915 if ( linkIsBetter ) {
7924 } // loop on inverse elements of prevSideNode
7927 MESSAGE(" Can't find path by links of the Side 2 ");
7928 return SEW_BAD_SIDE_NODES;
7930 sideNodes.push_back( sideNode );
7931 sideElems.push_back( sideElem );
7932 foundSideLinkIDs.insert ( linkID );
7933 prevSideNode = sideNode;
7935 if ( *nBordIt == theBordLastNode )
7936 searchByDir = false;
7938 // find the next border link to compare with
7939 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7940 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7941 // move to next border node if sideNode is before forward border node (bordPos)
7942 while ( *nBordIt != theBordLastNode && !searchByDir ) {
7943 prevBordNode = *nBordIt;
7945 bordPos = nBordXYZ[ *nBordIt ];
7946 bordDir = bordPos - nBordXYZ[ prevBordNode ];
7947 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7951 while ( sideNode != theSideSecondNode );
7953 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7954 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7955 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7957 } // end nodes search on the side 2
7959 // ============================
7960 // sew the border to the side 2
7961 // ============================
7963 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
7964 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
7966 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
7967 if ( toMergeConformal && toCreatePolygons )
7969 // do not merge quadrangles if polygons are OK (IPAL0052824)
7970 eIt[0] = eSide[0].begin();
7971 eIt[1] = eSide[1].begin();
7972 bool allQuads[2] = { true, true };
7973 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7974 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
7975 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
7977 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
7980 TListOfListOfNodes nodeGroupsToMerge;
7981 if (( toMergeConformal ) ||
7982 ( theSideIsFreeBorder && !theSideThirdNode )) {
7984 // all nodes are to be merged
7986 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
7987 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
7988 nIt[0]++, nIt[1]++ )
7990 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
7991 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
7992 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
7997 // insert new nodes into the border and the side to get equal nb of segments
7999 // get normalized parameters of nodes on the borders
8000 vector< double > param[ 2 ];
8001 param[0].resize( maxNbNodes );
8002 param[1].resize( maxNbNodes );
8004 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8005 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8006 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8007 const SMDS_MeshNode* nPrev = *nIt;
8008 double bordLength = 0;
8009 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8010 const SMDS_MeshNode* nCur = *nIt;
8011 gp_XYZ segment (nCur->X() - nPrev->X(),
8012 nCur->Y() - nPrev->Y(),
8013 nCur->Z() - nPrev->Z());
8014 double segmentLen = segment.Modulus();
8015 bordLength += segmentLen;
8016 param[ iBord ][ iNode ] = bordLength;
8019 // normalize within [0,1]
8020 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8021 param[ iBord ][ iNode ] /= bordLength;
8025 // loop on border segments
8026 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8027 int i[ 2 ] = { 0, 0 };
8028 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8029 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8031 // element can be split while iterating on border if it has two edges in the border
8032 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8033 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8035 TElemOfNodeListMap insertMap;
8036 TElemOfNodeListMap::iterator insertMapIt;
8038 // key: elem to insert nodes into
8039 // value: 2 nodes to insert between + nodes to be inserted
8041 bool next[ 2 ] = { false, false };
8043 // find min adjacent segment length after sewing
8044 double nextParam = 10., prevParam = 0;
8045 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8046 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8047 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8048 if ( i[ iBord ] > 0 )
8049 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8051 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8052 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8053 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8055 // choose to insert or to merge nodes
8056 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8057 if ( Abs( du ) <= minSegLen * 0.2 ) {
8060 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8061 const SMDS_MeshNode* n0 = *nIt[0];
8062 const SMDS_MeshNode* n1 = *nIt[1];
8063 nodeGroupsToMerge.back().push_back( n1 );
8064 nodeGroupsToMerge.back().push_back( n0 );
8065 // position of node of the border changes due to merge
8066 param[ 0 ][ i[0] ] += du;
8067 // move n1 for the sake of elem shape evaluation during insertion.
8068 // n1 will be removed by MergeNodes() anyway
8069 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8070 next[0] = next[1] = true;
8075 int intoBord = ( du < 0 ) ? 0 : 1;
8076 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8077 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8078 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8079 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8080 if ( intoBord == 1 ) {
8081 // move node of the border to be on a link of elem of the side
8082 SMESH_NodeXYZ p1( n1 ), p2( n2 );
8083 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8084 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8085 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8087 elemReplaceMapIt = elemReplaceMap.find( elem );
8088 if ( elemReplaceMapIt != elemReplaceMap.end() )
8089 elem = elemReplaceMapIt->second;
8091 insertMapIt = insertMap.find( elem );
8092 bool notFound = ( insertMapIt == insertMap.end() );
8093 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8095 // insert into another link of the same element:
8096 // 1. perform insertion into the other link of the elem
8097 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8098 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8099 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8100 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8101 // 2. perform insertion into the link of adjacent faces
8102 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8103 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8105 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8106 InsertNodesIntoLink( seg, n12, n22, nodeList );
8108 if (toCreatePolyedrs) {
8109 // perform insertion into the links of adjacent volumes
8110 UpdateVolumes(n12, n22, nodeList);
8112 // 3. find an element appeared on n1 and n2 after the insertion
8113 insertMap.erase( insertMapIt );
8114 const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8115 elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8118 if ( notFound || otherLink ) {
8119 // add element and nodes of the side into the insertMap
8120 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8121 (*insertMapIt).second.push_back( n1 );
8122 (*insertMapIt).second.push_back( n2 );
8124 // add node to be inserted into elem
8125 (*insertMapIt).second.push_back( nIns );
8126 next[ 1 - intoBord ] = true;
8129 // go to the next segment
8130 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8131 if ( next[ iBord ] ) {
8132 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8134 nPrev[ iBord ] = *nIt[ iBord ];
8135 nIt[ iBord ]++; i[ iBord ]++;
8139 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8141 // perform insertion of nodes into elements
8143 for (insertMapIt = insertMap.begin();
8144 insertMapIt != insertMap.end();
8147 const SMDS_MeshElement* elem = (*insertMapIt).first;
8148 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8149 if ( nodeList.size() < 3 ) continue;
8150 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8151 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8153 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8155 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8156 InsertNodesIntoLink( seg, n1, n2, nodeList );
8159 if ( !theSideIsFreeBorder ) {
8160 // look for and insert nodes into the faces adjacent to elem
8161 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8162 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8165 if (toCreatePolyedrs) {
8166 // perform insertion into the links of adjacent volumes
8167 UpdateVolumes(n1, n2, nodeList);
8170 } // end: insert new nodes
8172 MergeNodes ( nodeGroupsToMerge );
8175 // Remove coincident segments
8178 TIDSortedElemSet segments;
8179 SMESH_SequenceOfElemPtr newFaces;
8180 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8182 if ( !myLastCreatedElems[i] ) continue;
8183 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8184 segments.insert( segments.end(), myLastCreatedElems[i] );
8186 newFaces.push_back( myLastCreatedElems[i] );
8188 // get segments adjacent to merged nodes
8189 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8190 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8192 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8193 if ( nodes.front()->IsNull() ) continue;
8194 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8195 while ( segIt->more() )
8196 segments.insert( segIt->next() );
8200 TListOfListOfElementsID equalGroups;
8201 if ( !segments.empty() )
8202 FindEqualElements( segments, equalGroups );
8203 if ( !equalGroups.empty() )
8205 // remove from segments those that will be removed
8206 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8207 for ( ; itGroups != equalGroups.end(); ++itGroups )
8209 list< int >& group = *itGroups;
8210 list< int >::iterator id = group.begin();
8211 for ( ++id; id != group.end(); ++id )
8212 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8213 segments.erase( seg );
8215 // remove equal segments
8216 MergeElements( equalGroups );
8218 // restore myLastCreatedElems
8219 myLastCreatedElems = newFaces;
8220 TIDSortedElemSet::iterator seg = segments.begin();
8221 for ( ; seg != segments.end(); ++seg )
8222 myLastCreatedElems.push_back( *seg );
8228 //=======================================================================
8229 //function : InsertNodesIntoLink
8230 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8231 // and theBetweenNode2 and split theElement
8232 //=======================================================================
8234 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8235 const SMDS_MeshNode* theBetweenNode1,
8236 const SMDS_MeshNode* theBetweenNode2,
8237 list<const SMDS_MeshNode*>& theNodesToInsert,
8238 const bool toCreatePoly)
8240 if ( !theElement ) return;
8242 SMESHDS_Mesh *aMesh = GetMeshDS();
8243 vector<const SMDS_MeshElement*> newElems;
8245 if ( theElement->GetType() == SMDSAbs_Edge )
8247 theNodesToInsert.push_front( theBetweenNode1 );
8248 theNodesToInsert.push_back ( theBetweenNode2 );
8249 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8250 const SMDS_MeshNode* n1 = *n;
8251 for ( ++n; n != theNodesToInsert.end(); ++n )
8253 const SMDS_MeshNode* n2 = *n;
8254 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8255 AddToSameGroups( seg, theElement, aMesh );
8257 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8260 theNodesToInsert.pop_front();
8261 theNodesToInsert.pop_back();
8263 if ( theElement->IsQuadratic() ) // add a not split part
8265 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8266 theElement->end_nodes() );
8267 int iOther = 0, nbN = nodes.size();
8268 for ( ; iOther < nbN; ++iOther )
8269 if ( nodes[iOther] != theBetweenNode1 &&
8270 nodes[iOther] != theBetweenNode2 )
8274 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8275 AddToSameGroups( seg, theElement, aMesh );
8277 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8279 else if ( iOther == 2 )
8281 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8282 AddToSameGroups( seg, theElement, aMesh );
8284 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8287 // treat new elements
8288 for ( size_t i = 0; i < newElems.size(); ++i )
8291 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8292 myLastCreatedElems.push_back( newElems[i] );
8294 ReplaceElemInGroups( theElement, newElems, aMesh );
8295 aMesh->RemoveElement( theElement );
8298 } // if ( theElement->GetType() == SMDSAbs_Edge )
8300 const SMDS_MeshElement* theFace = theElement;
8301 if ( theFace->GetType() != SMDSAbs_Face ) return;
8303 // find indices of 2 link nodes and of the rest nodes
8304 int iNode = 0, il1, il2, i3, i4;
8305 il1 = il2 = i3 = i4 = -1;
8306 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8308 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8309 while ( nodeIt->more() ) {
8310 const SMDS_MeshNode* n = nodeIt->next();
8311 if ( n == theBetweenNode1 )
8313 else if ( n == theBetweenNode2 )
8319 nodes[ iNode++ ] = n;
8321 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8324 // arrange link nodes to go one after another regarding the face orientation
8325 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8326 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8331 aNodesToInsert.reverse();
8333 // check that not link nodes of a quadrangles are in good order
8334 int nbFaceNodes = theFace->NbNodes();
8335 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8341 if (toCreatePoly || theFace->IsPoly()) {
8344 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8346 // add nodes of face up to first node of link
8348 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8349 while ( nodeIt->more() && !isFLN ) {
8350 const SMDS_MeshNode* n = nodeIt->next();
8351 poly_nodes[iNode++] = n;
8352 isFLN = ( n == nodes[il1] );
8354 // add nodes to insert
8355 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8356 for (; nIt != aNodesToInsert.end(); nIt++) {
8357 poly_nodes[iNode++] = *nIt;
8359 // add nodes of face starting from last node of link
8360 while ( nodeIt->more() ) {
8361 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8362 poly_nodes[iNode++] = n;
8366 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8369 else if ( !theFace->IsQuadratic() )
8371 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8372 int nbLinkNodes = 2 + aNodesToInsert.size();
8373 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8374 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8375 linkNodes[ 0 ] = nodes[ il1 ];
8376 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8377 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8378 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8379 linkNodes[ iNode++ ] = *nIt;
8381 // decide how to split a quadrangle: compare possible variants
8382 // and choose which of splits to be a quadrangle
8383 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8384 if ( nbFaceNodes == 3 ) {
8385 iBestQuad = nbSplits;
8388 else if ( nbFaceNodes == 4 ) {
8389 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8390 double aBestRate = DBL_MAX;
8391 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8393 double aBadRate = 0;
8394 // evaluate elements quality
8395 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8396 if ( iSplit == iQuad ) {
8397 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8401 aBadRate += getBadRate( &quad, aCrit );
8404 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8406 nodes[ iSplit < iQuad ? i4 : i3 ]);
8407 aBadRate += getBadRate( &tria, aCrit );
8411 if ( aBadRate < aBestRate ) {
8413 aBestRate = aBadRate;
8418 // create new elements
8420 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8422 if ( iSplit == iBestQuad )
8423 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8428 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8430 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8433 const SMDS_MeshNode* newNodes[ 4 ];
8434 newNodes[ 0 ] = linkNodes[ i1 ];
8435 newNodes[ 1 ] = linkNodes[ i2 ];
8436 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8437 newNodes[ 3 ] = nodes[ i4 ];
8438 if (iSplit == iBestQuad)
8439 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8441 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8443 } // end if(!theFace->IsQuadratic())
8445 else { // theFace is quadratic
8446 // we have to split theFace on simple triangles and one simple quadrangle
8448 int nbshift = tmp*2;
8449 // shift nodes in nodes[] by nbshift
8451 for(i=0; i<nbshift; i++) {
8452 const SMDS_MeshNode* n = nodes[0];
8453 for(j=0; j<nbFaceNodes-1; j++) {
8454 nodes[j] = nodes[j+1];
8456 nodes[nbFaceNodes-1] = n;
8458 il1 = il1 - nbshift;
8459 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8460 // n0 n1 n2 n0 n1 n2
8461 // +-----+-----+ +-----+-----+
8470 // create new elements
8472 if ( nbFaceNodes == 6 ) { // quadratic triangle
8473 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8474 if ( theFace->IsMediumNode(nodes[il1]) ) {
8475 // create quadrangle
8476 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8482 // create quadrangle
8483 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8489 else { // nbFaceNodes==8 - quadratic quadrangle
8490 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8491 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8492 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8493 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8494 // create quadrangle
8495 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8501 // create quadrangle
8502 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8508 // create needed triangles using n1,n2,n3 and inserted nodes
8509 int nbn = 2 + aNodesToInsert.size();
8510 vector<const SMDS_MeshNode*> aNodes(nbn);
8511 aNodes[0 ] = nodes[n1];
8512 aNodes[nbn-1] = nodes[n2];
8513 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8514 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8515 aNodes[iNode++] = *nIt;
8517 for ( i = 1; i < nbn; i++ )
8518 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8521 // remove the old face
8522 for ( size_t i = 0; i < newElems.size(); ++i )
8525 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8526 myLastCreatedElems.push_back( newElems[i] );
8528 ReplaceElemInGroups( theFace, newElems, aMesh );
8529 aMesh->RemoveElement(theFace);
8531 } // InsertNodesIntoLink()
8533 //=======================================================================
8534 //function : UpdateVolumes
8536 //=======================================================================
8538 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8539 const SMDS_MeshNode* theBetweenNode2,
8540 list<const SMDS_MeshNode*>& theNodesToInsert)
8544 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8545 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8546 const SMDS_MeshElement* elem = invElemIt->next();
8548 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8549 SMDS_VolumeTool aVolume (elem);
8550 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8553 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8554 int iface, nbFaces = aVolume.NbFaces();
8555 vector<const SMDS_MeshNode *> poly_nodes;
8556 vector<int> quantities (nbFaces);
8558 for (iface = 0; iface < nbFaces; iface++) {
8559 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8560 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8561 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8563 for (int inode = 0; inode < nbFaceNodes; inode++) {
8564 poly_nodes.push_back(faceNodes[inode]);
8566 if (nbInserted == 0) {
8567 if (faceNodes[inode] == theBetweenNode1) {
8568 if (faceNodes[inode + 1] == theBetweenNode2) {
8569 nbInserted = theNodesToInsert.size();
8571 // add nodes to insert
8572 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8573 for (; nIt != theNodesToInsert.end(); nIt++) {
8574 poly_nodes.push_back(*nIt);
8578 else if (faceNodes[inode] == theBetweenNode2) {
8579 if (faceNodes[inode + 1] == theBetweenNode1) {
8580 nbInserted = theNodesToInsert.size();
8582 // add nodes to insert in reversed order
8583 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8585 for (; nIt != theNodesToInsert.begin(); nIt--) {
8586 poly_nodes.push_back(*nIt);
8588 poly_nodes.push_back(*nIt);
8595 quantities[iface] = nbFaceNodes + nbInserted;
8598 // Replace the volume
8599 SMESHDS_Mesh *aMesh = GetMeshDS();
8601 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8603 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8604 myLastCreatedElems.push_back( newElem );
8605 ReplaceElemInGroups( elem, newElem, aMesh );
8607 aMesh->RemoveElement( elem );
8613 //================================================================================
8615 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8617 //================================================================================
8619 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8620 vector<const SMDS_MeshNode *> & nodes,
8621 vector<int> & nbNodeInFaces )
8624 nbNodeInFaces.clear();
8625 SMDS_VolumeTool vTool ( elem );
8626 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8628 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8629 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8630 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8635 //=======================================================================
8637 * \brief Convert elements contained in a sub-mesh to quadratic
8638 * \return int - nb of checked elements
8640 //=======================================================================
8642 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8643 SMESH_MesherHelper& theHelper,
8644 const bool theForce3d)
8646 //MESSAGE("convertElemToQuadratic");
8648 if( !theSm ) return nbElem;
8650 vector<int> nbNodeInFaces;
8651 vector<const SMDS_MeshNode *> nodes;
8652 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8653 while(ElemItr->more())
8656 const SMDS_MeshElement* elem = ElemItr->next();
8657 if( !elem ) continue;
8659 // analyse a necessity of conversion
8660 const SMDSAbs_ElementType aType = elem->GetType();
8661 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8663 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8664 bool hasCentralNodes = false;
8665 if ( elem->IsQuadratic() )
8668 switch ( aGeomType ) {
8669 case SMDSEntity_Quad_Triangle:
8670 case SMDSEntity_Quad_Quadrangle:
8671 case SMDSEntity_Quad_Hexa:
8672 case SMDSEntity_Quad_Penta:
8673 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8675 case SMDSEntity_BiQuad_Triangle:
8676 case SMDSEntity_BiQuad_Quadrangle:
8677 case SMDSEntity_TriQuad_Hexa:
8678 case SMDSEntity_BiQuad_Penta:
8679 alreadyOK = theHelper.GetIsBiQuadratic();
8680 hasCentralNodes = true;
8685 // take into account already present medium nodes
8687 case SMDSAbs_Volume:
8688 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8690 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8692 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8698 // get elem data needed to re-create it
8700 const int id = elem->GetID();
8701 const int nbNodes = elem->NbCornerNodes();
8702 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8703 if ( aGeomType == SMDSEntity_Polyhedra )
8704 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8705 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8706 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8708 // remove a linear element
8709 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8711 // remove central nodes of biquadratic elements (biquad->quad conversion)
8712 if ( hasCentralNodes )
8713 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8714 if ( nodes[i]->NbInverseElements() == 0 )
8715 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8717 const SMDS_MeshElement* NewElem = 0;
8723 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8731 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8734 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8737 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8741 case SMDSAbs_Volume :
8745 case SMDSEntity_Tetra:
8746 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8748 case SMDSEntity_Pyramid:
8749 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8751 case SMDSEntity_Penta:
8752 case SMDSEntity_Quad_Penta:
8753 case SMDSEntity_BiQuad_Penta:
8754 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8756 case SMDSEntity_Hexa:
8757 case SMDSEntity_Quad_Hexa:
8758 case SMDSEntity_TriQuad_Hexa:
8759 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8760 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8762 case SMDSEntity_Hexagonal_Prism:
8764 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8771 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8772 if( NewElem && NewElem->getshapeId() < 1 )
8773 theSm->AddElement( NewElem );
8777 //=======================================================================
8778 //function : ConvertToQuadratic
8780 //=======================================================================
8782 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8784 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8785 SMESHDS_Mesh* meshDS = GetMeshDS();
8787 SMESH_MesherHelper aHelper(*myMesh);
8789 aHelper.SetIsQuadratic( true );
8790 aHelper.SetIsBiQuadratic( theToBiQuad );
8791 aHelper.SetElementsOnShape(true);
8792 aHelper.ToFixNodeParameters( true );
8794 // convert elements assigned to sub-meshes
8795 int nbCheckedElems = 0;
8796 if ( myMesh->HasShapeToMesh() )
8798 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8800 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8801 while ( smIt->more() ) {
8802 SMESH_subMesh* sm = smIt->next();
8803 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8804 aHelper.SetSubShape( sm->GetSubShape() );
8805 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8811 // convert elements NOT assigned to sub-meshes
8812 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8813 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8815 aHelper.SetElementsOnShape(false);
8816 SMESHDS_SubMesh *smDS = 0;
8819 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8820 while( aEdgeItr->more() )
8822 const SMDS_MeshEdge* edge = aEdgeItr->next();
8823 if ( !edge->IsQuadratic() )
8825 int id = edge->GetID();
8826 const SMDS_MeshNode* n1 = edge->GetNode(0);
8827 const SMDS_MeshNode* n2 = edge->GetNode(1);
8829 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8831 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8832 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8836 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8841 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8842 while( aFaceItr->more() )
8844 const SMDS_MeshFace* face = aFaceItr->next();
8845 if ( !face ) continue;
8847 const SMDSAbs_EntityType type = face->GetEntityType();
8851 case SMDSEntity_Quad_Triangle:
8852 case SMDSEntity_Quad_Quadrangle:
8853 alreadyOK = !theToBiQuad;
8854 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8856 case SMDSEntity_BiQuad_Triangle:
8857 case SMDSEntity_BiQuad_Quadrangle:
8858 alreadyOK = theToBiQuad;
8859 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8861 default: alreadyOK = false;
8866 const int id = face->GetID();
8867 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8869 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8871 SMDS_MeshFace * NewFace = 0;
8874 case SMDSEntity_Triangle:
8875 case SMDSEntity_Quad_Triangle:
8876 case SMDSEntity_BiQuad_Triangle:
8877 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8878 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8879 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8882 case SMDSEntity_Quadrangle:
8883 case SMDSEntity_Quad_Quadrangle:
8884 case SMDSEntity_BiQuad_Quadrangle:
8885 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8886 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8887 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8891 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8893 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8897 vector<int> nbNodeInFaces;
8898 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8899 while(aVolumeItr->more())
8901 const SMDS_MeshVolume* volume = aVolumeItr->next();
8902 if ( !volume ) continue;
8904 const SMDSAbs_EntityType type = volume->GetEntityType();
8905 if ( volume->IsQuadratic() )
8910 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8911 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8912 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
8913 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
8914 default: alreadyOK = true;
8918 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8922 const int id = volume->GetID();
8923 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8924 if ( type == SMDSEntity_Polyhedra )
8925 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
8926 else if ( type == SMDSEntity_Hexagonal_Prism )
8927 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8929 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8931 SMDS_MeshVolume * NewVolume = 0;
8934 case SMDSEntity_Tetra:
8935 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8937 case SMDSEntity_Hexa:
8938 case SMDSEntity_Quad_Hexa:
8939 case SMDSEntity_TriQuad_Hexa:
8940 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8941 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8942 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8943 if ( nodes[i]->NbInverseElements() == 0 )
8944 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8946 case SMDSEntity_Pyramid:
8947 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8948 nodes[3], nodes[4], id, theForce3d);
8950 case SMDSEntity_Penta:
8951 case SMDSEntity_Quad_Penta:
8952 case SMDSEntity_BiQuad_Penta:
8953 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8954 nodes[3], nodes[4], nodes[5], id, theForce3d);
8955 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
8956 if ( nodes[i]->NbInverseElements() == 0 )
8957 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8959 case SMDSEntity_Hexagonal_Prism:
8961 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8963 ReplaceElemInGroups(volume, NewVolume, meshDS);
8968 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8969 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8970 // aHelper.FixQuadraticElements(myError);
8971 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8975 //================================================================================
8977 * \brief Makes given elements quadratic
8978 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
8979 * \param theElements - elements to make quadratic
8981 //================================================================================
8983 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
8984 TIDSortedElemSet& theElements,
8985 const bool theToBiQuad)
8987 if ( theElements.empty() ) return;
8989 // we believe that all theElements are of the same type
8990 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
8992 // get all nodes shared by theElements
8993 TIDSortedNodeSet allNodes;
8994 TIDSortedElemSet::iterator eIt = theElements.begin();
8995 for ( ; eIt != theElements.end(); ++eIt )
8996 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
8998 // complete theElements with elements of lower dim whose all nodes are in allNodes
9000 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9001 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9002 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9003 for ( ; nIt != allNodes.end(); ++nIt )
9005 const SMDS_MeshNode* n = *nIt;
9006 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9007 while ( invIt->more() )
9009 const SMDS_MeshElement* e = invIt->next();
9010 const SMDSAbs_ElementType type = e->GetType();
9011 if ( e->IsQuadratic() )
9013 quadAdjacentElems[ type ].insert( e );
9016 switch ( e->GetEntityType() ) {
9017 case SMDSEntity_Quad_Triangle:
9018 case SMDSEntity_Quad_Quadrangle:
9019 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9020 case SMDSEntity_BiQuad_Triangle:
9021 case SMDSEntity_BiQuad_Quadrangle:
9022 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9023 default: alreadyOK = true;
9028 if ( type >= elemType )
9029 continue; // same type or more complex linear element
9031 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9032 continue; // e is already checked
9036 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9037 while ( nodeIt->more() && allIn )
9038 allIn = allNodes.count( nodeIt->next() );
9040 theElements.insert(e );
9044 SMESH_MesherHelper helper(*myMesh);
9045 helper.SetIsQuadratic( true );
9046 helper.SetIsBiQuadratic( theToBiQuad );
9048 // add links of quadratic adjacent elements to the helper
9050 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9051 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9052 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9054 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9056 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9057 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9058 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9060 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9062 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9063 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9064 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9066 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9069 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9071 SMESHDS_Mesh* meshDS = GetMeshDS();
9072 SMESHDS_SubMesh* smDS = 0;
9073 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9075 const SMDS_MeshElement* elem = *eIt;
9078 int nbCentralNodes = 0;
9079 switch ( elem->GetEntityType() ) {
9080 // linear convertible
9081 case SMDSEntity_Edge:
9082 case SMDSEntity_Triangle:
9083 case SMDSEntity_Quadrangle:
9084 case SMDSEntity_Tetra:
9085 case SMDSEntity_Pyramid:
9086 case SMDSEntity_Hexa:
9087 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9088 // quadratic that can become bi-quadratic
9089 case SMDSEntity_Quad_Triangle:
9090 case SMDSEntity_Quad_Quadrangle:
9091 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9093 case SMDSEntity_BiQuad_Triangle:
9094 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9095 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9097 default: alreadyOK = true;
9099 if ( alreadyOK ) continue;
9101 const SMDSAbs_ElementType type = elem->GetType();
9102 const int id = elem->GetID();
9103 const int nbNodes = elem->NbCornerNodes();
9104 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9106 helper.SetSubShape( elem->getshapeId() );
9108 if ( !smDS || !smDS->Contains( elem ))
9109 smDS = meshDS->MeshElements( elem->getshapeId() );
9110 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9112 SMDS_MeshElement * newElem = 0;
9115 case 4: // cases for most frequently used element types go first (for optimization)
9116 if ( type == SMDSAbs_Volume )
9117 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9119 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9122 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9123 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9126 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9129 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9132 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9133 nodes[4], id, theForce3d);
9136 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9137 nodes[4], nodes[5], id, theForce3d);
9141 ReplaceElemInGroups( elem, newElem, meshDS);
9142 if( newElem && smDS )
9143 smDS->AddElement( newElem );
9145 // remove central nodes
9146 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9147 if ( nodes[i]->NbInverseElements() == 0 )
9148 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9150 } // loop on theElements
9153 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9154 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9155 // helper.FixQuadraticElements( myError );
9156 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9160 //=======================================================================
9162 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9163 * \return int - nb of checked elements
9165 //=======================================================================
9167 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9168 SMDS_ElemIteratorPtr theItr,
9169 const int theShapeID)
9172 SMESHDS_Mesh* meshDS = GetMeshDS();
9173 ElemFeatures elemType;
9174 vector<const SMDS_MeshNode *> nodes;
9176 while( theItr->more() )
9178 const SMDS_MeshElement* elem = theItr->next();
9180 if( elem && elem->IsQuadratic())
9183 int nbCornerNodes = elem->NbCornerNodes();
9184 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9186 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9188 //remove a quadratic element
9189 if ( !theSm || !theSm->Contains( elem ))
9190 theSm = meshDS->MeshElements( elem->getshapeId() );
9191 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9193 // remove medium nodes
9194 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9195 if ( nodes[i]->NbInverseElements() == 0 )
9196 meshDS->RemoveFreeNode( nodes[i], theSm );
9198 // add a linear element
9199 nodes.resize( nbCornerNodes );
9200 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9201 ReplaceElemInGroups(elem, newElem, meshDS);
9202 if( theSm && newElem )
9203 theSm->AddElement( newElem );
9209 //=======================================================================
9210 //function : ConvertFromQuadratic
9212 //=======================================================================
9214 bool SMESH_MeshEditor::ConvertFromQuadratic()
9216 int nbCheckedElems = 0;
9217 if ( myMesh->HasShapeToMesh() )
9219 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9221 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9222 while ( smIt->more() ) {
9223 SMESH_subMesh* sm = smIt->next();
9224 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9225 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9231 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9232 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9234 SMESHDS_SubMesh *aSM = 0;
9235 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9243 //================================================================================
9245 * \brief Return true if all medium nodes of the element are in the node set
9247 //================================================================================
9249 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9251 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9252 if ( !nodeSet.count( elem->GetNode(i) ))
9258 //================================================================================
9260 * \brief Makes given elements linear
9262 //================================================================================
9264 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9266 if ( theElements.empty() ) return;
9268 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9269 set<int> mediumNodeIDs;
9270 TIDSortedElemSet::iterator eIt = theElements.begin();
9271 for ( ; eIt != theElements.end(); ++eIt )
9273 const SMDS_MeshElement* e = *eIt;
9274 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9275 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9278 // replace given elements by linear ones
9279 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9280 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9282 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9283 // except those elements sharing medium nodes of quadratic element whose medium nodes
9284 // are not all in mediumNodeIDs
9286 // get remaining medium nodes
9287 TIDSortedNodeSet mediumNodes;
9288 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9289 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9290 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9291 mediumNodes.insert( mediumNodes.end(), n );
9293 // find more quadratic elements to convert
9294 TIDSortedElemSet moreElemsToConvert;
9295 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9296 for ( ; nIt != mediumNodes.end(); ++nIt )
9298 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9299 while ( invIt->more() )
9301 const SMDS_MeshElement* e = invIt->next();
9302 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9304 // find a more complex element including e and
9305 // whose medium nodes are not in mediumNodes
9306 bool complexFound = false;
9307 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9309 SMDS_ElemIteratorPtr invIt2 =
9310 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9311 while ( invIt2->more() )
9313 const SMDS_MeshElement* eComplex = invIt2->next();
9314 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9316 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9317 if ( nbCommonNodes == e->NbNodes())
9319 complexFound = true;
9320 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9326 if ( !complexFound )
9327 moreElemsToConvert.insert( e );
9331 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9332 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9335 //=======================================================================
9336 //function : SewSideElements
9338 //=======================================================================
9340 SMESH_MeshEditor::Sew_Error
9341 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9342 TIDSortedElemSet& theSide2,
9343 const SMDS_MeshNode* theFirstNode1,
9344 const SMDS_MeshNode* theFirstNode2,
9345 const SMDS_MeshNode* theSecondNode1,
9346 const SMDS_MeshNode* theSecondNode2)
9350 if ( theSide1.size() != theSide2.size() )
9351 return SEW_DIFF_NB_OF_ELEMENTS;
9353 Sew_Error aResult = SEW_OK;
9355 // 1. Build set of faces representing each side
9356 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9357 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9359 // =======================================================================
9360 // 1. Build set of faces representing each side:
9361 // =======================================================================
9362 // a. build set of nodes belonging to faces
9363 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9364 // c. create temporary faces representing side of volumes if correspondent
9365 // face does not exist
9367 SMESHDS_Mesh* aMesh = GetMeshDS();
9368 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9369 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9370 TIDSortedElemSet faceSet1, faceSet2;
9371 set<const SMDS_MeshElement*> volSet1, volSet2;
9372 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9373 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9374 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9375 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9376 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9377 int iSide, iFace, iNode;
9379 list<const SMDS_MeshElement* > tempFaceList;
9380 for ( iSide = 0; iSide < 2; iSide++ ) {
9381 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9382 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9383 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9384 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9385 set<const SMDS_MeshElement*>::iterator vIt;
9386 TIDSortedElemSet::iterator eIt;
9387 set<const SMDS_MeshNode*>::iterator nIt;
9389 // check that given nodes belong to given elements
9390 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9391 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9392 int firstIndex = -1, secondIndex = -1;
9393 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9394 const SMDS_MeshElement* elem = *eIt;
9395 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9396 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9397 if ( firstIndex > -1 && secondIndex > -1 ) break;
9399 if ( firstIndex < 0 || secondIndex < 0 ) {
9400 // we can simply return until temporary faces created
9401 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9404 // -----------------------------------------------------------
9405 // 1a. Collect nodes of existing faces
9406 // and build set of face nodes in order to detect missing
9407 // faces corresponding to sides of volumes
9408 // -----------------------------------------------------------
9410 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9412 // loop on the given element of a side
9413 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9414 //const SMDS_MeshElement* elem = *eIt;
9415 const SMDS_MeshElement* elem = *eIt;
9416 if ( elem->GetType() == SMDSAbs_Face ) {
9417 faceSet->insert( elem );
9418 set <const SMDS_MeshNode*> faceNodeSet;
9419 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9420 while ( nodeIt->more() ) {
9421 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9422 nodeSet->insert( n );
9423 faceNodeSet.insert( n );
9425 setOfFaceNodeSet.insert( faceNodeSet );
9427 else if ( elem->GetType() == SMDSAbs_Volume )
9428 volSet->insert( elem );
9430 // ------------------------------------------------------------------------------
9431 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9432 // ------------------------------------------------------------------------------
9434 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9435 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9436 while ( fIt->more() ) { // loop on faces sharing a node
9437 const SMDS_MeshElement* f = fIt->next();
9438 if ( faceSet->find( f ) == faceSet->end() ) {
9439 // check if all nodes are in nodeSet and
9440 // complete setOfFaceNodeSet if they are
9441 set <const SMDS_MeshNode*> faceNodeSet;
9442 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9443 bool allInSet = true;
9444 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9445 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9446 if ( nodeSet->find( n ) == nodeSet->end() )
9449 faceNodeSet.insert( n );
9452 faceSet->insert( f );
9453 setOfFaceNodeSet.insert( faceNodeSet );
9459 // -------------------------------------------------------------------------
9460 // 1c. Create temporary faces representing sides of volumes if correspondent
9461 // face does not exist
9462 // -------------------------------------------------------------------------
9464 if ( !volSet->empty() ) {
9465 //int nodeSetSize = nodeSet->size();
9467 // loop on given volumes
9468 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9469 SMDS_VolumeTool vol (*vIt);
9470 // loop on volume faces: find free faces
9471 // --------------------------------------
9472 list<const SMDS_MeshElement* > freeFaceList;
9473 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9474 if ( !vol.IsFreeFace( iFace ))
9476 // check if there is already a face with same nodes in a face set
9477 const SMDS_MeshElement* aFreeFace = 0;
9478 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9479 int nbNodes = vol.NbFaceNodes( iFace );
9480 set <const SMDS_MeshNode*> faceNodeSet;
9481 vol.GetFaceNodes( iFace, faceNodeSet );
9482 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9484 // no such a face is given but it still can exist, check it
9485 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9486 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9489 // create a temporary face
9490 if ( nbNodes == 3 ) {
9491 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9492 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9494 else if ( nbNodes == 4 ) {
9495 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9496 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9499 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9500 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9501 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9504 tempFaceList.push_back( aFreeFace );
9508 freeFaceList.push_back( aFreeFace );
9510 } // loop on faces of a volume
9512 // choose one of several free faces of a volume
9513 // --------------------------------------------
9514 if ( freeFaceList.size() > 1 ) {
9515 // choose a face having max nb of nodes shared by other elems of a side
9516 int maxNbNodes = -1;
9517 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9518 while ( fIt != freeFaceList.end() ) { // loop on free faces
9519 int nbSharedNodes = 0;
9520 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9521 while ( nodeIt->more() ) { // loop on free face nodes
9522 const SMDS_MeshNode* n =
9523 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9524 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9525 while ( invElemIt->more() ) {
9526 const SMDS_MeshElement* e = invElemIt->next();
9527 nbSharedNodes += faceSet->count( e );
9528 nbSharedNodes += elemSet->count( e );
9531 if ( nbSharedNodes > maxNbNodes ) {
9532 maxNbNodes = nbSharedNodes;
9533 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9535 else if ( nbSharedNodes == maxNbNodes ) {
9539 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9542 if ( freeFaceList.size() > 1 )
9544 // could not choose one face, use another way
9545 // choose a face most close to the bary center of the opposite side
9546 gp_XYZ aBC( 0., 0., 0. );
9547 set <const SMDS_MeshNode*> addedNodes;
9548 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9549 eIt = elemSet2->begin();
9550 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9551 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9552 while ( nodeIt->more() ) { // loop on free face nodes
9553 const SMDS_MeshNode* n =
9554 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9555 if ( addedNodes.insert( n ).second )
9556 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9559 aBC /= addedNodes.size();
9560 double minDist = DBL_MAX;
9561 fIt = freeFaceList.begin();
9562 while ( fIt != freeFaceList.end() ) { // loop on free faces
9564 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9565 while ( nodeIt->more() ) { // loop on free face nodes
9566 const SMDS_MeshNode* n =
9567 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9568 gp_XYZ p( n->X(),n->Y(),n->Z() );
9569 dist += ( aBC - p ).SquareModulus();
9571 if ( dist < minDist ) {
9573 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9576 fIt = freeFaceList.erase( fIt++ );
9579 } // choose one of several free faces of a volume
9581 if ( freeFaceList.size() == 1 ) {
9582 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9583 faceSet->insert( aFreeFace );
9584 // complete a node set with nodes of a found free face
9585 // for ( iNode = 0; iNode < ; iNode++ )
9586 // nodeSet->insert( fNodes[ iNode ] );
9589 } // loop on volumes of a side
9591 // // complete a set of faces if new nodes in a nodeSet appeared
9592 // // ----------------------------------------------------------
9593 // if ( nodeSetSize != nodeSet->size() ) {
9594 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9595 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9596 // while ( fIt->more() ) { // loop on faces sharing a node
9597 // const SMDS_MeshElement* f = fIt->next();
9598 // if ( faceSet->find( f ) == faceSet->end() ) {
9599 // // check if all nodes are in nodeSet and
9600 // // complete setOfFaceNodeSet if they are
9601 // set <const SMDS_MeshNode*> faceNodeSet;
9602 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9603 // bool allInSet = true;
9604 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9605 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9606 // if ( nodeSet->find( n ) == nodeSet->end() )
9607 // allInSet = false;
9609 // faceNodeSet.insert( n );
9611 // if ( allInSet ) {
9612 // faceSet->insert( f );
9613 // setOfFaceNodeSet.insert( faceNodeSet );
9619 } // Create temporary faces, if there are volumes given
9622 if ( faceSet1.size() != faceSet2.size() ) {
9623 // delete temporary faces: they are in reverseElements of actual nodes
9624 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9625 // while ( tmpFaceIt->more() )
9626 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9627 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9628 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9629 // aMesh->RemoveElement(*tmpFaceIt);
9630 MESSAGE("Diff nb of faces");
9631 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9634 // ============================================================
9635 // 2. Find nodes to merge:
9636 // bind a node to remove to a node to put instead
9637 // ============================================================
9639 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9640 if ( theFirstNode1 != theFirstNode2 )
9641 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9642 if ( theSecondNode1 != theSecondNode2 )
9643 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9645 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9646 set< long > linkIdSet; // links to process
9647 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9649 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9650 list< NLink > linkList[2];
9651 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9652 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9653 // loop on links in linkList; find faces by links and append links
9654 // of the found faces to linkList
9655 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9656 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9658 NLink link[] = { *linkIt[0], *linkIt[1] };
9659 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9660 if ( !linkIdSet.count( linkID ) )
9663 // by links, find faces in the face sets,
9664 // and find indices of link nodes in the found faces;
9665 // in a face set, there is only one or no face sharing a link
9666 // ---------------------------------------------------------------
9668 const SMDS_MeshElement* face[] = { 0, 0 };
9669 vector<const SMDS_MeshNode*> fnodes[2];
9670 int iLinkNode[2][2];
9671 TIDSortedElemSet avoidSet;
9672 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9673 const SMDS_MeshNode* n1 = link[iSide].first;
9674 const SMDS_MeshNode* n2 = link[iSide].second;
9675 //cout << "Side " << iSide << " ";
9676 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9677 // find a face by two link nodes
9678 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9679 *faceSetPtr[ iSide ], avoidSet,
9680 &iLinkNode[iSide][0],
9681 &iLinkNode[iSide][1] );
9684 //cout << " F " << face[ iSide]->GetID() <<endl;
9685 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9686 // put face nodes to fnodes
9687 SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9688 fnodes[ iSide ].assign( nIt, nEnd );
9689 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9693 // check similarity of elements of the sides
9694 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9695 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9696 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9697 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9700 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9702 break; // do not return because it's necessary to remove tmp faces
9705 // set nodes to merge
9706 // -------------------
9708 if ( face[0] && face[1] ) {
9709 const int nbNodes = face[0]->NbNodes();
9710 if ( nbNodes != face[1]->NbNodes() ) {
9711 MESSAGE("Diff nb of face nodes");
9712 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9713 break; // do not return because it s necessary to remove tmp faces
9715 bool reverse[] = { false, false }; // order of nodes in the link
9716 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9717 // analyse link orientation in faces
9718 int i1 = iLinkNode[ iSide ][ 0 ];
9719 int i2 = iLinkNode[ iSide ][ 1 ];
9720 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9722 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9723 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9724 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9726 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9727 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9730 // add other links of the faces to linkList
9731 // -----------------------------------------
9733 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9734 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9735 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9736 if ( !iter_isnew.second ) { // already in a set: no need to process
9737 linkIdSet.erase( iter_isnew.first );
9739 else // new in set == encountered for the first time: add
9741 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9742 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9743 linkList[0].push_back ( NLink( n1, n2 ));
9744 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9749 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9752 } // loop on link lists
9754 if ( aResult == SEW_OK &&
9755 ( //linkIt[0] != linkList[0].end() ||
9756 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9757 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9758 " " << (faceSetPtr[1]->empty()));
9759 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9762 // ====================================================================
9763 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9764 // ====================================================================
9766 // delete temporary faces
9767 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9768 // while ( tmpFaceIt->more() )
9769 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9770 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9771 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9772 aMesh->RemoveElement(*tmpFaceIt);
9774 if ( aResult != SEW_OK)
9777 list< int > nodeIDsToRemove;
9778 vector< const SMDS_MeshNode*> nodes;
9779 ElemFeatures elemType;
9781 // loop on nodes replacement map
9782 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9783 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9784 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9786 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9787 nodeIDsToRemove.push_back( nToRemove->GetID() );
9788 // loop on elements sharing nToRemove
9789 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9790 while ( invElemIt->more() ) {
9791 const SMDS_MeshElement* e = invElemIt->next();
9792 // get a new suite of nodes: make replacement
9793 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9794 nodes.resize( nbNodes );
9795 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9796 while ( nIt->more() ) {
9797 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9798 nnIt = nReplaceMap.find( n );
9799 if ( nnIt != nReplaceMap.end() ) {
9805 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9806 // elemIDsToRemove.push_back( e->GetID() );
9810 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9811 aMesh->RemoveElement( e );
9813 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9815 AddToSameGroups( newElem, e, aMesh );
9816 if ( int aShapeId = e->getshapeId() )
9817 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9823 Remove( nodeIDsToRemove, true );
9828 //================================================================================
9830 * \brief Find corresponding nodes in two sets of faces
9831 * \param theSide1 - first face set
9832 * \param theSide2 - second first face
9833 * \param theFirstNode1 - a boundary node of set 1
9834 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9835 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9836 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9837 * \param nReplaceMap - output map of corresponding nodes
9838 * \return bool - is a success or not
9840 //================================================================================
9843 //#define DEBUG_MATCHING_NODES
9846 SMESH_MeshEditor::Sew_Error
9847 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9848 set<const SMDS_MeshElement*>& theSide2,
9849 const SMDS_MeshNode* theFirstNode1,
9850 const SMDS_MeshNode* theFirstNode2,
9851 const SMDS_MeshNode* theSecondNode1,
9852 const SMDS_MeshNode* theSecondNode2,
9853 TNodeNodeMap & nReplaceMap)
9855 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9857 nReplaceMap.clear();
9858 if ( theFirstNode1 != theFirstNode2 )
9859 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9860 if ( theSecondNode1 != theSecondNode2 )
9861 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9863 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9864 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9866 list< NLink > linkList[2];
9867 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9868 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9870 // loop on links in linkList; find faces by links and append links
9871 // of the found faces to linkList
9872 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9873 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9874 NLink link[] = { *linkIt[0], *linkIt[1] };
9875 if ( linkSet.find( link[0] ) == linkSet.end() )
9878 // by links, find faces in the face sets,
9879 // and find indices of link nodes in the found faces;
9880 // in a face set, there is only one or no face sharing a link
9881 // ---------------------------------------------------------------
9883 const SMDS_MeshElement* face[] = { 0, 0 };
9884 list<const SMDS_MeshNode*> notLinkNodes[2];
9885 //bool reverse[] = { false, false }; // order of notLinkNodes
9887 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9889 const SMDS_MeshNode* n1 = link[iSide].first;
9890 const SMDS_MeshNode* n2 = link[iSide].second;
9891 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9892 set< const SMDS_MeshElement* > facesOfNode1;
9893 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9895 // during a loop of the first node, we find all faces around n1,
9896 // during a loop of the second node, we find one face sharing both n1 and n2
9897 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9898 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9899 while ( fIt->more() ) { // loop on faces sharing a node
9900 const SMDS_MeshElement* f = fIt->next();
9901 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9902 ! facesOfNode1.insert( f ).second ) // f encounters twice
9904 if ( face[ iSide ] ) {
9905 MESSAGE( "2 faces per link " );
9906 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9909 faceSet->erase( f );
9911 // get not link nodes
9912 int nbN = f->NbNodes();
9913 if ( f->IsQuadratic() )
9915 nbNodes[ iSide ] = nbN;
9916 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9917 int i1 = f->GetNodeIndex( n1 );
9918 int i2 = f->GetNodeIndex( n2 );
9919 int iEnd = nbN, iBeg = -1, iDelta = 1;
9920 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9922 std::swap( iEnd, iBeg ); iDelta = -1;
9927 if ( i == iEnd ) i = iBeg + iDelta;
9928 if ( i == i1 ) break;
9929 nodes.push_back ( f->GetNode( i ) );
9935 // check similarity of elements of the sides
9936 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9937 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9938 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9939 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9942 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9946 // set nodes to merge
9947 // -------------------
9949 if ( face[0] && face[1] ) {
9950 if ( nbNodes[0] != nbNodes[1] ) {
9951 MESSAGE("Diff nb of face nodes");
9952 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9954 #ifdef DEBUG_MATCHING_NODES
9955 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9956 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9957 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9959 int nbN = nbNodes[0];
9961 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9962 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9963 for ( int i = 0 ; i < nbN - 2; ++i ) {
9964 #ifdef DEBUG_MATCHING_NODES
9965 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9967 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9971 // add other links of the face 1 to linkList
9972 // -----------------------------------------
9974 const SMDS_MeshElement* f0 = face[0];
9975 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
9976 for ( int i = 0; i < nbN; i++ )
9978 const SMDS_MeshNode* n2 = f0->GetNode( i );
9979 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
9980 linkSet.insert( SMESH_TLink( n1, n2 ));
9981 if ( !iter_isnew.second ) { // already in a set: no need to process
9982 linkSet.erase( iter_isnew.first );
9984 else // new in set == encountered for the first time: add
9986 #ifdef DEBUG_MATCHING_NODES
9987 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
9988 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
9990 linkList[0].push_back ( NLink( n1, n2 ));
9991 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9996 } // loop on link lists
10001 namespace // automatically find theAffectedElems for DoubleNodes()
10003 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10005 //--------------------------------------------------------------------------------
10006 // Nodes shared by adjacent FissureBorder's.
10007 // 1 node if FissureBorder separates faces
10008 // 2 nodes if FissureBorder separates volumes
10011 const SMDS_MeshNode* _nodes[2];
10014 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10018 _nbNodes = bool( n1 ) + bool( n2 );
10019 if ( _nbNodes == 2 && n1 > n2 )
10020 std::swap( _nodes[0], _nodes[1] );
10022 bool operator<( const SubBorder& other ) const
10024 for ( int i = 0; i < _nbNodes; ++i )
10026 if ( _nodes[i] < other._nodes[i] ) return true;
10027 if ( _nodes[i] > other._nodes[i] ) return false;
10033 //--------------------------------------------------------------------------------
10034 // Map a SubBorder to all FissureBorder it bounds
10035 struct FissureBorder;
10036 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10037 typedef TBorderLinks::iterator TMappedSub;
10039 //--------------------------------------------------------------------------------
10041 * \brief Element border (volume facet or face edge) at a fissure
10043 struct FissureBorder
10045 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10046 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10048 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10049 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10051 FissureBorder( FissureBorder && from ) // move constructor
10053 std::swap( _nodes, from._nodes );
10054 std::swap( _sortedNodes, from._sortedNodes );
10055 _elems[0] = from._elems[0];
10056 _elems[1] = from._elems[1];
10059 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10060 std::vector< const SMDS_MeshElement* > & adjElems)
10061 : _nodes( elemToDuplicate->NbCornerNodes() )
10063 for ( size_t i = 0; i < _nodes.size(); ++i )
10064 _nodes[i] = elemToDuplicate->GetNode( i );
10066 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10067 findAdjacent( type, adjElems );
10070 FissureBorder( const SMDS_MeshNode** nodes,
10071 const size_t nbNodes,
10072 const SMDSAbs_ElementType adjElemsType,
10073 std::vector< const SMDS_MeshElement* > & adjElems)
10074 : _nodes( nodes, nodes + nbNodes )
10076 findAdjacent( adjElemsType, adjElems );
10079 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10080 std::vector< const SMDS_MeshElement* > & adjElems)
10082 _elems[0] = _elems[1] = 0;
10084 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10085 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10086 _elems[i] = adjElems[i];
10089 bool operator<( const FissureBorder& other ) const
10091 return GetSortedNodes() < other.GetSortedNodes();
10094 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10096 if ( _sortedNodes.empty() && !_nodes.empty() )
10098 FissureBorder* me = const_cast<FissureBorder*>( this );
10099 me->_sortedNodes = me->_nodes;
10100 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10102 return _sortedNodes;
10105 size_t NbSub() const
10107 return _nodes.size();
10110 SubBorder Sub(size_t i) const
10112 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10115 void AddSelfTo( TBorderLinks& borderLinks )
10117 _mappedSubs.resize( NbSub() );
10118 for ( size_t i = 0; i < NbSub(); ++i )
10120 TBorderLinks::iterator s2b =
10121 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10122 s2b->second.push_back( this );
10123 _mappedSubs[ i ] = s2b;
10132 const SMDS_MeshElement* GetMarkedElem() const
10134 if ( _nodes.empty() ) return 0; // cleared
10135 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10136 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10140 gp_XYZ GetNorm() const // normal to the border
10143 if ( _nodes.size() == 2 )
10145 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10146 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10148 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10151 gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10152 norm = bordDir ^ avgNorm;
10156 SMESH_NodeXYZ p0( _nodes[0] );
10157 SMESH_NodeXYZ p1( _nodes[1] );
10158 SMESH_NodeXYZ p2( _nodes[2] );
10159 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10161 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10167 void ChooseSide() // mark an _elem located at positive side of fissure
10169 _elems[0]->setIsMarked( true );
10170 gp_XYZ norm = GetNorm();
10171 double maxX = norm.Coord(1);
10172 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10173 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10176 _elems[0]->setIsMarked( false );
10177 _elems[1]->setIsMarked( true );
10181 }; // struct FissureBorder
10183 //--------------------------------------------------------------------------------
10185 * \brief Classifier of elements at fissure edge
10187 class FissureNormal
10189 std::vector< gp_XYZ > _normals;
10193 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10196 _normals.reserve(2);
10197 _normals.push_back( bord.GetNorm() );
10198 if ( _normals.size() == 2 )
10199 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10202 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10205 switch ( _normals.size() ) {
10208 isIn = !isOut( n, _normals[0], elem );
10213 bool in1 = !isOut( n, _normals[0], elem );
10214 bool in2 = !isOut( n, _normals[1], elem );
10215 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10222 //================================================================================
10224 * \brief Classify an element by a plane passing through a node
10226 //================================================================================
10228 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10230 SMESH_NodeXYZ p = n;
10232 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10234 SMESH_NodeXYZ pi = elem->GetNode( i );
10235 sumDot += norm * ( pi - p );
10237 return sumDot < -1e-100;
10240 //================================================================================
10242 * \brief Find FissureBorder's by nodes to duplicate
10244 //================================================================================
10246 void findFissureBorders( const TIDSortedElemSet& theNodes,
10247 std::vector< FissureBorder > & theFissureBorders )
10249 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10250 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10252 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10253 if ( n->NbInverseElements( elemType ) == 0 )
10255 elemType = SMDSAbs_Face;
10256 if ( n->NbInverseElements( elemType ) == 0 )
10259 // unmark elements touching the fissure
10260 for ( ; nIt != theNodes.end(); ++nIt )
10261 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10263 // loop on elements touching the fissure to get their borders belonging to the fissure
10264 std::set< FissureBorder > fissureBorders;
10265 std::vector< const SMDS_MeshElement* > adjElems;
10266 std::vector< const SMDS_MeshNode* > nodes;
10267 SMDS_VolumeTool volTool;
10268 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10270 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10271 while ( invIt->more() )
10273 const SMDS_MeshElement* eInv = invIt->next();
10274 if ( eInv->isMarked() ) continue;
10275 eInv->setIsMarked( true );
10277 if ( elemType == SMDSAbs_Volume )
10279 volTool.Set( eInv );
10280 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10281 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10283 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10284 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10286 bool allOnFissure = true;
10287 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10288 if (( allOnFissure = theNodes.count( nn[ iN ])))
10289 nodes.push_back( nn[ iN ]);
10290 if ( allOnFissure )
10291 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10292 elemType, adjElems )));
10295 else // elemType == SMDSAbs_Face
10297 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10298 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10299 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10301 nn[1] = eInv->GetNode( iN );
10302 onFissure1 = theNodes.count( nn[1] );
10303 if ( onFissure0 && onFissure1 )
10304 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10306 onFissure0 = onFissure1;
10312 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10313 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10314 for ( ; bord != fissureBorders.end(); ++bord )
10316 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10319 } // findFissureBorders()
10321 //================================================================================
10323 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10324 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10325 * \param [in] theNodesNot - nodes not to duplicate
10326 * \param [out] theAffectedElems - the found elements
10328 //================================================================================
10330 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10331 TIDSortedElemSet& theAffectedElems)
10333 if ( theElemsOrNodes.empty() ) return;
10335 // find FissureBorder's
10337 std::vector< FissureBorder > fissure;
10338 std::vector< const SMDS_MeshElement* > elemsByFacet;
10340 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10341 if ( (*elIt)->GetType() == SMDSAbs_Node )
10343 findFissureBorders( theElemsOrNodes, fissure );
10347 fissure.reserve( theElemsOrNodes.size() );
10348 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10349 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10351 if ( fissure.empty() )
10354 // fill borderLinks
10356 TBorderLinks borderLinks;
10358 for ( size_t i = 0; i < fissure.size(); ++i )
10360 fissure[i].AddSelfTo( borderLinks );
10363 // get theAffectedElems
10365 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10366 for ( size_t i = 0; i < fissure.size(); ++i )
10367 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10369 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10370 false, /*markElem=*/true );
10373 std::vector<const SMDS_MeshNode *> facetNodes;
10374 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10375 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10377 // choose a side of fissure
10378 fissure[0].ChooseSide();
10379 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10381 size_t nbCheckedBorders = 0;
10382 while ( nbCheckedBorders < fissure.size() )
10384 // find a FissureBorder to treat
10385 FissureBorder* bord = 0;
10386 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10387 if ( fissure[i].GetMarkedElem() )
10388 bord = & fissure[i];
10389 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10390 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10392 bord = & fissure[i];
10393 bord->ChooseSide();
10394 theAffectedElems.insert( bord->GetMarkedElem() );
10396 if ( !bord ) return;
10397 ++nbCheckedBorders;
10399 // treat FissureBorder's linked to bord
10400 fissureNodes.clear();
10401 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10402 for ( size_t i = 0; i < bord->NbSub(); ++i )
10404 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10405 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10406 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10407 const SubBorder& sb = l2b->first;
10408 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10410 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10412 for ( int j = 0; j < sb._nbNodes; ++j )
10413 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10417 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10418 // until an elem adjacent to a neighbour FissureBorder is found
10419 facetNodes.clear();
10420 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10421 facetNodes.resize( sb._nbNodes + 1 );
10425 // check if bordElem is adjacent to a neighbour FissureBorder
10426 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10428 FissureBorder* bord2 = linkedBorders[j];
10429 if ( bord2 == bord ) continue;
10430 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10433 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10438 // find the next bordElem
10439 const SMDS_MeshElement* nextBordElem = 0;
10440 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10442 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10443 if ( fissureNodes.count( n )) continue;
10445 facetNodes[ sb._nbNodes ] = n;
10446 elemsByFacet.clear();
10447 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10449 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10450 if ( elemsByFacet[ iE ] != bordElem &&
10451 !elemsByFacet[ iE ]->isMarked() )
10453 theAffectedElems.insert( elemsByFacet[ iE ]);
10454 elemsByFacet[ iE ]->setIsMarked( true );
10455 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10456 nextBordElem = elemsByFacet[ iE ];
10460 bordElem = nextBordElem;
10462 } // while ( bordElem )
10464 linkedBorders.clear(); // not to treat this link any more
10466 } // loop on SubBorder's of a FissureBorder
10470 } // loop on FissureBorder's
10473 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10475 // mark nodes of theAffectedElems
10476 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10478 // unmark nodes of the fissure
10479 elIt = theElemsOrNodes.begin();
10480 if ( (*elIt)->GetType() == SMDSAbs_Node )
10481 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10483 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10485 std::vector< gp_XYZ > normVec;
10487 // loop on nodes of the fissure, add elements having marked nodes
10488 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10490 const SMDS_MeshElement* e = (*elIt);
10491 if ( e->GetType() != SMDSAbs_Node )
10492 e->setIsMarked( true ); // avoid adding a fissure element
10494 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10496 const SMDS_MeshNode* n = e->GetNode( iN );
10497 if ( fissEdgeNodes2Norm.count( n ))
10500 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10501 while ( invIt->more() )
10503 const SMDS_MeshElement* eInv = invIt->next();
10504 if ( eInv->isMarked() ) continue;
10505 eInv->setIsMarked( true );
10507 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10508 while( nIt->more() )
10509 if ( nIt->next()->isMarked())
10511 theAffectedElems.insert( eInv );
10512 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10513 n->setIsMarked( false );
10520 // add elements on the fissure edge
10521 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10522 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10524 const SMDS_MeshNode* edgeNode = n2N->first;
10525 const FissureNormal & normals = n2N->second;
10527 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10528 while ( invIt->more() )
10530 const SMDS_MeshElement* eInv = invIt->next();
10531 if ( eInv->isMarked() ) continue;
10532 eInv->setIsMarked( true );
10534 // classify eInv using normals
10535 bool toAdd = normals.IsIn( edgeNode, eInv );
10536 if ( toAdd ) // check if all nodes lie on the fissure edge
10538 bool notOnEdge = false;
10539 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
10540 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10545 theAffectedElems.insert( eInv );
10551 } // findAffectedElems()
10554 //================================================================================
10556 * \brief Create elements equal (on same nodes) to given ones
10557 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10558 * elements of the uppest dimension are duplicated.
10560 //================================================================================
10562 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10564 ClearLastCreated();
10565 SMESHDS_Mesh* mesh = GetMeshDS();
10567 // get an element type and an iterator over elements
10569 SMDSAbs_ElementType type = SMDSAbs_All;
10570 SMDS_ElemIteratorPtr elemIt;
10571 if ( theElements.empty() )
10573 if ( mesh->NbNodes() == 0 )
10575 // get most complex type
10576 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10577 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10578 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10580 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10581 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10584 elemIt = mesh->elementsIterator( type );
10590 //type = (*theElements.begin())->GetType();
10591 elemIt = SMESHUtils::elemSetIterator( theElements );
10594 // un-mark all elements to avoid duplicating just created elements
10595 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10597 // duplicate elements
10599 ElemFeatures elemType;
10601 vector< const SMDS_MeshNode* > nodes;
10602 while ( elemIt->more() )
10604 const SMDS_MeshElement* elem = elemIt->next();
10605 if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10606 ( elem->isMarked() ))
10609 elemType.Init( elem, /*basicOnly=*/false );
10610 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10612 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10613 newElem->setIsMarked( true );
10617 //================================================================================
10619 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10620 \param theElems - the list of elements (edges or faces) to be replicated
10621 The nodes for duplication could be found from these elements
10622 \param theNodesNot - list of nodes to NOT replicate
10623 \param theAffectedElems - the list of elements (cells and edges) to which the
10624 replicated nodes should be associated to.
10625 \return TRUE if operation has been completed successfully, FALSE otherwise
10627 //================================================================================
10629 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10630 const TIDSortedElemSet& theNodesNot,
10631 const TIDSortedElemSet& theAffectedElems )
10633 ClearLastCreated();
10635 if ( theElems.size() == 0 )
10638 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10643 TNodeNodeMap anOldNodeToNewNode;
10644 // duplicate elements and nodes
10645 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10646 // replce nodes by duplications
10647 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10651 //================================================================================
10653 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10654 \param theMeshDS - mesh instance
10655 \param theElems - the elements replicated or modified (nodes should be changed)
10656 \param theNodesNot - nodes to NOT replicate
10657 \param theNodeNodeMap - relation of old node to new created node
10658 \param theIsDoubleElem - flag os to replicate element or modify
10659 \return TRUE if operation has been completed successfully, FALSE otherwise
10661 //================================================================================
10663 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10664 const TIDSortedElemSet& theElems,
10665 const TIDSortedElemSet& theNodesNot,
10666 TNodeNodeMap& theNodeNodeMap,
10667 const bool theIsDoubleElem )
10669 // iterate through element and duplicate them (by nodes duplication)
10671 std::vector<const SMDS_MeshNode*> newNodes;
10672 ElemFeatures elemType;
10674 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10675 for ( ; elemItr != theElems.end(); ++elemItr )
10677 const SMDS_MeshElement* anElem = *elemItr;
10681 // duplicate nodes to duplicate element
10682 bool isDuplicate = false;
10683 newNodes.resize( anElem->NbNodes() );
10684 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10686 while ( anIter->more() )
10688 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10689 const SMDS_MeshNode* aNewNode = aCurrNode;
10690 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10691 if ( n2n != theNodeNodeMap.end() )
10693 aNewNode = n2n->second;
10695 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10698 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10699 copyPosition( aCurrNode, aNewNode );
10700 theNodeNodeMap[ aCurrNode ] = aNewNode;
10701 myLastCreatedNodes.push_back( aNewNode );
10703 isDuplicate |= (aCurrNode != aNewNode);
10704 newNodes[ ind++ ] = aNewNode;
10706 if ( !isDuplicate )
10709 if ( theIsDoubleElem )
10710 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10712 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10719 //================================================================================
10721 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10722 \param theNodes - identifiers of nodes to be doubled
10723 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10724 nodes. If list of element identifiers is empty then nodes are doubled but
10725 they not assigned to elements
10726 \return TRUE if operation has been completed successfully, FALSE otherwise
10728 //================================================================================
10730 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10731 const std::list< int >& theListOfModifiedElems )
10733 ClearLastCreated();
10735 if ( theListOfNodes.size() == 0 )
10738 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10742 // iterate through nodes and duplicate them
10744 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10746 std::list< int >::const_iterator aNodeIter;
10747 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10749 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10755 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10758 copyPosition( aNode, aNewNode );
10759 anOldNodeToNewNode[ aNode ] = aNewNode;
10760 myLastCreatedNodes.push_back( aNewNode );
10764 // Change nodes of elements
10766 std::vector<const SMDS_MeshNode*> aNodeArr;
10768 std::list< int >::const_iterator anElemIter;
10769 for ( anElemIter = theListOfModifiedElems.begin();
10770 anElemIter != theListOfModifiedElems.end();
10773 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10777 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10778 for( size_t i = 0; i < aNodeArr.size(); ++i )
10780 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10781 anOldNodeToNewNode.find( aNodeArr[ i ]);
10782 if ( n2n != anOldNodeToNewNode.end() )
10783 aNodeArr[ i ] = n2n->second;
10785 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10793 //================================================================================
10795 \brief Check if element located inside shape
10796 \return TRUE if IN or ON shape, FALSE otherwise
10798 //================================================================================
10800 template<class Classifier>
10801 bool isInside(const SMDS_MeshElement* theElem,
10802 Classifier& theClassifier,
10803 const double theTol)
10805 gp_XYZ centerXYZ (0, 0, 0);
10806 for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10807 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10809 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10810 theClassifier.Perform(aPnt, theTol);
10811 TopAbs_State aState = theClassifier.State();
10812 return (aState == TopAbs_IN || aState == TopAbs_ON );
10815 //================================================================================
10817 * \brief Classifier of the 3D point on the TopoDS_Face
10818 * with interaface suitable for isInside()
10820 //================================================================================
10822 struct _FaceClassifier
10824 Extrema_ExtPS _extremum;
10825 BRepAdaptor_Surface _surface;
10826 TopAbs_State _state;
10828 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10830 _extremum.Initialize( _surface,
10831 _surface.FirstUParameter(), _surface.LastUParameter(),
10832 _surface.FirstVParameter(), _surface.LastVParameter(),
10833 _surface.Tolerance(), _surface.Tolerance() );
10835 void Perform(const gp_Pnt& aPnt, double theTol)
10838 _state = TopAbs_OUT;
10839 _extremum.Perform(aPnt);
10840 if ( _extremum.IsDone() )
10841 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10842 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10844 TopAbs_State State() const
10851 //================================================================================
10853 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10854 This method is the first step of DoubleNodeElemGroupsInRegion.
10855 \param theElems - list of groups of elements (edges or faces) to be replicated
10856 \param theNodesNot - list of groups of nodes not to replicated
10857 \param theShape - shape to detect affected elements (element which geometric center
10858 located on or inside shape). If the shape is null, detection is done on faces orientations
10859 (select elements with a gravity center on the side given by faces normals).
10860 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10861 The replicated nodes should be associated to affected elements.
10863 \sa DoubleNodeElemGroupsInRegion()
10865 //================================================================================
10867 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10868 const TIDSortedElemSet& theNodesNot,
10869 const TopoDS_Shape& theShape,
10870 TIDSortedElemSet& theAffectedElems)
10872 if ( theShape.IsNull() )
10874 findAffectedElems( theElems, theAffectedElems );
10878 const double aTol = Precision::Confusion();
10879 std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10880 std::unique_ptr<_FaceClassifier> aFaceClassifier;
10881 if ( theShape.ShapeType() == TopAbs_SOLID )
10883 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10884 bsc3d->PerformInfinitePoint(aTol);
10886 else if (theShape.ShapeType() == TopAbs_FACE )
10888 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10891 // iterates on indicated elements and get elements by back references from their nodes
10892 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10893 for ( ; elemItr != theElems.end(); ++elemItr )
10895 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10896 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10897 while ( nodeItr->more() )
10899 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10900 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10902 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10903 while ( backElemItr->more() )
10905 const SMDS_MeshElement* curElem = backElemItr->next();
10906 if ( curElem && theElems.find(curElem) == theElems.end() &&
10908 isInside( curElem, *bsc3d, aTol ) :
10909 isInside( curElem, *aFaceClassifier, aTol )))
10910 theAffectedElems.insert( curElem );
10918 //================================================================================
10920 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10921 \param theElems - group of of elements (edges or faces) to be replicated
10922 \param theNodesNot - group of nodes not to replicate
10923 \param theShape - shape to detect affected elements (element which geometric center
10924 located on or inside shape).
10925 The replicated nodes should be associated to affected elements.
10926 \return TRUE if operation has been completed successfully, FALSE otherwise
10928 //================================================================================
10930 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10931 const TIDSortedElemSet& theNodesNot,
10932 const TopoDS_Shape& theShape )
10934 if ( theShape.IsNull() )
10937 const double aTol = Precision::Confusion();
10938 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10939 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
10940 if ( theShape.ShapeType() == TopAbs_SOLID )
10942 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10943 bsc3d->PerformInfinitePoint(aTol);
10945 else if (theShape.ShapeType() == TopAbs_FACE )
10947 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
10950 // iterates on indicated elements and get elements by back references from their nodes
10951 TIDSortedElemSet anAffected;
10952 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10953 for ( ; elemItr != theElems.end(); ++elemItr )
10955 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10959 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10960 while ( nodeItr->more() )
10962 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10963 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10965 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10966 while ( backElemItr->more() )
10968 const SMDS_MeshElement* curElem = backElemItr->next();
10969 if ( curElem && theElems.find(curElem) == theElems.end() &&
10971 isInside( curElem, *bsc3d, aTol ) :
10972 isInside( curElem, *aFaceClassifier, aTol )))
10973 anAffected.insert( curElem );
10977 return DoubleNodes( theElems, theNodesNot, anAffected );
10981 * \brief compute an oriented angle between two planes defined by four points.
10982 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10983 * @param p0 base of the rotation axe
10984 * @param p1 extremity of the rotation axe
10985 * @param g1 belongs to the first plane
10986 * @param g2 belongs to the second plane
10988 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10990 gp_Vec vref(p0, p1);
10993 gp_Vec n1 = vref.Crossed(v1);
10994 gp_Vec n2 = vref.Crossed(v2);
10996 return n2.AngleWithRef(n1, vref);
10998 catch ( Standard_Failure ) {
11000 return Max( v1.Magnitude(), v2.Magnitude() );
11004 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11005 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11006 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11007 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11008 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11009 * 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.
11010 * 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.
11011 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11012 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11013 * \param theElems - list of groups of volumes, where a group of volume is a set of
11014 * SMDS_MeshElements sorted by Id.
11015 * \param createJointElems - if TRUE, create the elements
11016 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11017 * the boundary between \a theDomains and the rest mesh
11018 * \return TRUE if operation has been completed successfully, FALSE otherwise
11020 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11021 bool createJointElems,
11022 bool onAllBoundaries)
11024 // MESSAGE("----------------------------------------------");
11025 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11026 // MESSAGE("----------------------------------------------");
11028 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11029 meshDS->BuildDownWardConnectivity(true);
11031 SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11033 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11034 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11035 // build the list of nodes shared by 2 or more domains, with their domain indexes
11037 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11038 std::map<int,int>celldom; // cell vtkId --> domain
11039 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11040 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11041 faceDomains.clear();
11043 cellDomains.clear();
11044 nodeDomains.clear();
11045 std::map<int,int> emptyMap;
11046 std::set<int> emptySet;
11049 //MESSAGE(".. Number of domains :"<<theElems.size());
11051 TIDSortedElemSet theRestDomElems;
11052 const int iRestDom = -1;
11053 const int idom0 = onAllBoundaries ? iRestDom : 0;
11054 const int nbDomains = theElems.size();
11056 // Check if the domains do not share an element
11057 for (int idom = 0; idom < nbDomains-1; idom++)
11059 // MESSAGE("... Check of domain #" << idom);
11060 const TIDSortedElemSet& domain = theElems[idom];
11061 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11062 for (; elemItr != domain.end(); ++elemItr)
11064 const SMDS_MeshElement* anElem = *elemItr;
11065 int idombisdeb = idom + 1 ;
11066 // check if the element belongs to a domain further in the list
11067 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11069 const TIDSortedElemSet& domainbis = theElems[idombis];
11070 if ( domainbis.count( anElem ))
11072 MESSAGE(".... Domain #" << idom);
11073 MESSAGE(".... Domain #" << idombis);
11074 throw SALOME_Exception("The domains are not disjoint.");
11081 for (int idom = 0; idom < nbDomains; idom++)
11084 // --- build a map (face to duplicate --> volume to modify)
11085 // with all the faces shared by 2 domains (group of elements)
11086 // and corresponding volume of this domain, for each shared face.
11087 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11089 //MESSAGE("... Neighbors of domain #" << idom);
11090 const TIDSortedElemSet& domain = theElems[idom];
11091 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11092 for (; elemItr != domain.end(); ++elemItr)
11094 const SMDS_MeshElement* anElem = *elemItr;
11097 int vtkId = anElem->GetVtkID();
11098 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11099 int neighborsVtkIds[NBMAXNEIGHBORS];
11100 int downIds[NBMAXNEIGHBORS];
11101 unsigned char downTypes[NBMAXNEIGHBORS];
11102 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11103 for (int n = 0; n < nbNeighbors; n++)
11105 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11106 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11107 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11110 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11112 // MESSAGE("Domain " << idombis);
11113 const TIDSortedElemSet& domainbis = theElems[idombis];
11114 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11116 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11118 DownIdType face(downIds[n], downTypes[n]);
11119 if (!faceDomains[face].count(idom))
11121 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11122 celldom[vtkId] = idom;
11123 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11127 theRestDomElems.insert( elem );
11128 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11129 celldom[neighborsVtkIds[n]] = iRestDom;
11137 //MESSAGE("Number of shared faces " << faceDomains.size());
11138 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11140 // --- explore the shared faces domain by domain,
11141 // explore the nodes of the face and see if they belong to a cell in the domain,
11142 // which has only a node or an edge on the border (not a shared face)
11144 for (int idomain = idom0; idomain < nbDomains; idomain++)
11146 //MESSAGE("Domain " << idomain);
11147 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11148 itface = faceDomains.begin();
11149 for (; itface != faceDomains.end(); ++itface)
11151 const std::map<int, int>& domvol = itface->second;
11152 if (!domvol.count(idomain))
11154 DownIdType face = itface->first;
11155 //MESSAGE(" --- face " << face.cellId);
11156 std::set<int> oldNodes;
11158 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11159 std::set<int>::iterator itn = oldNodes.begin();
11160 for (; itn != oldNodes.end(); ++itn)
11163 //MESSAGE(" node " << oldId);
11164 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11165 for (int i=0; i<l.ncells; i++)
11167 int vtkId = l.cells[i];
11168 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11169 if (!domain.count(anElem))
11171 int vtkType = grid->GetCellType(vtkId);
11172 int downId = grid->CellIdToDownId(vtkId);
11175 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11176 continue; // not OK at this stage of the algorithm:
11177 //no cells created after BuildDownWardConnectivity
11179 DownIdType aCell(downId, vtkType);
11180 cellDomains[aCell][idomain] = vtkId;
11181 celldom[vtkId] = idomain;
11182 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11188 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11189 // for each shared face, get the nodes
11190 // for each node, for each domain of the face, create a clone of the node
11192 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11193 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11194 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11196 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11197 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11198 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11200 //MESSAGE(".. Duplication of the nodes");
11201 for (int idomain = idom0; idomain < nbDomains; idomain++)
11203 itface = faceDomains.begin();
11204 for (; itface != faceDomains.end(); ++itface)
11206 const std::map<int, int>& domvol = itface->second;
11207 if (!domvol.count(idomain))
11209 DownIdType face = itface->first;
11210 //MESSAGE(" --- face " << face.cellId);
11211 std::set<int> oldNodes;
11213 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11214 std::set<int>::iterator itn = oldNodes.begin();
11215 for (; itn != oldNodes.end(); ++itn)
11218 if (nodeDomains[oldId].empty())
11220 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11221 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11223 std::map<int, int>::const_iterator itdom = domvol.begin();
11224 for (; itdom != domvol.end(); ++itdom)
11226 int idom = itdom->first;
11227 //MESSAGE(" domain " << idom);
11228 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11230 if (nodeDomains[oldId].size() >= 2) // a multiple node
11232 vector<int> orderedDoms;
11233 //MESSAGE("multiple node " << oldId);
11234 if (mutipleNodes.count(oldId))
11235 orderedDoms = mutipleNodes[oldId];
11238 map<int,int>::iterator it = nodeDomains[oldId].begin();
11239 for (; it != nodeDomains[oldId].end(); ++it)
11240 orderedDoms.push_back(it->first);
11242 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11243 //stringstream txt;
11244 //for (int i=0; i<orderedDoms.size(); i++)
11245 // txt << orderedDoms[i] << " ";
11246 //MESSAGE("orderedDoms " << txt.str());
11247 mutipleNodes[oldId] = orderedDoms;
11249 double *coords = grid->GetPoint(oldId);
11250 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11251 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11252 int newId = newNode->GetVtkID();
11253 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11254 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11261 //MESSAGE(".. Creation of elements");
11262 for (int idomain = idom0; idomain < nbDomains; idomain++)
11264 itface = faceDomains.begin();
11265 for (; itface != faceDomains.end(); ++itface)
11267 std::map<int, int> domvol = itface->second;
11268 if (!domvol.count(idomain))
11270 DownIdType face = itface->first;
11271 //MESSAGE(" --- face " << face.cellId);
11272 std::set<int> oldNodes;
11274 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11275 int nbMultipleNodes = 0;
11276 std::set<int>::iterator itn = oldNodes.begin();
11277 for (; itn != oldNodes.end(); ++itn)
11280 if (mutipleNodes.count(oldId))
11283 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11285 //MESSAGE("multiple Nodes detected on a shared face");
11286 int downId = itface->first.cellId;
11287 unsigned char cellType = itface->first.cellType;
11288 // --- shared edge or shared face ?
11289 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11292 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11293 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11294 if (mutipleNodes.count(nodes[i]))
11295 if (!mutipleNodesToFace.count(nodes[i]))
11296 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11298 else // shared face (between two volumes)
11300 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11301 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11302 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11303 for (int ie =0; ie < nbEdges; ie++)
11306 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11307 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11309 vector<int> vn0 = mutipleNodes[nodes[0]];
11310 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11312 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11313 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11314 if ( vn0[i0] == vn1[i1] )
11315 doms.push_back( vn0[ i0 ]);
11316 if ( doms.size() > 2 )
11318 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11319 double *coords = grid->GetPoint(nodes[0]);
11320 gp_Pnt p0(coords[0], coords[1], coords[2]);
11321 coords = grid->GetPoint(nodes[nbNodes - 1]);
11322 gp_Pnt p1(coords[0], coords[1], coords[2]);
11324 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11325 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11326 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11327 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11328 for ( size_t id = 0; id < doms.size(); id++ )
11330 int idom = doms[id];
11331 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11332 for ( int ivol = 0; ivol < nbvol; ivol++ )
11334 int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11335 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11336 if (domain.count(elem))
11338 const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11339 domvol[idom] = (SMDS_MeshVolume*) svol;
11340 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11341 double values[3] = { 0,0,0 };
11342 vtkIdType npts = 0;
11343 vtkIdType* pts = 0;
11344 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11345 for ( vtkIdType i = 0; i < npts; ++i )
11347 double *coords = grid->GetPoint( pts[i] );
11348 for ( int j = 0; j < 3; ++j )
11349 values[j] += coords[j] / npts;
11353 gref.SetCoord( values[0], values[1], values[2] );
11354 angleDom[idom] = 0;
11358 gp_Pnt g( values[0], values[1], values[2] );
11359 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11360 //MESSAGE(" angle=" << angleDom[idom]);
11366 map<double, int> sortedDom; // sort domains by angle
11367 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11368 sortedDom[ia->second] = ia->first;
11369 vector<int> vnodes;
11371 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11373 vdom.push_back(ib->second);
11374 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11376 for (int ino = 0; ino < nbNodes; ino++)
11377 vnodes.push_back(nodes[ino]);
11378 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11387 // --- iterate on shared faces (volumes to modify, face to extrude)
11388 // get node id's of the face (id SMDS = id VTK)
11389 // create flat element with old and new nodes if requested
11391 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11392 // (domain1 X domain2) = domain1 + MAXINT*domain2
11394 std::map<int, std::map<long,int> > nodeQuadDomains;
11395 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11397 //MESSAGE(".. Creation of elements: simple junction");
11398 if (createJointElems)
11400 string joints2DName = "joints2D";
11401 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11402 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11403 string joints3DName = "joints3D";
11404 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11405 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11407 itface = faceDomains.begin();
11408 for (; itface != faceDomains.end(); ++itface)
11410 DownIdType face = itface->first;
11411 std::set<int> oldNodes;
11412 std::set<int>::iterator itn;
11414 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11416 std::map<int, int> domvol = itface->second;
11417 std::map<int, int>::iterator itdom = domvol.begin();
11418 int dom1 = itdom->first;
11419 int vtkVolId = itdom->second;
11421 int dom2 = itdom->first;
11422 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11424 stringstream grpname;
11427 grpname << dom1 << "_" << dom2;
11429 grpname << dom2 << "_" << dom1;
11430 string namegrp = grpname.str();
11431 if (!mapOfJunctionGroups.count(namegrp))
11432 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11433 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11435 sgrp->Add(vol->GetID());
11436 if (vol->GetType() == SMDSAbs_Volume)
11437 joints3DGrp->Add(vol->GetID());
11438 else if (vol->GetType() == SMDSAbs_Face)
11439 joints2DGrp->Add(vol->GetID());
11443 // --- create volumes on multiple domain intersection if requested
11444 // iterate on mutipleNodesToFace
11445 // iterate on edgesMultiDomains
11447 //MESSAGE(".. Creation of elements: multiple junction");
11448 if (createJointElems)
11450 // --- iterate on mutipleNodesToFace
11452 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11453 for (; itn != mutipleNodesToFace.end(); ++itn)
11455 int node = itn->first;
11456 vector<int> orderDom = itn->second;
11457 vector<vtkIdType> orderedNodes;
11458 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11459 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11460 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11462 stringstream grpname;
11464 grpname << 0 << "_" << 0;
11465 string namegrp = grpname.str();
11466 if (!mapOfJunctionGroups.count(namegrp))
11467 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11468 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11470 sgrp->Add(face->GetID());
11473 // --- iterate on edgesMultiDomains
11475 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11476 for (; ite != edgesMultiDomains.end(); ++ite)
11478 vector<int> nodes = ite->first;
11479 vector<int> orderDom = ite->second;
11480 vector<vtkIdType> orderedNodes;
11481 if (nodes.size() == 2)
11483 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11484 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11485 if ( orderDom.size() == 3 )
11486 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11487 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11489 for (int idom = orderDom.size()-1; idom >=0; idom--)
11490 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11491 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11493 string namegrp = "jointsMultiples";
11494 if (!mapOfJunctionGroups.count(namegrp))
11495 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11496 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11498 sgrp->Add(vol->GetID());
11502 //INFOS("Quadratic multiple joints not implemented");
11503 // TODO quadratic nodes
11508 // --- list the explicit faces and edges of the mesh that need to be modified,
11509 // i.e. faces and edges built with one or more duplicated nodes.
11510 // associate these faces or edges to their corresponding domain.
11511 // only the first domain found is kept when a face or edge is shared
11513 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11514 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11515 faceOrEdgeDom.clear();
11518 //MESSAGE(".. Modification of elements");
11519 for (int idomain = idom0; idomain < nbDomains; idomain++)
11521 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11522 for (; itnod != nodeDomains.end(); ++itnod)
11524 int oldId = itnod->first;
11525 //MESSAGE(" node " << oldId);
11526 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11527 for (int i = 0; i < l.ncells; i++)
11529 int vtkId = l.cells[i];
11530 int vtkType = grid->GetCellType(vtkId);
11531 int downId = grid->CellIdToDownId(vtkId);
11533 continue; // new cells: not to be modified
11534 DownIdType aCell(downId, vtkType);
11535 int volParents[1000];
11536 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11537 for (int j = 0; j < nbvol; j++)
11538 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11539 if (!feDom.count(vtkId))
11541 feDom[vtkId] = idomain;
11542 faceOrEdgeDom[aCell] = emptyMap;
11543 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11544 //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11545 // << " type " << vtkType << " downId " << downId);
11551 // --- iterate on shared faces (volumes to modify, face to extrude)
11552 // get node id's of the face
11553 // replace old nodes by new nodes in volumes, and update inverse connectivity
11555 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11556 for (int m=0; m<3; m++)
11558 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11559 itface = (*amap).begin();
11560 for (; itface != (*amap).end(); ++itface)
11562 DownIdType face = itface->first;
11563 std::set<int> oldNodes;
11564 std::set<int>::iterator itn;
11566 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11567 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11568 std::map<int, int> localClonedNodeIds;
11570 std::map<int, int> domvol = itface->second;
11571 std::map<int, int>::iterator itdom = domvol.begin();
11572 for (; itdom != domvol.end(); ++itdom)
11574 int idom = itdom->first;
11575 int vtkVolId = itdom->second;
11576 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11577 localClonedNodeIds.clear();
11578 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11581 if (nodeDomains[oldId].count(idom))
11583 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11584 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11587 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11592 // Remove empty groups (issue 0022812)
11593 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11594 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11596 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11597 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11600 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11601 grid->DeleteLinks();
11609 * \brief Double nodes on some external faces and create flat elements.
11610 * Flat elements are mainly used by some types of mechanic calculations.
11612 * Each group of the list must be constituted of faces.
11613 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11614 * @param theElems - list of groups of faces, where a group of faces is a set of
11615 * SMDS_MeshElements sorted by Id.
11616 * @return TRUE if operation has been completed successfully, FALSE otherwise
11618 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11620 // MESSAGE("-------------------------------------------------");
11621 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11622 // MESSAGE("-------------------------------------------------");
11624 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11626 // --- For each group of faces
11627 // duplicate the nodes, create a flat element based on the face
11628 // replace the nodes of the faces by their clones
11630 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11631 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11632 clonedNodes.clear();
11633 intermediateNodes.clear();
11634 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11635 mapOfJunctionGroups.clear();
11637 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11639 const TIDSortedElemSet& domain = theElems[idom];
11640 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11641 for ( ; elemItr != domain.end(); ++elemItr )
11643 const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11646 // MESSAGE("aFace=" << aFace->GetID());
11647 bool isQuad = aFace->IsQuadratic();
11648 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11650 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11652 SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11653 while (nodeIt->more())
11655 const SMDS_MeshNode* node = nodeIt->next();
11656 bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11658 ln2.push_back(node);
11660 ln0.push_back(node);
11662 const SMDS_MeshNode* clone = 0;
11663 if (!clonedNodes.count(node))
11665 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11666 copyPosition( node, clone );
11667 clonedNodes[node] = clone;
11670 clone = clonedNodes[node];
11673 ln3.push_back(clone);
11675 ln1.push_back(clone);
11677 const SMDS_MeshNode* inter = 0;
11678 if (isQuad && (!isMedium))
11680 if (!intermediateNodes.count(node))
11682 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11683 copyPosition( node, inter );
11684 intermediateNodes[node] = inter;
11687 inter = intermediateNodes[node];
11688 ln4.push_back(inter);
11692 // --- extrude the face
11694 vector<const SMDS_MeshNode*> ln;
11695 SMDS_MeshVolume* vol = 0;
11696 vtkIdType aType = aFace->GetVtkType();
11700 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11701 // MESSAGE("vol prism " << vol->GetID());
11702 ln.push_back(ln1[0]);
11703 ln.push_back(ln1[1]);
11704 ln.push_back(ln1[2]);
11707 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11708 // MESSAGE("vol hexa " << vol->GetID());
11709 ln.push_back(ln1[0]);
11710 ln.push_back(ln1[1]);
11711 ln.push_back(ln1[2]);
11712 ln.push_back(ln1[3]);
11714 case VTK_QUADRATIC_TRIANGLE:
11715 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11716 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11717 // MESSAGE("vol quad prism " << vol->GetID());
11718 ln.push_back(ln1[0]);
11719 ln.push_back(ln1[1]);
11720 ln.push_back(ln1[2]);
11721 ln.push_back(ln3[0]);
11722 ln.push_back(ln3[1]);
11723 ln.push_back(ln3[2]);
11725 case VTK_QUADRATIC_QUAD:
11726 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11727 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11728 // ln4[0], ln4[1], ln4[2], ln4[3]);
11729 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11730 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11731 ln4[0], ln4[1], ln4[2], ln4[3]);
11732 // MESSAGE("vol quad hexa " << vol->GetID());
11733 ln.push_back(ln1[0]);
11734 ln.push_back(ln1[1]);
11735 ln.push_back(ln1[2]);
11736 ln.push_back(ln1[3]);
11737 ln.push_back(ln3[0]);
11738 ln.push_back(ln3[1]);
11739 ln.push_back(ln3[2]);
11740 ln.push_back(ln3[3]);
11750 stringstream grpname;
11753 string namegrp = grpname.str();
11754 if (!mapOfJunctionGroups.count(namegrp))
11755 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11756 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11758 sgrp->Add(vol->GetID());
11761 // --- modify the face
11763 const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11770 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11771 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11772 * groups of faces to remove inside the object, (idem edges).
11773 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11775 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11776 const TopoDS_Shape& theShape,
11777 SMESH_NodeSearcher* theNodeSearcher,
11778 const char* groupName,
11779 std::vector<double>& nodesCoords,
11780 std::vector<std::vector<int> >& listOfListOfNodes)
11782 // MESSAGE("--------------------------------");
11783 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11784 // MESSAGE("--------------------------------");
11786 // --- zone of volumes to remove is given :
11787 // 1 either by a geom shape (one or more vertices) and a radius,
11788 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11789 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11790 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11791 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11792 // defined by it's name.
11794 SMESHDS_GroupBase* groupDS = 0;
11795 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11796 while ( groupIt->more() )
11799 SMESH_Group * group = groupIt->next();
11800 if ( !group ) continue;
11801 groupDS = group->GetGroupDS();
11802 if ( !groupDS || groupDS->IsEmpty() ) continue;
11803 std::string grpName = group->GetName();
11804 //MESSAGE("grpName=" << grpName);
11805 if (grpName == groupName)
11811 bool isNodeGroup = false;
11812 bool isNodeCoords = false;
11815 if (groupDS->GetType() != SMDSAbs_Node)
11817 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11820 if (nodesCoords.size() > 0)
11821 isNodeCoords = true; // a list o nodes given by their coordinates
11822 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11824 // --- define groups to build
11826 // --- group of SMDS volumes
11827 string grpvName = groupName;
11828 grpvName += "_vol";
11829 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11832 MESSAGE("group not created " << grpvName);
11835 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11837 // --- group of SMDS faces on the skin
11838 string grpsName = groupName;
11839 grpsName += "_skin";
11840 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11843 MESSAGE("group not created " << grpsName);
11846 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11848 // --- group of SMDS faces internal (several shapes)
11849 string grpiName = groupName;
11850 grpiName += "_internalFaces";
11851 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11854 MESSAGE("group not created " << grpiName);
11857 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11859 // --- group of SMDS faces internal (several shapes)
11860 string grpeiName = groupName;
11861 grpeiName += "_internalEdges";
11862 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11865 MESSAGE("group not created " << grpeiName);
11868 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11870 // --- build downward connectivity
11872 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11873 meshDS->BuildDownWardConnectivity(true);
11874 SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11876 // --- set of volumes detected inside
11878 std::set<int> setOfInsideVol;
11879 std::set<int> setOfVolToCheck;
11881 std::vector<gp_Pnt> gpnts;
11884 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11886 //MESSAGE("group of nodes provided");
11887 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11888 while ( elemIt->more() )
11890 const SMDS_MeshElement* elem = elemIt->next();
11893 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11896 SMDS_MeshElement* vol = 0;
11897 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11898 while (volItr->more())
11900 vol = (SMDS_MeshElement*)volItr->next();
11901 setOfInsideVol.insert(vol->GetVtkID());
11902 sgrp->Add(vol->GetID());
11906 else if (isNodeCoords)
11908 //MESSAGE("list of nodes coordinates provided");
11911 while ( i < nodesCoords.size()-2 )
11913 double x = nodesCoords[i++];
11914 double y = nodesCoords[i++];
11915 double z = nodesCoords[i++];
11916 gp_Pnt p = gp_Pnt(x, y ,z);
11917 gpnts.push_back(p);
11918 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11922 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11924 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11925 TopTools_IndexedMapOfShape vertexMap;
11926 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11927 gp_Pnt p = gp_Pnt(0,0,0);
11928 if (vertexMap.Extent() < 1)
11931 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11933 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11934 p = BRep_Tool::Pnt(vertex);
11935 gpnts.push_back(p);
11936 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11940 if (gpnts.size() > 0)
11942 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11943 //MESSAGE("startNode->nodeId " << nodeId);
11945 double radius2 = radius*radius;
11946 //MESSAGE("radius2 " << radius2);
11948 // --- volumes on start node
11950 setOfVolToCheck.clear();
11951 SMDS_MeshElement* startVol = 0;
11952 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11953 while (volItr->more())
11955 startVol = (SMDS_MeshElement*)volItr->next();
11956 setOfVolToCheck.insert(startVol->GetVtkID());
11958 if (setOfVolToCheck.empty())
11960 MESSAGE("No volumes found");
11964 // --- starting with central volumes then their neighbors, check if they are inside
11965 // or outside the domain, until no more new neighbor volume is inside.
11966 // Fill the group of inside volumes
11968 std::map<int, double> mapOfNodeDistance2;
11969 mapOfNodeDistance2.clear();
11970 std::set<int> setOfOutsideVol;
11971 while (!setOfVolToCheck.empty())
11973 std::set<int>::iterator it = setOfVolToCheck.begin();
11975 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
11976 bool volInside = false;
11977 vtkIdType npts = 0;
11978 vtkIdType* pts = 0;
11979 grid->GetCellPoints(vtkId, npts, pts);
11980 for (int i=0; i<npts; i++)
11982 double distance2 = 0;
11983 if (mapOfNodeDistance2.count(pts[i]))
11985 distance2 = mapOfNodeDistance2[pts[i]];
11986 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
11990 double *coords = grid->GetPoint(pts[i]);
11991 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11993 for ( size_t j = 0; j < gpnts.size(); j++ )
11995 double d2 = aPoint.SquareDistance( gpnts[ j ]);
11996 if (d2 < distance2)
11999 if (distance2 < radius2)
12003 mapOfNodeDistance2[pts[i]] = distance2;
12004 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12006 if (distance2 < radius2)
12008 volInside = true; // one or more nodes inside the domain
12009 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12015 setOfInsideVol.insert(vtkId);
12016 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12017 int neighborsVtkIds[NBMAXNEIGHBORS];
12018 int downIds[NBMAXNEIGHBORS];
12019 unsigned char downTypes[NBMAXNEIGHBORS];
12020 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12021 for (int n = 0; n < nbNeighbors; n++)
12022 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12023 setOfVolToCheck.insert(neighborsVtkIds[n]);
12027 setOfOutsideVol.insert(vtkId);
12028 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12030 setOfVolToCheck.erase(vtkId);
12034 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12035 // If yes, add the volume to the inside set
12037 bool addedInside = true;
12038 std::set<int> setOfVolToReCheck;
12039 while (addedInside)
12041 //MESSAGE(" --------------------------- re check");
12042 addedInside = false;
12043 std::set<int>::iterator itv = setOfInsideVol.begin();
12044 for (; itv != setOfInsideVol.end(); ++itv)
12047 int neighborsVtkIds[NBMAXNEIGHBORS];
12048 int downIds[NBMAXNEIGHBORS];
12049 unsigned char downTypes[NBMAXNEIGHBORS];
12050 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12051 for (int n = 0; n < nbNeighbors; n++)
12052 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12053 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12055 setOfVolToCheck = setOfVolToReCheck;
12056 setOfVolToReCheck.clear();
12057 while (!setOfVolToCheck.empty())
12059 std::set<int>::iterator it = setOfVolToCheck.begin();
12061 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12063 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12064 int countInside = 0;
12065 int neighborsVtkIds[NBMAXNEIGHBORS];
12066 int downIds[NBMAXNEIGHBORS];
12067 unsigned char downTypes[NBMAXNEIGHBORS];
12068 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12069 for (int n = 0; n < nbNeighbors; n++)
12070 if (setOfInsideVol.count(neighborsVtkIds[n]))
12072 //MESSAGE("countInside " << countInside);
12073 if (countInside > 1)
12075 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12076 setOfInsideVol.insert(vtkId);
12077 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12078 addedInside = true;
12081 setOfVolToReCheck.insert(vtkId);
12083 setOfVolToCheck.erase(vtkId);
12087 // --- map of Downward faces at the boundary, inside the global volume
12088 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12089 // fill group of SMDS faces inside the volume (when several volume shapes)
12090 // fill group of SMDS faces on the skin of the global volume (if skin)
12092 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12093 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12094 std::set<int>::iterator it = setOfInsideVol.begin();
12095 for (; it != setOfInsideVol.end(); ++it)
12098 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12099 int neighborsVtkIds[NBMAXNEIGHBORS];
12100 int downIds[NBMAXNEIGHBORS];
12101 unsigned char downTypes[NBMAXNEIGHBORS];
12102 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12103 for (int n = 0; n < nbNeighbors; n++)
12105 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12106 if (neighborDim == 3)
12108 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12110 DownIdType face(downIds[n], downTypes[n]);
12111 boundaryFaces[face] = vtkId;
12113 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12114 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12115 if (vtkFaceId >= 0)
12117 sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12118 // find also the smds edges on this face
12119 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12120 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12121 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12122 for (int i = 0; i < nbEdges; i++)
12124 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12125 if (vtkEdgeId >= 0)
12126 sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12130 else if (neighborDim == 2) // skin of the volume
12132 DownIdType face(downIds[n], downTypes[n]);
12133 skinFaces[face] = vtkId;
12134 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12135 if (vtkFaceId >= 0)
12136 sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12141 // --- identify the edges constituting the wire of each subshape on the skin
12142 // define polylines with the nodes of edges, equivalent to wires
12143 // project polylines on subshapes, and partition, to get geom faces
12145 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12146 std::set<int> emptySet;
12148 std::set<int> shapeIds;
12150 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12151 while (itelem->more())
12153 const SMDS_MeshElement *elem = itelem->next();
12154 int shapeId = elem->getshapeId();
12155 int vtkId = elem->GetVtkID();
12156 if (!shapeIdToVtkIdSet.count(shapeId))
12158 shapeIdToVtkIdSet[shapeId] = emptySet;
12159 shapeIds.insert(shapeId);
12161 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12164 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12165 std::set<DownIdType, DownIdCompare> emptyEdges;
12166 emptyEdges.clear();
12168 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12169 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12171 int shapeId = itShape->first;
12172 //MESSAGE(" --- Shape ID --- "<< shapeId);
12173 shapeIdToEdges[shapeId] = emptyEdges;
12175 std::vector<int> nodesEdges;
12177 std::set<int>::iterator its = itShape->second.begin();
12178 for (; its != itShape->second.end(); ++its)
12181 //MESSAGE(" " << vtkId);
12182 int neighborsVtkIds[NBMAXNEIGHBORS];
12183 int downIds[NBMAXNEIGHBORS];
12184 unsigned char downTypes[NBMAXNEIGHBORS];
12185 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12186 for (int n = 0; n < nbNeighbors; n++)
12188 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12190 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12191 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12192 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12194 DownIdType edge(downIds[n], downTypes[n]);
12195 if (!shapeIdToEdges[shapeId].count(edge))
12197 shapeIdToEdges[shapeId].insert(edge);
12199 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12200 nodesEdges.push_back(vtkNodeId[0]);
12201 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12202 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12208 std::list<int> order;
12210 if (nodesEdges.size() > 0)
12212 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12213 nodesEdges[0] = -1;
12214 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12215 nodesEdges[1] = -1; // do not reuse this edge
12219 int nodeTofind = order.back(); // try first to push back
12221 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12222 if (nodesEdges[i] == nodeTofind)
12224 if ( i == (int) nodesEdges.size() )
12225 found = false; // no follower found on back
12228 if (i%2) // odd ==> use the previous one
12229 if (nodesEdges[i-1] < 0)
12233 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12234 nodesEdges[i-1] = -1;
12236 else // even ==> use the next one
12237 if (nodesEdges[i+1] < 0)
12241 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12242 nodesEdges[i+1] = -1;
12247 // try to push front
12249 nodeTofind = order.front(); // try to push front
12250 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12251 if ( nodesEdges[i] == nodeTofind )
12253 if ( i == (int)nodesEdges.size() )
12255 found = false; // no predecessor found on front
12258 if (i%2) // odd ==> use the previous one
12259 if (nodesEdges[i-1] < 0)
12263 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12264 nodesEdges[i-1] = -1;
12266 else // even ==> use the next one
12267 if (nodesEdges[i+1] < 0)
12271 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12272 nodesEdges[i+1] = -1;
12278 std::vector<int> nodes;
12279 nodes.push_back(shapeId);
12280 std::list<int>::iterator itl = order.begin();
12281 for (; itl != order.end(); itl++)
12283 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12284 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12286 listOfListOfNodes.push_back(nodes);
12289 // partition geom faces with blocFissure
12290 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12291 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12297 //================================================================================
12299 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12300 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12301 * \return TRUE if operation has been completed successfully, FALSE otherwise
12303 //================================================================================
12305 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12307 // iterates on volume elements and detect all free faces on them
12308 SMESHDS_Mesh* aMesh = GetMeshDS();
12312 ElemFeatures faceType( SMDSAbs_Face );
12313 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12314 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12317 const SMDS_MeshVolume* volume = vIt->next();
12318 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12319 vTool.SetExternalNormal();
12320 const int iQuad = volume->IsQuadratic();
12321 faceType.SetQuad( iQuad );
12322 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12324 if (!vTool.IsFreeFace(iface))
12327 vector<const SMDS_MeshNode *> nodes;
12328 int nbFaceNodes = vTool.NbFaceNodes(iface);
12329 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12331 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12332 nodes.push_back(faceNodes[inode]);
12334 if (iQuad) // add medium nodes
12336 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12337 nodes.push_back(faceNodes[inode]);
12338 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12339 nodes.push_back(faceNodes[8]);
12341 // add new face based on volume nodes
12342 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12344 nbExisted++; // face already exists
12348 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12353 return ( nbFree == ( nbExisted + nbCreated ));
12358 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12360 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12362 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12365 //================================================================================
12367 * \brief Creates missing boundary elements
12368 * \param elements - elements whose boundary is to be checked
12369 * \param dimension - defines type of boundary elements to create
12370 * \param group - a group to store created boundary elements in
12371 * \param targetMesh - a mesh to store created boundary elements in
12372 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12373 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12374 * boundary elements will be copied into the targetMesh
12375 * \param toAddExistingBondary - if true, not only new but also pre-existing
12376 * boundary elements will be added into the new group
12377 * \param aroundElements - if true, elements will be created on boundary of given
12378 * elements else, on boundary of the whole mesh.
12379 * \return nb of added boundary elements
12381 //================================================================================
12383 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12384 Bnd_Dimension dimension,
12385 SMESH_Group* group/*=0*/,
12386 SMESH_Mesh* targetMesh/*=0*/,
12387 bool toCopyElements/*=false*/,
12388 bool toCopyExistingBoundary/*=false*/,
12389 bool toAddExistingBondary/*= false*/,
12390 bool aroundElements/*= false*/)
12392 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12393 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12394 // hope that all elements are of the same type, do not check them all
12395 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12396 throw SALOME_Exception(LOCALIZED("wrong element type"));
12399 toCopyElements = toCopyExistingBoundary = false;
12401 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12402 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12403 int nbAddedBnd = 0;
12405 // editor adding present bnd elements and optionally holding elements to add to the group
12406 SMESH_MeshEditor* presentEditor;
12407 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12408 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12410 SMESH_MesherHelper helper( *myMesh );
12411 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12412 SMDS_VolumeTool vTool;
12413 TIDSortedElemSet avoidSet;
12414 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12417 typedef vector<const SMDS_MeshNode*> TConnectivity;
12418 TConnectivity tgtNodes;
12419 ElemFeatures elemKind( missType ), elemToCopy;
12421 vector<const SMDS_MeshElement*> presentBndElems;
12422 vector<TConnectivity> missingBndElems;
12423 vector<int> freeFacets;
12424 TConnectivity nodes, elemNodes;
12426 SMDS_ElemIteratorPtr eIt;
12427 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12428 else eIt = SMESHUtils::elemSetIterator( elements );
12430 while ( eIt->more() )
12432 const SMDS_MeshElement* elem = eIt->next();
12433 const int iQuad = elem->IsQuadratic();
12434 elemKind.SetQuad( iQuad );
12436 // ------------------------------------------------------------------------------------
12437 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12438 // ------------------------------------------------------------------------------------
12439 presentBndElems.clear();
12440 missingBndElems.clear();
12441 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12442 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12444 const SMDS_MeshElement* otherVol = 0;
12445 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12447 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12448 ( !aroundElements || elements.count( otherVol )))
12450 freeFacets.push_back( iface );
12452 if ( missType == SMDSAbs_Face )
12453 vTool.SetExternalNormal();
12454 for ( size_t i = 0; i < freeFacets.size(); ++i )
12456 int iface = freeFacets[i];
12457 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12458 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12459 if ( missType == SMDSAbs_Edge ) // boundary edges
12461 nodes.resize( 2+iQuad );
12462 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12464 for ( size_t j = 0; j < nodes.size(); ++j )
12465 nodes[ j ] = nn[ i+j ];
12466 if ( const SMDS_MeshElement* edge =
12467 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12468 presentBndElems.push_back( edge );
12470 missingBndElems.push_back( nodes );
12473 else // boundary face
12476 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12477 nodes.push_back( nn[inode] ); // add corner nodes
12479 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12480 nodes.push_back( nn[inode] ); // add medium nodes
12481 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12483 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12485 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12486 SMDSAbs_Face, /*noMedium=*/false ))
12487 presentBndElems.push_back( f );
12489 missingBndElems.push_back( nodes );
12491 if ( targetMesh != myMesh )
12493 // add 1D elements on face boundary to be added to a new mesh
12494 const SMDS_MeshElement* edge;
12495 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12498 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12500 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12501 if ( edge && avoidSet.insert( edge ).second )
12502 presentBndElems.push_back( edge );
12508 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12510 avoidSet.clear(), avoidSet.insert( elem );
12511 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12512 SMDS_MeshElement::iterator() );
12513 elemNodes.push_back( elemNodes[0] );
12514 nodes.resize( 2 + iQuad );
12515 const int nbLinks = elem->NbCornerNodes();
12516 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12518 nodes[0] = elemNodes[iN];
12519 nodes[1] = elemNodes[iN+1+iQuad];
12520 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12521 continue; // not free link
12523 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12524 if ( const SMDS_MeshElement* edge =
12525 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12526 presentBndElems.push_back( edge );
12528 missingBndElems.push_back( nodes );
12532 // ---------------------------------
12533 // 2. Add missing boundary elements
12534 // ---------------------------------
12535 if ( targetMesh != myMesh )
12536 // instead of making a map of nodes in this mesh and targetMesh,
12537 // we create nodes with same IDs.
12538 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12540 TConnectivity& srcNodes = missingBndElems[i];
12541 tgtNodes.resize( srcNodes.size() );
12542 for ( inode = 0; inode < srcNodes.size(); ++inode )
12543 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12544 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12546 /*noMedium=*/false))
12548 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12552 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12554 TConnectivity& nodes = missingBndElems[ i ];
12555 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12557 /*noMedium=*/false))
12559 SMDS_MeshElement* newElem =
12560 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12561 nbAddedBnd += bool( newElem );
12563 // try to set a new element to a shape
12564 if ( myMesh->HasShapeToMesh() )
12567 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12568 const size_t nbN = nodes.size() / (iQuad+1 );
12569 for ( inode = 0; inode < nbN && ok; ++inode )
12571 pair<int, TopAbs_ShapeEnum> i_stype =
12572 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12573 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12574 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12576 if ( ok && mediumShapes.size() > 1 )
12578 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12579 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12580 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12582 if (( ok = ( stype_i->first != stype_i_0.first )))
12583 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12584 aMesh->IndexToShape( stype_i_0.second ));
12587 if ( ok && mediumShapes.begin()->first == missShapeType )
12588 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12592 // ----------------------------------
12593 // 3. Copy present boundary elements
12594 // ----------------------------------
12595 if ( toCopyExistingBoundary )
12596 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12598 const SMDS_MeshElement* e = presentBndElems[i];
12599 tgtNodes.resize( e->NbNodes() );
12600 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12601 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12602 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12604 else // store present elements to add them to a group
12605 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12607 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12610 } // loop on given elements
12612 // ---------------------------------------------
12613 // 4. Fill group with boundary elements
12614 // ---------------------------------------------
12617 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12618 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12619 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12621 tgtEditor.myLastCreatedElems.clear();
12622 tgtEditor2.myLastCreatedElems.clear();
12624 // -----------------------
12625 // 5. Copy given elements
12626 // -----------------------
12627 if ( toCopyElements && targetMesh != myMesh )
12629 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12630 else eIt = SMESHUtils::elemSetIterator( elements );
12631 while (eIt->more())
12633 const SMDS_MeshElement* elem = eIt->next();
12634 tgtNodes.resize( elem->NbNodes() );
12635 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12636 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12637 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12639 tgtEditor.myLastCreatedElems.clear();
12645 //================================================================================
12647 * \brief Copy node position and set \a to node on the same geometry
12649 //================================================================================
12651 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12652 const SMDS_MeshNode* to )
12654 if ( !from || !to ) return;
12656 SMDS_PositionPtr pos = from->GetPosition();
12657 if ( !pos || from->getshapeId() < 1 ) return;
12659 switch ( pos->GetTypeOfPosition() )
12661 case SMDS_TOP_3DSPACE: break;
12663 case SMDS_TOP_FACE:
12665 SMDS_FacePositionPtr fPos = pos;
12666 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12667 fPos->GetUParameter(), fPos->GetVParameter() );
12670 case SMDS_TOP_EDGE:
12672 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12673 SMDS_EdgePositionPtr ePos = pos;
12674 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12677 case SMDS_TOP_VERTEX:
12679 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12682 case SMDS_TOP_UNSPEC: