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
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 );
6461 //=======================================================================
6463 * \brief Create groups of elements made during transformation
6464 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6465 * \param elemGens - elements making corresponding myLastCreatedElems
6466 * \param postfix - to push_back to names of new groups
6467 * \param targetMesh - mesh to create groups in
6468 * \param topPresent - is there are "top" elements that are created by sweeping
6470 //=======================================================================
6472 SMESH_MeshEditor::PGroupIDs
6473 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6474 const SMESH_SequenceOfElemPtr& elemGens,
6475 const std::string& postfix,
6476 SMESH_Mesh* targetMesh,
6477 const bool topPresent)
6479 PGroupIDs newGroupIDs( new list<int> );
6480 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6482 // Sort existing groups by types and collect their names
6484 // containers to store an old group and generated new ones;
6485 // 1st new group is for result elems of different type than a source one;
6486 // 2nd new group is for same type result elems ("top" group at extrusion)
6488 using boost::make_tuple;
6489 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6490 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6491 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6493 set< string > groupNames;
6495 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6496 if ( !groupIt->more() ) return newGroupIDs;
6498 int newGroupID = mesh->GetGroupIds().back()+1;
6499 while ( groupIt->more() )
6501 SMESH_Group * group = groupIt->next();
6502 if ( !group ) continue;
6503 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6504 if ( !groupDS || groupDS->IsEmpty() ) continue;
6505 groupNames.insert ( group->GetName() );
6506 groupDS->SetStoreName( group->GetName() );
6507 const SMDSAbs_ElementType type = groupDS->GetType();
6508 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6509 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6510 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6511 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6514 // Loop on nodes and elements to add them in new groups
6516 vector< const SMDS_MeshElement* > resultElems;
6517 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6519 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6520 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6521 if ( gens.size() != elems.size() )
6522 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6524 // loop on created elements
6525 for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6527 const SMDS_MeshElement* sourceElem = gens[ iElem ];
6528 if ( !sourceElem ) {
6529 MESSAGE("generateGroups(): NULL source element");
6532 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6533 if ( groupsOldNew.empty() ) { // no groups of this type at all
6534 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6535 ++iElem; // skip all elements made by sourceElem
6538 // collect all elements made by the iElem-th sourceElem
6539 resultElems.clear();
6540 if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6541 if ( resElem != sourceElem )
6542 resultElems.push_back( resElem );
6543 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6544 if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6545 if ( resElem != sourceElem )
6546 resultElems.push_back( resElem );
6548 const SMDS_MeshElement* topElem = 0;
6549 if ( isNodes ) // there must be a top element
6551 topElem = resultElems.back();
6552 resultElems.pop_back();
6556 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6557 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6558 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6560 topElem = *resElemIt;
6561 *resElemIt = 0; // erase *resElemIt
6565 // add resultElems to groups originted from ones the sourceElem belongs to
6566 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6567 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6569 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6570 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6572 // fill in a new group
6573 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6574 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6575 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6577 newGroup.Add( *resElemIt );
6579 // fill a "top" group
6582 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6583 newTopGroup.Add( topElem );
6587 } // loop on created elements
6588 }// loop on nodes and elements
6590 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6592 list<int> topGrouIds;
6593 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6595 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6596 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6597 orderedOldNewGroups[i]->get<2>() };
6598 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6600 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6601 if ( newGroupDS->IsEmpty() )
6603 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6608 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6611 const bool isTop = ( topPresent &&
6612 newGroupDS->GetType() == oldGroupDS->GetType() &&
6615 string name = oldGroupDS->GetStoreName();
6616 { // remove trailing whitespaces (issue 22599)
6617 size_t size = name.size();
6618 while ( size > 1 && isspace( name[ size-1 ]))
6620 if ( size != name.size() )
6622 name.resize( size );
6623 oldGroupDS->SetStoreName( name.c_str() );
6626 if ( !targetMesh ) {
6627 string suffix = ( isTop ? "top": postfix.c_str() );
6631 while ( !groupNames.insert( name ).second ) // name exists
6632 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6637 newGroupDS->SetStoreName( name.c_str() );
6639 // make a SMESH_Groups
6640 mesh->AddGroup( newGroupDS );
6642 topGrouIds.push_back( newGroupDS->GetID() );
6644 newGroupIDs->push_back( newGroupDS->GetID() );
6648 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6653 //================================================================================
6655 * * \brief Return list of group of nodes close to each other within theTolerance
6656 * * Search among theNodes or in the whole mesh if theNodes is empty using
6657 * * an Octree algorithm
6658 * \param [in,out] theNodes - the nodes to treat
6659 * \param [in] theTolerance - the tolerance
6660 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
6661 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
6662 * corner and medium nodes in separate groups
6664 //================================================================================
6666 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6667 const double theTolerance,
6668 TListOfListOfNodes & theGroupsOfNodes,
6669 bool theSeparateCornersAndMedium)
6673 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
6674 myMesh->NbFaces ( ORDER_QUADRATIC ) +
6675 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
6676 theSeparateCornersAndMedium = false;
6678 TIDSortedNodeSet& corners = theNodes;
6679 TIDSortedNodeSet medium;
6681 if ( theNodes.empty() ) // get all nodes in the mesh
6683 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
6684 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
6685 if ( theSeparateCornersAndMedium )
6686 while ( nIt->more() )
6688 const SMDS_MeshNode* n = nIt->next();
6689 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
6690 nodeSet->insert( nodeSet->end(), n );
6693 while ( nIt->more() )
6694 theNodes.insert( theNodes.end(), nIt->next() );
6696 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
6698 TIDSortedNodeSet::iterator nIt = corners.begin();
6699 while ( nIt != corners.end() )
6700 if ( SMESH_MesherHelper::IsMedium( *nIt ))
6702 medium.insert( medium.end(), *nIt );
6703 corners.erase( nIt++ );
6711 if ( !corners.empty() )
6712 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
6713 if ( !medium.empty() )
6714 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
6717 //=======================================================================
6718 //function : SimplifyFace
6719 //purpose : split a chain of nodes into several closed chains
6720 //=======================================================================
6722 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6723 vector<const SMDS_MeshNode *>& poly_nodes,
6724 vector<int>& quantities) const
6726 int nbNodes = faceNodes.size();
6727 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
6731 size_t prevNbQuant = quantities.size();
6733 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
6734 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
6735 map< const SMDS_MeshNode*, int >::iterator nInd;
6737 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
6738 simpleNodes.push_back( faceNodes[0] );
6739 for ( int iCur = 1; iCur < nbNodes; iCur++ )
6741 if ( faceNodes[ iCur ] != simpleNodes.back() )
6743 int index = simpleNodes.size();
6744 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
6745 int prevIndex = nInd->second;
6746 if ( prevIndex < index )
6749 int loopLen = index - prevIndex;
6752 // store the sub-loop
6753 quantities.push_back( loopLen );
6754 for ( int i = prevIndex; i < index; i++ )
6755 poly_nodes.push_back( simpleNodes[ i ]);
6757 simpleNodes.resize( prevIndex+1 );
6761 simpleNodes.push_back( faceNodes[ iCur ]);
6766 if ( simpleNodes.size() > 2 )
6768 quantities.push_back( simpleNodes.size() );
6769 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
6772 return quantities.size() - prevNbQuant;
6775 //=======================================================================
6776 //function : MergeNodes
6777 //purpose : In each group, the cdr of nodes are substituted by the first one
6779 //=======================================================================
6781 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
6782 const bool theAvoidMakingHoles)
6786 SMESHDS_Mesh* mesh = GetMeshDS();
6788 TNodeNodeMap nodeNodeMap; // node to replace - new node
6789 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6790 list< int > rmElemIds, rmNodeIds;
6791 vector< ElemFeatures > newElemDefs;
6793 // Fill nodeNodeMap and elems
6795 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6796 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
6798 list<const SMDS_MeshNode*>& nodes = *grIt;
6799 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6800 const SMDS_MeshNode* nToKeep = *nIt;
6801 for ( ++nIt; nIt != nodes.end(); nIt++ )
6803 const SMDS_MeshNode* nToRemove = *nIt;
6804 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
6805 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6806 while ( invElemIt->more() ) {
6807 const SMDS_MeshElement* elem = invElemIt->next();
6813 // Apply recursive replacements (BUG 0020185)
6814 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
6815 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
6817 const SMDS_MeshNode* nToKeep = nnIt->second;
6818 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
6819 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
6821 nToKeep = nnIt_i->second;
6822 nnIt->second = nToKeep;
6823 nnIt_i = nodeNodeMap.find( nToKeep );
6827 if ( theAvoidMakingHoles )
6829 // find elements whose topology changes
6831 vector<const SMDS_MeshElement*> pbElems;
6832 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6833 for ( ; eIt != elems.end(); ++eIt )
6835 const SMDS_MeshElement* elem = *eIt;
6836 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6837 while ( itN->more() )
6839 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6840 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6841 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
6843 // several nodes of elem stick
6844 pbElems.push_back( elem );
6849 // exclude from merge nodes causing spoiling element
6850 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
6852 bool nodesExcluded = false;
6853 for ( size_t i = 0; i < pbElems.size(); ++i )
6855 size_t prevNbMergeNodes = nodeNodeMap.size();
6856 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
6857 prevNbMergeNodes < nodeNodeMap.size() )
6858 nodesExcluded = true;
6860 if ( !nodesExcluded )
6865 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
6867 const SMDS_MeshNode* nToRemove = nnIt->first;
6868 const SMDS_MeshNode* nToKeep = nnIt->second;
6869 if ( nToRemove != nToKeep )
6871 rmNodeIds.push_back( nToRemove->GetID() );
6872 AddToSameGroups( nToKeep, nToRemove, mesh );
6873 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
6874 // w/o creating node in place of merged ones.
6875 SMDS_PositionPtr pos = nToRemove->GetPosition();
6876 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6877 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6878 sm->SetIsAlwaysComputed( true );
6882 // Change element nodes or remove an element
6884 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6885 for ( ; eIt != elems.end(); eIt++ )
6887 const SMDS_MeshElement* elem = *eIt;
6888 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
6890 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
6892 rmElemIds.push_back( elem->GetID() );
6894 for ( size_t i = 0; i < newElemDefs.size(); ++i )
6896 if ( i > 0 || !mesh->ChangeElementNodes( elem,
6897 & newElemDefs[i].myNodes[0],
6898 newElemDefs[i].myNodes.size() ))
6902 newElemDefs[i].SetID( elem->GetID() );
6903 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6904 if ( !keepElem ) rmElemIds.pop_back();
6908 newElemDefs[i].SetID( -1 );
6910 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
6911 if ( sm && newElem )
6912 sm->AddElement( newElem );
6913 if ( elem != newElem )
6914 ReplaceElemInGroups( elem, newElem, mesh );
6919 // Remove bad elements, then equal nodes (order important)
6920 Remove( rmElemIds, /*isNodes=*/false );
6921 Remove( rmNodeIds, /*isNodes=*/true );
6926 //=======================================================================
6927 //function : applyMerge
6928 //purpose : Compute new connectivity of an element after merging nodes
6929 // \param [in] elems - the element
6930 // \param [out] newElemDefs - definition(s) of result element(s)
6931 // \param [inout] nodeNodeMap - nodes to merge
6932 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
6933 // after merging (but not degenerated), removes nodes causing
6934 // the invalidity from \a nodeNodeMap.
6935 // \return bool - true if the element should be removed
6936 //=======================================================================
6938 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
6939 vector< ElemFeatures >& newElemDefs,
6940 TNodeNodeMap& nodeNodeMap,
6941 const bool avoidMakingHoles )
6943 bool toRemove = false; // to remove elem
6944 int nbResElems = 1; // nb new elements
6946 newElemDefs.resize(nbResElems);
6947 newElemDefs[0].Init( elem );
6948 newElemDefs[0].myNodes.clear();
6950 set<const SMDS_MeshNode*> nodeSet;
6951 vector< const SMDS_MeshNode*> curNodes;
6952 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
6955 const int nbNodes = elem->NbNodes();
6956 SMDSAbs_EntityType entity = elem->GetEntityType();
6958 curNodes.resize( nbNodes );
6959 uniqueNodes.resize( nbNodes );
6960 iRepl.resize( nbNodes );
6961 int iUnique = 0, iCur = 0, nbRepl = 0;
6963 // Get new seq of nodes
6965 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6966 while ( itN->more() )
6968 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6970 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6971 if ( nnIt != nodeNodeMap.end() ) {
6974 curNodes[ iCur ] = n;
6975 bool isUnique = nodeSet.insert( n ).second;
6977 uniqueNodes[ iUnique++ ] = n;
6979 iRepl[ nbRepl++ ] = iCur;
6983 // Analyse element topology after replacement
6985 int nbUniqueNodes = nodeSet.size();
6986 if ( nbNodes != nbUniqueNodes ) // some nodes stick
6991 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
6993 // if corner nodes stick, remove medium nodes between them from uniqueNodes
6994 int nbCorners = nbNodes / 2;
6995 for ( int iCur = 0; iCur < nbCorners; ++iCur )
6997 int iNext = ( iCur + 1 ) % nbCorners;
6998 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7000 int iMedium = iCur + nbCorners;
7001 vector< const SMDS_MeshNode* >::iterator i =
7002 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7004 curNodes[ iMedium ]);
7005 if ( i != uniqueNodes.end() )
7008 for ( ; i+1 != uniqueNodes.end(); ++i )
7017 case SMDSEntity_Polygon:
7018 case SMDSEntity_Quad_Polygon: // Polygon
7020 ElemFeatures* elemType = & newElemDefs[0];
7021 const bool isQuad = elemType->myIsQuad;
7023 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7024 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7026 // a polygon can divide into several elements
7027 vector<const SMDS_MeshNode *> polygons_nodes;
7028 vector<int> quantities;
7029 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7030 newElemDefs.resize( nbResElems );
7031 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7033 ElemFeatures* elemType = & newElemDefs[iface];
7034 if ( iface ) elemType->Init( elem );
7036 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7037 int nbNewNodes = quantities[iface];
7038 face_nodes.assign( polygons_nodes.begin() + inode,
7039 polygons_nodes.begin() + inode + nbNewNodes );
7040 inode += nbNewNodes;
7041 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7043 bool isValid = ( nbNewNodes % 2 == 0 );
7044 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7045 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7046 elemType->SetQuad( isValid );
7047 if ( isValid ) // put medium nodes after corners
7048 SMDS_MeshCell::applyInterlaceRev
7049 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7050 nbNewNodes ), face_nodes );
7052 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7054 nbUniqueNodes = newElemDefs[0].myNodes.size();
7058 case SMDSEntity_Polyhedra: // Polyhedral volume
7060 if ( nbUniqueNodes >= 4 )
7062 // each face has to be analyzed in order to check volume validity
7063 if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7065 int nbFaces = aPolyedre->NbFaces();
7067 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7068 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7069 vector<const SMDS_MeshNode *> faceNodes;
7073 for (int iface = 1; iface <= nbFaces; iface++)
7075 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7076 faceNodes.resize( nbFaceNodes );
7077 for (int inode = 1; inode <= nbFaceNodes; inode++)
7079 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7080 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7081 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7082 faceNode = (*nnIt).second;
7083 faceNodes[inode - 1] = faceNode;
7085 SimplifyFace(faceNodes, poly_nodes, quantities);
7088 if ( quantities.size() > 3 )
7090 // TODO: remove coincident faces
7092 nbUniqueNodes = newElemDefs[0].myNodes.size();
7100 // TODO not all the possible cases are solved. Find something more generic?
7101 case SMDSEntity_Edge: //////// EDGE
7102 case SMDSEntity_Triangle: //// TRIANGLE
7103 case SMDSEntity_Quad_Triangle:
7104 case SMDSEntity_Tetra:
7105 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7109 case SMDSEntity_Quad_Edge:
7113 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7115 if ( nbUniqueNodes < 3 )
7117 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7118 toRemove = true; // opposite nodes stick
7123 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7132 if ( nbUniqueNodes == 6 &&
7134 ( nbRepl == 1 || iRepl[1] >= 4 ))
7140 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7149 if ( nbUniqueNodes == 7 &&
7151 ( nbRepl == 1 || iRepl[1] != 8 ))
7157 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7159 if ( nbUniqueNodes == 4 ) {
7160 // ---------------------------------> tetrahedron
7161 if ( curNodes[3] == curNodes[4] &&
7162 curNodes[3] == curNodes[5] ) {
7166 else if ( curNodes[0] == curNodes[1] &&
7167 curNodes[0] == curNodes[2] ) {
7168 // bottom nodes stick: set a top before
7169 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7170 uniqueNodes[ 0 ] = curNodes [ 5 ];
7171 uniqueNodes[ 1 ] = curNodes [ 4 ];
7172 uniqueNodes[ 2 ] = curNodes [ 3 ];
7175 else if (( curNodes[0] == curNodes[3] ) +
7176 ( curNodes[1] == curNodes[4] ) +
7177 ( curNodes[2] == curNodes[5] ) == 2 ) {
7178 // a lateral face turns into a line
7182 else if ( nbUniqueNodes == 5 ) {
7183 // PENTAHEDRON --------------------> pyramid
7184 if ( curNodes[0] == curNodes[3] )
7186 uniqueNodes[ 0 ] = curNodes[ 1 ];
7187 uniqueNodes[ 1 ] = curNodes[ 4 ];
7188 uniqueNodes[ 2 ] = curNodes[ 5 ];
7189 uniqueNodes[ 3 ] = curNodes[ 2 ];
7190 uniqueNodes[ 4 ] = curNodes[ 0 ];
7193 if ( curNodes[1] == curNodes[4] )
7195 uniqueNodes[ 0 ] = curNodes[ 0 ];
7196 uniqueNodes[ 1 ] = curNodes[ 2 ];
7197 uniqueNodes[ 2 ] = curNodes[ 5 ];
7198 uniqueNodes[ 3 ] = curNodes[ 3 ];
7199 uniqueNodes[ 4 ] = curNodes[ 1 ];
7202 if ( curNodes[2] == curNodes[5] )
7204 uniqueNodes[ 0 ] = curNodes[ 0 ];
7205 uniqueNodes[ 1 ] = curNodes[ 3 ];
7206 uniqueNodes[ 2 ] = curNodes[ 4 ];
7207 uniqueNodes[ 3 ] = curNodes[ 1 ];
7208 uniqueNodes[ 4 ] = curNodes[ 2 ];
7214 case SMDSEntity_Hexa:
7216 //////////////////////////////////// HEXAHEDRON
7217 SMDS_VolumeTool hexa (elem);
7218 hexa.SetExternalNormal();
7219 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7220 //////////////////////// HEX ---> tetrahedron
7221 for ( int iFace = 0; iFace < 6; iFace++ ) {
7222 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7223 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7224 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7225 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7226 // one face turns into a point ...
7227 int pickInd = ind[ 0 ];
7228 int iOppFace = hexa.GetOppFaceIndex( iFace );
7229 ind = hexa.GetFaceNodesIndices( iOppFace );
7231 uniqueNodes.clear();
7232 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7233 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7236 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7238 if ( nbStick == 1 ) {
7239 // ... and the opposite one - into a triangle.
7241 uniqueNodes.push_back( curNodes[ pickInd ]);
7248 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7249 //////////////////////// HEX ---> prism
7250 int nbTria = 0, iTria[3];
7251 const int *ind; // indices of face nodes
7252 // look for triangular faces
7253 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7254 ind = hexa.GetFaceNodesIndices( iFace );
7255 TIDSortedNodeSet faceNodes;
7256 for ( iCur = 0; iCur < 4; iCur++ )
7257 faceNodes.insert( curNodes[ind[iCur]] );
7258 if ( faceNodes.size() == 3 )
7259 iTria[ nbTria++ ] = iFace;
7261 // check if triangles are opposite
7262 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7264 // set nodes of the bottom triangle
7265 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7267 for ( iCur = 0; iCur < 4; iCur++ )
7268 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7269 indB.push_back( ind[iCur] );
7270 if ( !hexa.IsForward() )
7271 std::swap( indB[0], indB[2] );
7272 for ( iCur = 0; iCur < 3; iCur++ )
7273 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7274 // set nodes of the top triangle
7275 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7276 for ( iCur = 0; iCur < 3; ++iCur )
7277 for ( int j = 0; j < 4; ++j )
7278 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7280 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7287 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7288 //////////////////// HEXAHEDRON ---> pyramid
7289 for ( int iFace = 0; iFace < 6; iFace++ ) {
7290 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7291 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7292 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7293 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7294 // one face turns into a point ...
7295 int iOppFace = hexa.GetOppFaceIndex( iFace );
7296 ind = hexa.GetFaceNodesIndices( iOppFace );
7297 uniqueNodes.clear();
7298 for ( iCur = 0; iCur < 4; iCur++ ) {
7299 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7302 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7304 if ( uniqueNodes.size() == 4 ) {
7305 // ... and the opposite one is a quadrangle
7307 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7308 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7316 if ( toRemove && nbUniqueNodes > 4 ) {
7317 ////////////////// HEXAHEDRON ---> polyhedron
7318 hexa.SetExternalNormal();
7319 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7320 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7321 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7322 quantities.reserve( 6 ); quantities.clear();
7323 for ( int iFace = 0; iFace < 6; iFace++ )
7325 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7326 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7327 curNodes[ind[1]] == curNodes[ind[3]] )
7330 break; // opposite nodes stick
7333 for ( iCur = 0; iCur < 4; iCur++ )
7335 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7336 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7338 if ( nodeSet.size() < 3 )
7339 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7341 quantities.push_back( nodeSet.size() );
7343 if ( quantities.size() >= 4 )
7346 nbUniqueNodes = poly_nodes.size();
7347 newElemDefs[0].SetPoly(true);
7351 } // case HEXAHEDRON
7356 } // switch ( entity )
7358 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7360 // erase from nodeNodeMap nodes whose merge spoils elem
7361 vector< const SMDS_MeshNode* > noMergeNodes;
7362 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7363 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7364 nodeNodeMap.erase( noMergeNodes[i] );
7367 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7369 uniqueNodes.resize( nbUniqueNodes );
7371 if ( !toRemove && nbResElems == 0 )
7374 newElemDefs.resize( nbResElems );
7380 // ========================================================
7381 // class : ComparableElement
7382 // purpose : allow comparing elements basing on their nodes
7383 // ========================================================
7385 class ComparableElement : public boost::container::flat_set< int >
7387 typedef boost::container::flat_set< int > int_set;
7389 const SMDS_MeshElement* myElem;
7391 mutable int myGroupID;
7395 ComparableElement( const SMDS_MeshElement* theElem ):
7396 myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7398 this->reserve( theElem->NbNodes() );
7399 for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7401 int id = nodeIt->next()->GetID();
7407 const SMDS_MeshElement* GetElem() const { return myElem; }
7409 int& GroupID() const { return myGroupID; }
7410 //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7412 ComparableElement( const ComparableElement& theSource ) // move copy
7414 ComparableElement& src = const_cast< ComparableElement& >( theSource );
7415 (int_set&) (*this ) = boost::move( src );
7416 myElem = src.myElem;
7417 mySumID = src.mySumID;
7418 myGroupID = src.myGroupID;
7421 static int HashCode(const ComparableElement& se, int limit )
7423 return ::HashCode( se.mySumID, limit );
7425 static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7427 return ( se1 == se2 );
7432 //=======================================================================
7433 //function : FindEqualElements
7434 //purpose : Return list of group of elements built on the same nodes.
7435 // Search among theElements or in the whole mesh if theElements is empty
7436 //=======================================================================
7438 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet & theElements,
7439 TListOfListOfElementsID & theGroupsOfElementsID )
7443 SMDS_ElemIteratorPtr elemIt;
7444 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7445 else elemIt = SMESHUtils::elemSetIterator( theElements );
7447 typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7448 typedef std::list<int> TGroupOfElems;
7449 TMapOfElements mapOfElements;
7450 std::vector< TGroupOfElems > arrayOfGroups;
7451 TGroupOfElems groupOfElems;
7453 while ( elemIt->more() )
7455 const SMDS_MeshElement* curElem = elemIt->next();
7456 ComparableElement compElem = curElem;
7458 const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7459 if ( elemInSet.GetElem() != curElem ) // coincident elem
7461 int& iG = elemInSet.GroupID();
7464 iG = arrayOfGroups.size();
7465 arrayOfGroups.push_back( groupOfElems );
7466 arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7468 arrayOfGroups[ iG ].push_back( curElem->GetID() );
7472 groupOfElems.clear();
7473 std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7474 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7476 if ( groupIt->size() > 1 ) {
7477 //groupOfElems.sort(); -- theElements are sorted already
7478 theGroupsOfElementsID.emplace_back( *groupIt );
7483 //=======================================================================
7484 //function : MergeElements
7485 //purpose : In each given group, substitute all elements by the first one.
7486 //=======================================================================
7488 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7492 typedef list<int> TListOfIDs;
7493 TListOfIDs rmElemIds; // IDs of elems to remove
7495 SMESHDS_Mesh* aMesh = GetMeshDS();
7497 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7498 while ( groupsIt != theGroupsOfElementsID.end() ) {
7499 TListOfIDs& aGroupOfElemID = *groupsIt;
7500 aGroupOfElemID.sort();
7501 int elemIDToKeep = aGroupOfElemID.front();
7502 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7503 aGroupOfElemID.pop_front();
7504 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7505 while ( idIt != aGroupOfElemID.end() ) {
7506 int elemIDToRemove = *idIt;
7507 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7508 // add the kept element in groups of removed one (PAL15188)
7509 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7510 rmElemIds.push_back( elemIDToRemove );
7516 Remove( rmElemIds, false );
7519 //=======================================================================
7520 //function : MergeEqualElements
7521 //purpose : Remove all but one of elements built on the same nodes.
7522 //=======================================================================
7524 void SMESH_MeshEditor::MergeEqualElements()
7526 TIDSortedElemSet aMeshElements; /* empty input ==
7527 to merge equal elements in the whole mesh */
7528 TListOfListOfElementsID aGroupsOfElementsID;
7529 FindEqualElements( aMeshElements, aGroupsOfElementsID );
7530 MergeElements( aGroupsOfElementsID );
7533 //=======================================================================
7534 //function : findAdjacentFace
7536 //=======================================================================
7538 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7539 const SMDS_MeshNode* n2,
7540 const SMDS_MeshElement* elem)
7542 TIDSortedElemSet elemSet, avoidSet;
7544 avoidSet.insert ( elem );
7545 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7548 //=======================================================================
7549 //function : findSegment
7550 //purpose : Return a mesh segment by two nodes one of which can be medium
7551 //=======================================================================
7553 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7554 const SMDS_MeshNode* n2)
7556 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7557 while ( it->more() )
7559 const SMDS_MeshElement* seg = it->next();
7560 if ( seg->GetNodeIndex( n2 ) >= 0 )
7566 //=======================================================================
7567 //function : FindFreeBorder
7569 //=======================================================================
7571 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7573 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7574 const SMDS_MeshNode* theSecondNode,
7575 const SMDS_MeshNode* theLastNode,
7576 list< const SMDS_MeshNode* > & theNodes,
7577 list< const SMDS_MeshElement* >& theFaces)
7579 if ( !theFirstNode || !theSecondNode )
7581 // find border face between theFirstNode and theSecondNode
7582 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7586 theFaces.push_back( curElem );
7587 theNodes.push_back( theFirstNode );
7588 theNodes.push_back( theSecondNode );
7590 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7591 //TIDSortedElemSet foundElems;
7592 bool needTheLast = ( theLastNode != 0 );
7594 vector<const SMDS_MeshNode*> nodes;
7596 while ( nStart != theLastNode ) {
7597 if ( nStart == theFirstNode )
7598 return !needTheLast;
7600 // find all free border faces sharing nStart
7602 list< const SMDS_MeshElement* > curElemList;
7603 list< const SMDS_MeshNode* > nStartList;
7604 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7605 while ( invElemIt->more() ) {
7606 const SMDS_MeshElement* e = invElemIt->next();
7607 //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
7610 nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7611 SMDS_MeshElement::iterator() );
7612 nodes.push_back( nodes[ 0 ]);
7615 int iNode = 0, nbNodes = nodes.size() - 1;
7616 for ( iNode = 0; iNode < nbNodes; iNode++ )
7617 if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7618 ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7619 ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
7621 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
7622 curElemList.push_back( e );
7626 // analyse the found
7628 int nbNewBorders = curElemList.size();
7629 if ( nbNewBorders == 0 ) {
7630 // no free border furthermore
7631 return !needTheLast;
7633 else if ( nbNewBorders == 1 ) {
7634 // one more element found
7636 nStart = nStartList.front();
7637 curElem = curElemList.front();
7638 theFaces.push_back( curElem );
7639 theNodes.push_back( nStart );
7642 // several continuations found
7643 list< const SMDS_MeshElement* >::iterator curElemIt;
7644 list< const SMDS_MeshNode* >::iterator nStartIt;
7645 // check if one of them reached the last node
7646 if ( needTheLast ) {
7647 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7648 curElemIt!= curElemList.end();
7649 curElemIt++, nStartIt++ )
7650 if ( *nStartIt == theLastNode ) {
7651 theFaces.push_back( *curElemIt );
7652 theNodes.push_back( *nStartIt );
7656 // find the best free border by the continuations
7657 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7658 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7659 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7660 curElemIt!= curElemList.end();
7661 curElemIt++, nStartIt++ )
7663 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7664 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7665 // find one more free border
7666 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7670 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7671 // choice: clear a worse one
7672 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7673 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7674 contNodes[ iWorse ].clear();
7675 contFaces[ iWorse ].clear();
7678 if ( contNodes[0].empty() && contNodes[1].empty() )
7681 // push_back the best free border
7682 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7683 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7684 //theNodes.pop_back(); // remove nIgnore
7685 theNodes.pop_back(); // remove nStart
7686 //theFaces.pop_back(); // remove curElem
7687 theNodes.splice( theNodes.end(), *cNL );
7688 theFaces.splice( theFaces.end(), *cFL );
7691 } // several continuations found
7692 } // while ( nStart != theLastNode )
7697 //=======================================================================
7698 //function : CheckFreeBorderNodes
7699 //purpose : Return true if the tree nodes are on a free border
7700 //=======================================================================
7702 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7703 const SMDS_MeshNode* theNode2,
7704 const SMDS_MeshNode* theNode3)
7706 list< const SMDS_MeshNode* > nodes;
7707 list< const SMDS_MeshElement* > faces;
7708 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7711 //=======================================================================
7712 //function : SewFreeBorder
7714 //warning : for border-to-side sewing theSideSecondNode is considered as
7715 // the last side node and theSideThirdNode is not used
7716 //=======================================================================
7718 SMESH_MeshEditor::Sew_Error
7719 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7720 const SMDS_MeshNode* theBordSecondNode,
7721 const SMDS_MeshNode* theBordLastNode,
7722 const SMDS_MeshNode* theSideFirstNode,
7723 const SMDS_MeshNode* theSideSecondNode,
7724 const SMDS_MeshNode* theSideThirdNode,
7725 const bool theSideIsFreeBorder,
7726 const bool toCreatePolygons,
7727 const bool toCreatePolyedrs)
7731 Sew_Error aResult = SEW_OK;
7733 // ====================================
7734 // find side nodes and elements
7735 // ====================================
7737 list< const SMDS_MeshNode* > nSide[ 2 ];
7738 list< const SMDS_MeshElement* > eSide[ 2 ];
7739 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7740 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7744 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7745 nSide[0], eSide[0])) {
7746 MESSAGE(" Free Border 1 not found " );
7747 aResult = SEW_BORDER1_NOT_FOUND;
7749 if (theSideIsFreeBorder) {
7752 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7753 nSide[1], eSide[1])) {
7754 MESSAGE(" Free Border 2 not found " );
7755 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7758 if ( aResult != SEW_OK )
7761 if (!theSideIsFreeBorder) {
7765 // -------------------------------------------------------------------------
7767 // 1. If nodes to merge are not coincident, move nodes of the free border
7768 // from the coord sys defined by the direction from the first to last
7769 // nodes of the border to the correspondent sys of the side 2
7770 // 2. On the side 2, find the links most co-directed with the correspondent
7771 // links of the free border
7772 // -------------------------------------------------------------------------
7774 // 1. Since sewing may break if there are volumes to split on the side 2,
7775 // we won't move nodes but just compute new coordinates for them
7776 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7777 TNodeXYZMap nBordXYZ;
7778 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7779 list< const SMDS_MeshNode* >::iterator nBordIt;
7781 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7782 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7783 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7784 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7785 double tol2 = 1.e-8;
7786 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7787 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7788 // Need node movement.
7790 // find X and Z axes to create trsf
7791 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7793 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7795 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7798 gp_Ax3 toBordAx( Pb1, Zb, X );
7799 gp_Ax3 fromSideAx( Ps1, Zs, X );
7800 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7802 gp_Trsf toBordSys, fromSide2Sys;
7803 toBordSys.SetTransformation( toBordAx );
7804 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7805 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7808 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7809 const SMDS_MeshNode* n = *nBordIt;
7810 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7811 toBordSys.Transforms( xyz );
7812 fromSide2Sys.Transforms( xyz );
7813 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7817 // just insert nodes XYZ in the nBordXYZ map
7818 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7819 const SMDS_MeshNode* n = *nBordIt;
7820 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7824 // 2. On the side 2, find the links most co-directed with the correspondent
7825 // links of the free border
7827 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7828 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7829 sideNodes.push_back( theSideFirstNode );
7831 bool hasVolumes = false;
7832 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7833 set<long> foundSideLinkIDs, checkedLinkIDs;
7834 SMDS_VolumeTool volume;
7835 //const SMDS_MeshNode* faceNodes[ 4 ];
7837 const SMDS_MeshNode* sideNode;
7838 const SMDS_MeshElement* sideElem = 0;
7839 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7840 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7841 nBordIt = bordNodes.begin();
7843 // border node position and border link direction to compare with
7844 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7845 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7846 // choose next side node by link direction or by closeness to
7847 // the current border node:
7848 bool searchByDir = ( *nBordIt != theBordLastNode );
7850 // find the next node on the Side 2
7852 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7854 checkedLinkIDs.clear();
7855 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7857 // loop on inverse elements of current node (prevSideNode) on the Side 2
7858 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7859 while ( invElemIt->more() )
7861 const SMDS_MeshElement* elem = invElemIt->next();
7862 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7863 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7864 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7865 bool isVolume = volume.Set( elem );
7866 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7867 if ( isVolume ) // --volume
7869 else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7870 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7871 SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
7872 while ( nIt->more() ) {
7873 nodes[ iNode ] = cast2Node( nIt->next() );
7874 if ( nodes[ iNode++ ] == prevSideNode )
7875 iPrevNode = iNode - 1;
7877 // there are 2 links to check
7882 // loop on links, to be precise, on the second node of links
7883 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7884 const SMDS_MeshNode* n = nodes[ iNode ];
7886 if ( !volume.IsLinked( n, prevSideNode ))
7890 if ( iNode ) // a node before prevSideNode
7891 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7892 else // a node after prevSideNode
7893 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7895 // check if this link was already used
7896 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7897 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7898 if (!isJustChecked &&
7899 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7901 // test a link geometrically
7902 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7903 bool linkIsBetter = false;
7904 double dot = 0.0, dist = 0.0;
7905 if ( searchByDir ) { // choose most co-directed link
7906 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7907 linkIsBetter = ( dot > maxDot );
7909 else { // choose link with the node closest to bordPos
7910 dist = ( nextXYZ - bordPos ).SquareModulus();
7911 linkIsBetter = ( dist < minDist );
7913 if ( linkIsBetter ) {
7922 } // loop on inverse elements of prevSideNode
7925 MESSAGE(" Can't find path by links of the Side 2 ");
7926 return SEW_BAD_SIDE_NODES;
7928 sideNodes.push_back( sideNode );
7929 sideElems.push_back( sideElem );
7930 foundSideLinkIDs.insert ( linkID );
7931 prevSideNode = sideNode;
7933 if ( *nBordIt == theBordLastNode )
7934 searchByDir = false;
7936 // find the next border link to compare with
7937 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7938 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7939 // move to next border node if sideNode is before forward border node (bordPos)
7940 while ( *nBordIt != theBordLastNode && !searchByDir ) {
7941 prevBordNode = *nBordIt;
7943 bordPos = nBordXYZ[ *nBordIt ];
7944 bordDir = bordPos - nBordXYZ[ prevBordNode ];
7945 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7949 while ( sideNode != theSideSecondNode );
7951 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7952 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7953 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7955 } // end nodes search on the side 2
7957 // ============================
7958 // sew the border to the side 2
7959 // ============================
7961 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
7962 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
7964 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
7965 if ( toMergeConformal && toCreatePolygons )
7967 // do not merge quadrangles if polygons are OK (IPAL0052824)
7968 eIt[0] = eSide[0].begin();
7969 eIt[1] = eSide[1].begin();
7970 bool allQuads[2] = { true, true };
7971 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7972 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
7973 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
7975 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
7978 TListOfListOfNodes nodeGroupsToMerge;
7979 if (( toMergeConformal ) ||
7980 ( theSideIsFreeBorder && !theSideThirdNode )) {
7982 // all nodes are to be merged
7984 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
7985 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
7986 nIt[0]++, nIt[1]++ )
7988 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
7989 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
7990 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
7995 // insert new nodes into the border and the side to get equal nb of segments
7997 // get normalized parameters of nodes on the borders
7998 vector< double > param[ 2 ];
7999 param[0].resize( maxNbNodes );
8000 param[1].resize( maxNbNodes );
8002 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8003 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8004 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8005 const SMDS_MeshNode* nPrev = *nIt;
8006 double bordLength = 0;
8007 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8008 const SMDS_MeshNode* nCur = *nIt;
8009 gp_XYZ segment (nCur->X() - nPrev->X(),
8010 nCur->Y() - nPrev->Y(),
8011 nCur->Z() - nPrev->Z());
8012 double segmentLen = segment.Modulus();
8013 bordLength += segmentLen;
8014 param[ iBord ][ iNode ] = bordLength;
8017 // normalize within [0,1]
8018 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8019 param[ iBord ][ iNode ] /= bordLength;
8023 // loop on border segments
8024 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8025 int i[ 2 ] = { 0, 0 };
8026 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8027 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8029 // element can be split while iterating on border if it has two edges in the border
8030 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8031 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8033 TElemOfNodeListMap insertMap;
8034 TElemOfNodeListMap::iterator insertMapIt;
8036 // key: elem to insert nodes into
8037 // value: 2 nodes to insert between + nodes to be inserted
8039 bool next[ 2 ] = { false, false };
8041 // find min adjacent segment length after sewing
8042 double nextParam = 10., prevParam = 0;
8043 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8044 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8045 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8046 if ( i[ iBord ] > 0 )
8047 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8049 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8050 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8051 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8053 // choose to insert or to merge nodes
8054 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8055 if ( Abs( du ) <= minSegLen * 0.2 ) {
8058 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8059 const SMDS_MeshNode* n0 = *nIt[0];
8060 const SMDS_MeshNode* n1 = *nIt[1];
8061 nodeGroupsToMerge.back().push_back( n1 );
8062 nodeGroupsToMerge.back().push_back( n0 );
8063 // position of node of the border changes due to merge
8064 param[ 0 ][ i[0] ] += du;
8065 // move n1 for the sake of elem shape evaluation during insertion.
8066 // n1 will be removed by MergeNodes() anyway
8067 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8068 next[0] = next[1] = true;
8073 int intoBord = ( du < 0 ) ? 0 : 1;
8074 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8075 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8076 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8077 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8078 if ( intoBord == 1 ) {
8079 // move node of the border to be on a link of elem of the side
8080 SMESH_NodeXYZ p1( n1 ), p2( n2 );
8081 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8082 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8083 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8085 elemReplaceMapIt = elemReplaceMap.find( elem );
8086 if ( elemReplaceMapIt != elemReplaceMap.end() )
8087 elem = elemReplaceMapIt->second;
8089 insertMapIt = insertMap.find( elem );
8090 bool notFound = ( insertMapIt == insertMap.end() );
8091 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8093 // insert into another link of the same element:
8094 // 1. perform insertion into the other link of the elem
8095 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8096 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8097 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8098 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8099 // 2. perform insertion into the link of adjacent faces
8100 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8101 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8103 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8104 InsertNodesIntoLink( seg, n12, n22, nodeList );
8106 if (toCreatePolyedrs) {
8107 // perform insertion into the links of adjacent volumes
8108 UpdateVolumes(n12, n22, nodeList);
8110 // 3. find an element appeared on n1 and n2 after the insertion
8111 insertMap.erase( insertMapIt );
8112 const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8113 elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8116 if ( notFound || otherLink ) {
8117 // add element and nodes of the side into the insertMap
8118 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8119 (*insertMapIt).second.push_back( n1 );
8120 (*insertMapIt).second.push_back( n2 );
8122 // add node to be inserted into elem
8123 (*insertMapIt).second.push_back( nIns );
8124 next[ 1 - intoBord ] = true;
8127 // go to the next segment
8128 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8129 if ( next[ iBord ] ) {
8130 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8132 nPrev[ iBord ] = *nIt[ iBord ];
8133 nIt[ iBord ]++; i[ iBord ]++;
8137 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8139 // perform insertion of nodes into elements
8141 for (insertMapIt = insertMap.begin();
8142 insertMapIt != insertMap.end();
8145 const SMDS_MeshElement* elem = (*insertMapIt).first;
8146 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8147 if ( nodeList.size() < 3 ) continue;
8148 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8149 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8151 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8153 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8154 InsertNodesIntoLink( seg, n1, n2, nodeList );
8157 if ( !theSideIsFreeBorder ) {
8158 // look for and insert nodes into the faces adjacent to elem
8159 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8160 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8163 if (toCreatePolyedrs) {
8164 // perform insertion into the links of adjacent volumes
8165 UpdateVolumes(n1, n2, nodeList);
8168 } // end: insert new nodes
8170 MergeNodes ( nodeGroupsToMerge );
8173 // Remove coincident segments
8176 TIDSortedElemSet segments;
8177 SMESH_SequenceOfElemPtr newFaces;
8178 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8180 if ( !myLastCreatedElems[i] ) continue;
8181 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8182 segments.insert( segments.end(), myLastCreatedElems[i] );
8184 newFaces.push_back( myLastCreatedElems[i] );
8186 // get segments adjacent to merged nodes
8187 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8188 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8190 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8191 if ( nodes.front()->IsNull() ) continue;
8192 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8193 while ( segIt->more() )
8194 segments.insert( segIt->next() );
8198 TListOfListOfElementsID equalGroups;
8199 if ( !segments.empty() )
8200 FindEqualElements( segments, equalGroups );
8201 if ( !equalGroups.empty() )
8203 // remove from segments those that will be removed
8204 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8205 for ( ; itGroups != equalGroups.end(); ++itGroups )
8207 list< int >& group = *itGroups;
8208 list< int >::iterator id = group.begin();
8209 for ( ++id; id != group.end(); ++id )
8210 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8211 segments.erase( seg );
8213 // remove equal segments
8214 MergeElements( equalGroups );
8216 // restore myLastCreatedElems
8217 myLastCreatedElems = newFaces;
8218 TIDSortedElemSet::iterator seg = segments.begin();
8219 for ( ; seg != segments.end(); ++seg )
8220 myLastCreatedElems.push_back( *seg );
8226 //=======================================================================
8227 //function : InsertNodesIntoLink
8228 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8229 // and theBetweenNode2 and split theElement
8230 //=======================================================================
8232 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8233 const SMDS_MeshNode* theBetweenNode1,
8234 const SMDS_MeshNode* theBetweenNode2,
8235 list<const SMDS_MeshNode*>& theNodesToInsert,
8236 const bool toCreatePoly)
8238 if ( !theElement ) return;
8240 SMESHDS_Mesh *aMesh = GetMeshDS();
8241 vector<const SMDS_MeshElement*> newElems;
8243 if ( theElement->GetType() == SMDSAbs_Edge )
8245 theNodesToInsert.push_front( theBetweenNode1 );
8246 theNodesToInsert.push_back ( theBetweenNode2 );
8247 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8248 const SMDS_MeshNode* n1 = *n;
8249 for ( ++n; n != theNodesToInsert.end(); ++n )
8251 const SMDS_MeshNode* n2 = *n;
8252 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8253 AddToSameGroups( seg, theElement, aMesh );
8255 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8258 theNodesToInsert.pop_front();
8259 theNodesToInsert.pop_back();
8261 if ( theElement->IsQuadratic() ) // add a not split part
8263 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8264 theElement->end_nodes() );
8265 int iOther = 0, nbN = nodes.size();
8266 for ( ; iOther < nbN; ++iOther )
8267 if ( nodes[iOther] != theBetweenNode1 &&
8268 nodes[iOther] != theBetweenNode2 )
8272 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8273 AddToSameGroups( seg, theElement, aMesh );
8275 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8277 else if ( iOther == 2 )
8279 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8280 AddToSameGroups( seg, theElement, aMesh );
8282 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8285 // treat new elements
8286 for ( size_t i = 0; i < newElems.size(); ++i )
8289 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8290 myLastCreatedElems.push_back( newElems[i] );
8292 ReplaceElemInGroups( theElement, newElems, aMesh );
8293 aMesh->RemoveElement( theElement );
8296 } // if ( theElement->GetType() == SMDSAbs_Edge )
8298 const SMDS_MeshElement* theFace = theElement;
8299 if ( theFace->GetType() != SMDSAbs_Face ) return;
8301 // find indices of 2 link nodes and of the rest nodes
8302 int iNode = 0, il1, il2, i3, i4;
8303 il1 = il2 = i3 = i4 = -1;
8304 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8306 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8307 while ( nodeIt->more() ) {
8308 const SMDS_MeshNode* n = nodeIt->next();
8309 if ( n == theBetweenNode1 )
8311 else if ( n == theBetweenNode2 )
8317 nodes[ iNode++ ] = n;
8319 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8322 // arrange link nodes to go one after another regarding the face orientation
8323 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8324 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8329 aNodesToInsert.reverse();
8331 // check that not link nodes of a quadrangles are in good order
8332 int nbFaceNodes = theFace->NbNodes();
8333 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8339 if (toCreatePoly || theFace->IsPoly()) {
8342 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8344 // add nodes of face up to first node of link
8346 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8347 while ( nodeIt->more() && !isFLN ) {
8348 const SMDS_MeshNode* n = nodeIt->next();
8349 poly_nodes[iNode++] = n;
8350 isFLN = ( n == nodes[il1] );
8352 // add nodes to insert
8353 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8354 for (; nIt != aNodesToInsert.end(); nIt++) {
8355 poly_nodes[iNode++] = *nIt;
8357 // add nodes of face starting from last node of link
8358 while ( nodeIt->more() ) {
8359 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8360 poly_nodes[iNode++] = n;
8364 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8367 else if ( !theFace->IsQuadratic() )
8369 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8370 int nbLinkNodes = 2 + aNodesToInsert.size();
8371 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8372 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8373 linkNodes[ 0 ] = nodes[ il1 ];
8374 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8375 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8376 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8377 linkNodes[ iNode++ ] = *nIt;
8379 // decide how to split a quadrangle: compare possible variants
8380 // and choose which of splits to be a quadrangle
8381 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8382 if ( nbFaceNodes == 3 ) {
8383 iBestQuad = nbSplits;
8386 else if ( nbFaceNodes == 4 ) {
8387 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8388 double aBestRate = DBL_MAX;
8389 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8391 double aBadRate = 0;
8392 // evaluate elements quality
8393 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8394 if ( iSplit == iQuad ) {
8395 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8399 aBadRate += getBadRate( &quad, aCrit );
8402 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8404 nodes[ iSplit < iQuad ? i4 : i3 ]);
8405 aBadRate += getBadRate( &tria, aCrit );
8409 if ( aBadRate < aBestRate ) {
8411 aBestRate = aBadRate;
8416 // create new elements
8418 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8420 if ( iSplit == iBestQuad )
8421 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8426 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8428 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8431 const SMDS_MeshNode* newNodes[ 4 ];
8432 newNodes[ 0 ] = linkNodes[ i1 ];
8433 newNodes[ 1 ] = linkNodes[ i2 ];
8434 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8435 newNodes[ 3 ] = nodes[ i4 ];
8436 if (iSplit == iBestQuad)
8437 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8439 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8441 } // end if(!theFace->IsQuadratic())
8443 else { // theFace is quadratic
8444 // we have to split theFace on simple triangles and one simple quadrangle
8446 int nbshift = tmp*2;
8447 // shift nodes in nodes[] by nbshift
8449 for(i=0; i<nbshift; i++) {
8450 const SMDS_MeshNode* n = nodes[0];
8451 for(j=0; j<nbFaceNodes-1; j++) {
8452 nodes[j] = nodes[j+1];
8454 nodes[nbFaceNodes-1] = n;
8456 il1 = il1 - nbshift;
8457 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8458 // n0 n1 n2 n0 n1 n2
8459 // +-----+-----+ +-----+-----+
8468 // create new elements
8470 if ( nbFaceNodes == 6 ) { // quadratic triangle
8471 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8472 if ( theFace->IsMediumNode(nodes[il1]) ) {
8473 // create quadrangle
8474 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8480 // create quadrangle
8481 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8487 else { // nbFaceNodes==8 - quadratic quadrangle
8488 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8489 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8490 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8491 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8492 // create quadrangle
8493 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8499 // create quadrangle
8500 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8506 // create needed triangles using n1,n2,n3 and inserted nodes
8507 int nbn = 2 + aNodesToInsert.size();
8508 vector<const SMDS_MeshNode*> aNodes(nbn);
8509 aNodes[0 ] = nodes[n1];
8510 aNodes[nbn-1] = nodes[n2];
8511 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8512 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8513 aNodes[iNode++] = *nIt;
8515 for ( i = 1; i < nbn; i++ )
8516 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8519 // remove the old face
8520 for ( size_t i = 0; i < newElems.size(); ++i )
8523 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8524 myLastCreatedElems.push_back( newElems[i] );
8526 ReplaceElemInGroups( theFace, newElems, aMesh );
8527 aMesh->RemoveElement(theFace);
8529 } // InsertNodesIntoLink()
8531 //=======================================================================
8532 //function : UpdateVolumes
8534 //=======================================================================
8536 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8537 const SMDS_MeshNode* theBetweenNode2,
8538 list<const SMDS_MeshNode*>& theNodesToInsert)
8542 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8543 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8544 const SMDS_MeshElement* elem = invElemIt->next();
8546 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8547 SMDS_VolumeTool aVolume (elem);
8548 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8551 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8552 int iface, nbFaces = aVolume.NbFaces();
8553 vector<const SMDS_MeshNode *> poly_nodes;
8554 vector<int> quantities (nbFaces);
8556 for (iface = 0; iface < nbFaces; iface++) {
8557 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8558 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8559 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8561 for (int inode = 0; inode < nbFaceNodes; inode++) {
8562 poly_nodes.push_back(faceNodes[inode]);
8564 if (nbInserted == 0) {
8565 if (faceNodes[inode] == theBetweenNode1) {
8566 if (faceNodes[inode + 1] == theBetweenNode2) {
8567 nbInserted = theNodesToInsert.size();
8569 // add nodes to insert
8570 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8571 for (; nIt != theNodesToInsert.end(); nIt++) {
8572 poly_nodes.push_back(*nIt);
8576 else if (faceNodes[inode] == theBetweenNode2) {
8577 if (faceNodes[inode + 1] == theBetweenNode1) {
8578 nbInserted = theNodesToInsert.size();
8580 // add nodes to insert in reversed order
8581 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8583 for (; nIt != theNodesToInsert.begin(); nIt--) {
8584 poly_nodes.push_back(*nIt);
8586 poly_nodes.push_back(*nIt);
8593 quantities[iface] = nbFaceNodes + nbInserted;
8596 // Replace the volume
8597 SMESHDS_Mesh *aMesh = GetMeshDS();
8599 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8601 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8602 myLastCreatedElems.push_back( newElem );
8603 ReplaceElemInGroups( elem, newElem, aMesh );
8605 aMesh->RemoveElement( elem );
8611 //================================================================================
8613 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8615 //================================================================================
8617 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8618 vector<const SMDS_MeshNode *> & nodes,
8619 vector<int> & nbNodeInFaces )
8622 nbNodeInFaces.clear();
8623 SMDS_VolumeTool vTool ( elem );
8624 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8626 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8627 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8628 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8633 //=======================================================================
8635 * \brief Convert elements contained in a sub-mesh to quadratic
8636 * \return int - nb of checked elements
8638 //=======================================================================
8640 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8641 SMESH_MesherHelper& theHelper,
8642 const bool theForce3d)
8644 //MESSAGE("convertElemToQuadratic");
8646 if( !theSm ) return nbElem;
8648 vector<int> nbNodeInFaces;
8649 vector<const SMDS_MeshNode *> nodes;
8650 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8651 while(ElemItr->more())
8654 const SMDS_MeshElement* elem = ElemItr->next();
8655 if( !elem ) continue;
8657 // analyse a necessity of conversion
8658 const SMDSAbs_ElementType aType = elem->GetType();
8659 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8661 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8662 bool hasCentralNodes = false;
8663 if ( elem->IsQuadratic() )
8666 switch ( aGeomType ) {
8667 case SMDSEntity_Quad_Triangle:
8668 case SMDSEntity_Quad_Quadrangle:
8669 case SMDSEntity_Quad_Hexa:
8670 case SMDSEntity_Quad_Penta:
8671 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8673 case SMDSEntity_BiQuad_Triangle:
8674 case SMDSEntity_BiQuad_Quadrangle:
8675 case SMDSEntity_TriQuad_Hexa:
8676 case SMDSEntity_BiQuad_Penta:
8677 alreadyOK = theHelper.GetIsBiQuadratic();
8678 hasCentralNodes = true;
8683 // take into account already present medium nodes
8685 case SMDSAbs_Volume:
8686 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8688 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8690 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8696 // get elem data needed to re-create it
8698 const int id = elem->GetID();
8699 const int nbNodes = elem->NbCornerNodes();
8700 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8701 if ( aGeomType == SMDSEntity_Polyhedra )
8702 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8703 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8704 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8706 // remove a linear element
8707 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8709 // remove central nodes of biquadratic elements (biquad->quad conversion)
8710 if ( hasCentralNodes )
8711 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8712 if ( nodes[i]->NbInverseElements() == 0 )
8713 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8715 const SMDS_MeshElement* NewElem = 0;
8721 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8729 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8732 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8735 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8739 case SMDSAbs_Volume :
8743 case SMDSEntity_Tetra:
8744 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8746 case SMDSEntity_Pyramid:
8747 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8749 case SMDSEntity_Penta:
8750 case SMDSEntity_Quad_Penta:
8751 case SMDSEntity_BiQuad_Penta:
8752 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8754 case SMDSEntity_Hexa:
8755 case SMDSEntity_Quad_Hexa:
8756 case SMDSEntity_TriQuad_Hexa:
8757 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8758 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8760 case SMDSEntity_Hexagonal_Prism:
8762 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8769 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8770 if( NewElem && NewElem->getshapeId() < 1 )
8771 theSm->AddElement( NewElem );
8775 //=======================================================================
8776 //function : ConvertToQuadratic
8778 //=======================================================================
8780 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8782 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8783 SMESHDS_Mesh* meshDS = GetMeshDS();
8785 SMESH_MesherHelper aHelper(*myMesh);
8787 aHelper.SetIsQuadratic( true );
8788 aHelper.SetIsBiQuadratic( theToBiQuad );
8789 aHelper.SetElementsOnShape(true);
8790 aHelper.ToFixNodeParameters( true );
8792 // convert elements assigned to sub-meshes
8793 int nbCheckedElems = 0;
8794 if ( myMesh->HasShapeToMesh() )
8796 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8798 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8799 while ( smIt->more() ) {
8800 SMESH_subMesh* sm = smIt->next();
8801 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8802 aHelper.SetSubShape( sm->GetSubShape() );
8803 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8809 // convert elements NOT assigned to sub-meshes
8810 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8811 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8813 aHelper.SetElementsOnShape(false);
8814 SMESHDS_SubMesh *smDS = 0;
8817 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8818 while( aEdgeItr->more() )
8820 const SMDS_MeshEdge* edge = aEdgeItr->next();
8821 if ( !edge->IsQuadratic() )
8823 int id = edge->GetID();
8824 const SMDS_MeshNode* n1 = edge->GetNode(0);
8825 const SMDS_MeshNode* n2 = edge->GetNode(1);
8827 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8829 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8830 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8834 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8839 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8840 while( aFaceItr->more() )
8842 const SMDS_MeshFace* face = aFaceItr->next();
8843 if ( !face ) continue;
8845 const SMDSAbs_EntityType type = face->GetEntityType();
8849 case SMDSEntity_Quad_Triangle:
8850 case SMDSEntity_Quad_Quadrangle:
8851 alreadyOK = !theToBiQuad;
8852 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8854 case SMDSEntity_BiQuad_Triangle:
8855 case SMDSEntity_BiQuad_Quadrangle:
8856 alreadyOK = theToBiQuad;
8857 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8859 default: alreadyOK = false;
8864 const int id = face->GetID();
8865 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8867 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8869 SMDS_MeshFace * NewFace = 0;
8872 case SMDSEntity_Triangle:
8873 case SMDSEntity_Quad_Triangle:
8874 case SMDSEntity_BiQuad_Triangle:
8875 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8876 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8877 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8880 case SMDSEntity_Quadrangle:
8881 case SMDSEntity_Quad_Quadrangle:
8882 case SMDSEntity_BiQuad_Quadrangle:
8883 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8884 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8885 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8889 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8891 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8895 vector<int> nbNodeInFaces;
8896 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8897 while(aVolumeItr->more())
8899 const SMDS_MeshVolume* volume = aVolumeItr->next();
8900 if ( !volume ) continue;
8902 const SMDSAbs_EntityType type = volume->GetEntityType();
8903 if ( volume->IsQuadratic() )
8908 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8909 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8910 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
8911 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
8912 default: alreadyOK = true;
8916 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8920 const int id = volume->GetID();
8921 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8922 if ( type == SMDSEntity_Polyhedra )
8923 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
8924 else if ( type == SMDSEntity_Hexagonal_Prism )
8925 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8927 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8929 SMDS_MeshVolume * NewVolume = 0;
8932 case SMDSEntity_Tetra:
8933 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8935 case SMDSEntity_Hexa:
8936 case SMDSEntity_Quad_Hexa:
8937 case SMDSEntity_TriQuad_Hexa:
8938 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8939 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8940 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8941 if ( nodes[i]->NbInverseElements() == 0 )
8942 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8944 case SMDSEntity_Pyramid:
8945 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8946 nodes[3], nodes[4], id, theForce3d);
8948 case SMDSEntity_Penta:
8949 case SMDSEntity_Quad_Penta:
8950 case SMDSEntity_BiQuad_Penta:
8951 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8952 nodes[3], nodes[4], nodes[5], id, theForce3d);
8953 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
8954 if ( nodes[i]->NbInverseElements() == 0 )
8955 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8957 case SMDSEntity_Hexagonal_Prism:
8959 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8961 ReplaceElemInGroups(volume, NewVolume, meshDS);
8966 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8967 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8968 // aHelper.FixQuadraticElements(myError);
8969 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8973 //================================================================================
8975 * \brief Makes given elements quadratic
8976 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
8977 * \param theElements - elements to make quadratic
8979 //================================================================================
8981 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
8982 TIDSortedElemSet& theElements,
8983 const bool theToBiQuad)
8985 if ( theElements.empty() ) return;
8987 // we believe that all theElements are of the same type
8988 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
8990 // get all nodes shared by theElements
8991 TIDSortedNodeSet allNodes;
8992 TIDSortedElemSet::iterator eIt = theElements.begin();
8993 for ( ; eIt != theElements.end(); ++eIt )
8994 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
8996 // complete theElements with elements of lower dim whose all nodes are in allNodes
8998 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
8999 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9000 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9001 for ( ; nIt != allNodes.end(); ++nIt )
9003 const SMDS_MeshNode* n = *nIt;
9004 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9005 while ( invIt->more() )
9007 const SMDS_MeshElement* e = invIt->next();
9008 const SMDSAbs_ElementType type = e->GetType();
9009 if ( e->IsQuadratic() )
9011 quadAdjacentElems[ type ].insert( e );
9014 switch ( e->GetEntityType() ) {
9015 case SMDSEntity_Quad_Triangle:
9016 case SMDSEntity_Quad_Quadrangle:
9017 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9018 case SMDSEntity_BiQuad_Triangle:
9019 case SMDSEntity_BiQuad_Quadrangle:
9020 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9021 default: alreadyOK = true;
9026 if ( type >= elemType )
9027 continue; // same type or more complex linear element
9029 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9030 continue; // e is already checked
9034 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9035 while ( nodeIt->more() && allIn )
9036 allIn = allNodes.count( nodeIt->next() );
9038 theElements.insert(e );
9042 SMESH_MesherHelper helper(*myMesh);
9043 helper.SetIsQuadratic( true );
9044 helper.SetIsBiQuadratic( theToBiQuad );
9046 // add links of quadratic adjacent elements to the helper
9048 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9049 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9050 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9052 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9054 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9055 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9056 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9058 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9060 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9061 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9062 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9064 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9067 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9069 SMESHDS_Mesh* meshDS = GetMeshDS();
9070 SMESHDS_SubMesh* smDS = 0;
9071 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9073 const SMDS_MeshElement* elem = *eIt;
9076 int nbCentralNodes = 0;
9077 switch ( elem->GetEntityType() ) {
9078 // linear convertible
9079 case SMDSEntity_Edge:
9080 case SMDSEntity_Triangle:
9081 case SMDSEntity_Quadrangle:
9082 case SMDSEntity_Tetra:
9083 case SMDSEntity_Pyramid:
9084 case SMDSEntity_Hexa:
9085 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9086 // quadratic that can become bi-quadratic
9087 case SMDSEntity_Quad_Triangle:
9088 case SMDSEntity_Quad_Quadrangle:
9089 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9091 case SMDSEntity_BiQuad_Triangle:
9092 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9093 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9095 default: alreadyOK = true;
9097 if ( alreadyOK ) continue;
9099 const SMDSAbs_ElementType type = elem->GetType();
9100 const int id = elem->GetID();
9101 const int nbNodes = elem->NbCornerNodes();
9102 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9104 helper.SetSubShape( elem->getshapeId() );
9106 if ( !smDS || !smDS->Contains( elem ))
9107 smDS = meshDS->MeshElements( elem->getshapeId() );
9108 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9110 SMDS_MeshElement * newElem = 0;
9113 case 4: // cases for most frequently used element types go first (for optimization)
9114 if ( type == SMDSAbs_Volume )
9115 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9117 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9120 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9121 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9124 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9127 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9130 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9131 nodes[4], id, theForce3d);
9134 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9135 nodes[4], nodes[5], id, theForce3d);
9139 ReplaceElemInGroups( elem, newElem, meshDS);
9140 if( newElem && smDS )
9141 smDS->AddElement( newElem );
9143 // remove central nodes
9144 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9145 if ( nodes[i]->NbInverseElements() == 0 )
9146 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9148 } // loop on theElements
9151 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9152 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9153 // helper.FixQuadraticElements( myError );
9154 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9158 //=======================================================================
9160 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9161 * \return int - nb of checked elements
9163 //=======================================================================
9165 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9166 SMDS_ElemIteratorPtr theItr,
9167 const int theShapeID)
9170 SMESHDS_Mesh* meshDS = GetMeshDS();
9171 ElemFeatures elemType;
9172 vector<const SMDS_MeshNode *> nodes;
9174 while( theItr->more() )
9176 const SMDS_MeshElement* elem = theItr->next();
9178 if( elem && elem->IsQuadratic())
9181 int nbCornerNodes = elem->NbCornerNodes();
9182 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9184 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9186 //remove a quadratic element
9187 if ( !theSm || !theSm->Contains( elem ))
9188 theSm = meshDS->MeshElements( elem->getshapeId() );
9189 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9191 // remove medium nodes
9192 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9193 if ( nodes[i]->NbInverseElements() == 0 )
9194 meshDS->RemoveFreeNode( nodes[i], theSm );
9196 // add a linear element
9197 nodes.resize( nbCornerNodes );
9198 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9199 ReplaceElemInGroups(elem, newElem, meshDS);
9200 if( theSm && newElem )
9201 theSm->AddElement( newElem );
9207 //=======================================================================
9208 //function : ConvertFromQuadratic
9210 //=======================================================================
9212 bool SMESH_MeshEditor::ConvertFromQuadratic()
9214 int nbCheckedElems = 0;
9215 if ( myMesh->HasShapeToMesh() )
9217 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9219 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9220 while ( smIt->more() ) {
9221 SMESH_subMesh* sm = smIt->next();
9222 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9223 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9229 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9230 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9232 SMESHDS_SubMesh *aSM = 0;
9233 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9241 //================================================================================
9243 * \brief Return true if all medium nodes of the element are in the node set
9245 //================================================================================
9247 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9249 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9250 if ( !nodeSet.count( elem->GetNode(i) ))
9256 //================================================================================
9258 * \brief Makes given elements linear
9260 //================================================================================
9262 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9264 if ( theElements.empty() ) return;
9266 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9267 set<int> mediumNodeIDs;
9268 TIDSortedElemSet::iterator eIt = theElements.begin();
9269 for ( ; eIt != theElements.end(); ++eIt )
9271 const SMDS_MeshElement* e = *eIt;
9272 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9273 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9276 // replace given elements by linear ones
9277 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9278 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9280 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9281 // except those elements sharing medium nodes of quadratic element whose medium nodes
9282 // are not all in mediumNodeIDs
9284 // get remaining medium nodes
9285 TIDSortedNodeSet mediumNodes;
9286 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9287 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9288 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9289 mediumNodes.insert( mediumNodes.end(), n );
9291 // find more quadratic elements to convert
9292 TIDSortedElemSet moreElemsToConvert;
9293 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9294 for ( ; nIt != mediumNodes.end(); ++nIt )
9296 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9297 while ( invIt->more() )
9299 const SMDS_MeshElement* e = invIt->next();
9300 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9302 // find a more complex element including e and
9303 // whose medium nodes are not in mediumNodes
9304 bool complexFound = false;
9305 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9307 SMDS_ElemIteratorPtr invIt2 =
9308 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9309 while ( invIt2->more() )
9311 const SMDS_MeshElement* eComplex = invIt2->next();
9312 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9314 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9315 if ( nbCommonNodes == e->NbNodes())
9317 complexFound = true;
9318 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9324 if ( !complexFound )
9325 moreElemsToConvert.insert( e );
9329 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9330 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9333 //=======================================================================
9334 //function : SewSideElements
9336 //=======================================================================
9338 SMESH_MeshEditor::Sew_Error
9339 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9340 TIDSortedElemSet& theSide2,
9341 const SMDS_MeshNode* theFirstNode1,
9342 const SMDS_MeshNode* theFirstNode2,
9343 const SMDS_MeshNode* theSecondNode1,
9344 const SMDS_MeshNode* theSecondNode2)
9348 if ( theSide1.size() != theSide2.size() )
9349 return SEW_DIFF_NB_OF_ELEMENTS;
9351 Sew_Error aResult = SEW_OK;
9353 // 1. Build set of faces representing each side
9354 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9355 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9357 // =======================================================================
9358 // 1. Build set of faces representing each side:
9359 // =======================================================================
9360 // a. build set of nodes belonging to faces
9361 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9362 // c. create temporary faces representing side of volumes if correspondent
9363 // face does not exist
9365 SMESHDS_Mesh* aMesh = GetMeshDS();
9366 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9367 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9368 TIDSortedElemSet faceSet1, faceSet2;
9369 set<const SMDS_MeshElement*> volSet1, volSet2;
9370 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9371 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9372 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9373 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9374 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9375 int iSide, iFace, iNode;
9377 list<const SMDS_MeshElement* > tempFaceList;
9378 for ( iSide = 0; iSide < 2; iSide++ ) {
9379 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9380 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9381 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9382 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9383 set<const SMDS_MeshElement*>::iterator vIt;
9384 TIDSortedElemSet::iterator eIt;
9385 set<const SMDS_MeshNode*>::iterator nIt;
9387 // check that given nodes belong to given elements
9388 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9389 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9390 int firstIndex = -1, secondIndex = -1;
9391 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9392 const SMDS_MeshElement* elem = *eIt;
9393 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9394 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9395 if ( firstIndex > -1 && secondIndex > -1 ) break;
9397 if ( firstIndex < 0 || secondIndex < 0 ) {
9398 // we can simply return until temporary faces created
9399 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9402 // -----------------------------------------------------------
9403 // 1a. Collect nodes of existing faces
9404 // and build set of face nodes in order to detect missing
9405 // faces corresponding to sides of volumes
9406 // -----------------------------------------------------------
9408 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9410 // loop on the given element of a side
9411 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9412 //const SMDS_MeshElement* elem = *eIt;
9413 const SMDS_MeshElement* elem = *eIt;
9414 if ( elem->GetType() == SMDSAbs_Face ) {
9415 faceSet->insert( elem );
9416 set <const SMDS_MeshNode*> faceNodeSet;
9417 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9418 while ( nodeIt->more() ) {
9419 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9420 nodeSet->insert( n );
9421 faceNodeSet.insert( n );
9423 setOfFaceNodeSet.insert( faceNodeSet );
9425 else if ( elem->GetType() == SMDSAbs_Volume )
9426 volSet->insert( elem );
9428 // ------------------------------------------------------------------------------
9429 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9430 // ------------------------------------------------------------------------------
9432 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9433 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9434 while ( fIt->more() ) { // loop on faces sharing a node
9435 const SMDS_MeshElement* f = fIt->next();
9436 if ( faceSet->find( f ) == faceSet->end() ) {
9437 // check if all nodes are in nodeSet and
9438 // complete setOfFaceNodeSet if they are
9439 set <const SMDS_MeshNode*> faceNodeSet;
9440 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9441 bool allInSet = true;
9442 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9443 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9444 if ( nodeSet->find( n ) == nodeSet->end() )
9447 faceNodeSet.insert( n );
9450 faceSet->insert( f );
9451 setOfFaceNodeSet.insert( faceNodeSet );
9457 // -------------------------------------------------------------------------
9458 // 1c. Create temporary faces representing sides of volumes if correspondent
9459 // face does not exist
9460 // -------------------------------------------------------------------------
9462 if ( !volSet->empty() ) {
9463 //int nodeSetSize = nodeSet->size();
9465 // loop on given volumes
9466 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9467 SMDS_VolumeTool vol (*vIt);
9468 // loop on volume faces: find free faces
9469 // --------------------------------------
9470 list<const SMDS_MeshElement* > freeFaceList;
9471 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9472 if ( !vol.IsFreeFace( iFace ))
9474 // check if there is already a face with same nodes in a face set
9475 const SMDS_MeshElement* aFreeFace = 0;
9476 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9477 int nbNodes = vol.NbFaceNodes( iFace );
9478 set <const SMDS_MeshNode*> faceNodeSet;
9479 vol.GetFaceNodes( iFace, faceNodeSet );
9480 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9482 // no such a face is given but it still can exist, check it
9483 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9484 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9487 // create a temporary face
9488 if ( nbNodes == 3 ) {
9489 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9490 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9492 else if ( nbNodes == 4 ) {
9493 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9494 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9497 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9498 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9499 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9502 tempFaceList.push_back( aFreeFace );
9506 freeFaceList.push_back( aFreeFace );
9508 } // loop on faces of a volume
9510 // choose one of several free faces of a volume
9511 // --------------------------------------------
9512 if ( freeFaceList.size() > 1 ) {
9513 // choose a face having max nb of nodes shared by other elems of a side
9514 int maxNbNodes = -1;
9515 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9516 while ( fIt != freeFaceList.end() ) { // loop on free faces
9517 int nbSharedNodes = 0;
9518 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9519 while ( nodeIt->more() ) { // loop on free face nodes
9520 const SMDS_MeshNode* n =
9521 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9522 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9523 while ( invElemIt->more() ) {
9524 const SMDS_MeshElement* e = invElemIt->next();
9525 nbSharedNodes += faceSet->count( e );
9526 nbSharedNodes += elemSet->count( e );
9529 if ( nbSharedNodes > maxNbNodes ) {
9530 maxNbNodes = nbSharedNodes;
9531 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9533 else if ( nbSharedNodes == maxNbNodes ) {
9537 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9540 if ( freeFaceList.size() > 1 )
9542 // could not choose one face, use another way
9543 // choose a face most close to the bary center of the opposite side
9544 gp_XYZ aBC( 0., 0., 0. );
9545 set <const SMDS_MeshNode*> addedNodes;
9546 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9547 eIt = elemSet2->begin();
9548 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9549 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9550 while ( nodeIt->more() ) { // loop on free face nodes
9551 const SMDS_MeshNode* n =
9552 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9553 if ( addedNodes.insert( n ).second )
9554 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9557 aBC /= addedNodes.size();
9558 double minDist = DBL_MAX;
9559 fIt = freeFaceList.begin();
9560 while ( fIt != freeFaceList.end() ) { // loop on free faces
9562 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9563 while ( nodeIt->more() ) { // loop on free face nodes
9564 const SMDS_MeshNode* n =
9565 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9566 gp_XYZ p( n->X(),n->Y(),n->Z() );
9567 dist += ( aBC - p ).SquareModulus();
9569 if ( dist < minDist ) {
9571 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9574 fIt = freeFaceList.erase( fIt++ );
9577 } // choose one of several free faces of a volume
9579 if ( freeFaceList.size() == 1 ) {
9580 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9581 faceSet->insert( aFreeFace );
9582 // complete a node set with nodes of a found free face
9583 // for ( iNode = 0; iNode < ; iNode++ )
9584 // nodeSet->insert( fNodes[ iNode ] );
9587 } // loop on volumes of a side
9589 // // complete a set of faces if new nodes in a nodeSet appeared
9590 // // ----------------------------------------------------------
9591 // if ( nodeSetSize != nodeSet->size() ) {
9592 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9593 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9594 // while ( fIt->more() ) { // loop on faces sharing a node
9595 // const SMDS_MeshElement* f = fIt->next();
9596 // if ( faceSet->find( f ) == faceSet->end() ) {
9597 // // check if all nodes are in nodeSet and
9598 // // complete setOfFaceNodeSet if they are
9599 // set <const SMDS_MeshNode*> faceNodeSet;
9600 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9601 // bool allInSet = true;
9602 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9603 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9604 // if ( nodeSet->find( n ) == nodeSet->end() )
9605 // allInSet = false;
9607 // faceNodeSet.insert( n );
9609 // if ( allInSet ) {
9610 // faceSet->insert( f );
9611 // setOfFaceNodeSet.insert( faceNodeSet );
9617 } // Create temporary faces, if there are volumes given
9620 if ( faceSet1.size() != faceSet2.size() ) {
9621 // delete temporary faces: they are in reverseElements of actual nodes
9622 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9623 // while ( tmpFaceIt->more() )
9624 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9625 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9626 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9627 // aMesh->RemoveElement(*tmpFaceIt);
9628 MESSAGE("Diff nb of faces");
9629 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9632 // ============================================================
9633 // 2. Find nodes to merge:
9634 // bind a node to remove to a node to put instead
9635 // ============================================================
9637 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9638 if ( theFirstNode1 != theFirstNode2 )
9639 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9640 if ( theSecondNode1 != theSecondNode2 )
9641 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9643 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9644 set< long > linkIdSet; // links to process
9645 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9647 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9648 list< NLink > linkList[2];
9649 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9650 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9651 // loop on links in linkList; find faces by links and append links
9652 // of the found faces to linkList
9653 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9654 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9656 NLink link[] = { *linkIt[0], *linkIt[1] };
9657 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9658 if ( !linkIdSet.count( linkID ) )
9661 // by links, find faces in the face sets,
9662 // and find indices of link nodes in the found faces;
9663 // in a face set, there is only one or no face sharing a link
9664 // ---------------------------------------------------------------
9666 const SMDS_MeshElement* face[] = { 0, 0 };
9667 vector<const SMDS_MeshNode*> fnodes[2];
9668 int iLinkNode[2][2];
9669 TIDSortedElemSet avoidSet;
9670 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9671 const SMDS_MeshNode* n1 = link[iSide].first;
9672 const SMDS_MeshNode* n2 = link[iSide].second;
9673 //cout << "Side " << iSide << " ";
9674 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9675 // find a face by two link nodes
9676 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9677 *faceSetPtr[ iSide ], avoidSet,
9678 &iLinkNode[iSide][0],
9679 &iLinkNode[iSide][1] );
9682 //cout << " F " << face[ iSide]->GetID() <<endl;
9683 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9684 // put face nodes to fnodes
9685 SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9686 fnodes[ iSide ].assign( nIt, nEnd );
9687 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9691 // check similarity of elements of the sides
9692 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9693 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9694 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9695 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9698 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9700 break; // do not return because it's necessary to remove tmp faces
9703 // set nodes to merge
9704 // -------------------
9706 if ( face[0] && face[1] ) {
9707 const int nbNodes = face[0]->NbNodes();
9708 if ( nbNodes != face[1]->NbNodes() ) {
9709 MESSAGE("Diff nb of face nodes");
9710 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9711 break; // do not return because it s necessary to remove tmp faces
9713 bool reverse[] = { false, false }; // order of nodes in the link
9714 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9715 // analyse link orientation in faces
9716 int i1 = iLinkNode[ iSide ][ 0 ];
9717 int i2 = iLinkNode[ iSide ][ 1 ];
9718 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9720 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9721 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9722 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9724 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9725 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9728 // add other links of the faces to linkList
9729 // -----------------------------------------
9731 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9732 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9733 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9734 if ( !iter_isnew.second ) { // already in a set: no need to process
9735 linkIdSet.erase( iter_isnew.first );
9737 else // new in set == encountered for the first time: add
9739 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9740 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9741 linkList[0].push_back ( NLink( n1, n2 ));
9742 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9747 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9750 } // loop on link lists
9752 if ( aResult == SEW_OK &&
9753 ( //linkIt[0] != linkList[0].end() ||
9754 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9755 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9756 " " << (faceSetPtr[1]->empty()));
9757 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9760 // ====================================================================
9761 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9762 // ====================================================================
9764 // delete temporary faces
9765 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9766 // while ( tmpFaceIt->more() )
9767 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9768 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9769 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9770 aMesh->RemoveElement(*tmpFaceIt);
9772 if ( aResult != SEW_OK)
9775 list< int > nodeIDsToRemove;
9776 vector< const SMDS_MeshNode*> nodes;
9777 ElemFeatures elemType;
9779 // loop on nodes replacement map
9780 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9781 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9782 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9784 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9785 nodeIDsToRemove.push_back( nToRemove->GetID() );
9786 // loop on elements sharing nToRemove
9787 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9788 while ( invElemIt->more() ) {
9789 const SMDS_MeshElement* e = invElemIt->next();
9790 // get a new suite of nodes: make replacement
9791 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9792 nodes.resize( nbNodes );
9793 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9794 while ( nIt->more() ) {
9795 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9796 nnIt = nReplaceMap.find( n );
9797 if ( nnIt != nReplaceMap.end() ) {
9803 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9804 // elemIDsToRemove.push_back( e->GetID() );
9808 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9809 aMesh->RemoveElement( e );
9811 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9813 AddToSameGroups( newElem, e, aMesh );
9814 if ( int aShapeId = e->getshapeId() )
9815 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9821 Remove( nodeIDsToRemove, true );
9826 //================================================================================
9828 * \brief Find corresponding nodes in two sets of faces
9829 * \param theSide1 - first face set
9830 * \param theSide2 - second first face
9831 * \param theFirstNode1 - a boundary node of set 1
9832 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9833 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9834 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9835 * \param nReplaceMap - output map of corresponding nodes
9836 * \return bool - is a success or not
9838 //================================================================================
9841 //#define DEBUG_MATCHING_NODES
9844 SMESH_MeshEditor::Sew_Error
9845 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9846 set<const SMDS_MeshElement*>& theSide2,
9847 const SMDS_MeshNode* theFirstNode1,
9848 const SMDS_MeshNode* theFirstNode2,
9849 const SMDS_MeshNode* theSecondNode1,
9850 const SMDS_MeshNode* theSecondNode2,
9851 TNodeNodeMap & nReplaceMap)
9853 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9855 nReplaceMap.clear();
9856 if ( theFirstNode1 != theFirstNode2 )
9857 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9858 if ( theSecondNode1 != theSecondNode2 )
9859 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9861 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9862 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9864 list< NLink > linkList[2];
9865 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9866 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9868 // loop on links in linkList; find faces by links and append links
9869 // of the found faces to linkList
9870 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9871 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9872 NLink link[] = { *linkIt[0], *linkIt[1] };
9873 if ( linkSet.find( link[0] ) == linkSet.end() )
9876 // by links, find faces in the face sets,
9877 // and find indices of link nodes in the found faces;
9878 // in a face set, there is only one or no face sharing a link
9879 // ---------------------------------------------------------------
9881 const SMDS_MeshElement* face[] = { 0, 0 };
9882 list<const SMDS_MeshNode*> notLinkNodes[2];
9883 //bool reverse[] = { false, false }; // order of notLinkNodes
9885 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9887 const SMDS_MeshNode* n1 = link[iSide].first;
9888 const SMDS_MeshNode* n2 = link[iSide].second;
9889 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9890 set< const SMDS_MeshElement* > facesOfNode1;
9891 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9893 // during a loop of the first node, we find all faces around n1,
9894 // during a loop of the second node, we find one face sharing both n1 and n2
9895 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9896 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9897 while ( fIt->more() ) { // loop on faces sharing a node
9898 const SMDS_MeshElement* f = fIt->next();
9899 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9900 ! facesOfNode1.insert( f ).second ) // f encounters twice
9902 if ( face[ iSide ] ) {
9903 MESSAGE( "2 faces per link " );
9904 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9907 faceSet->erase( f );
9909 // get not link nodes
9910 int nbN = f->NbNodes();
9911 if ( f->IsQuadratic() )
9913 nbNodes[ iSide ] = nbN;
9914 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9915 int i1 = f->GetNodeIndex( n1 );
9916 int i2 = f->GetNodeIndex( n2 );
9917 int iEnd = nbN, iBeg = -1, iDelta = 1;
9918 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9920 std::swap( iEnd, iBeg ); iDelta = -1;
9925 if ( i == iEnd ) i = iBeg + iDelta;
9926 if ( i == i1 ) break;
9927 nodes.push_back ( f->GetNode( i ) );
9933 // check similarity of elements of the sides
9934 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9935 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9936 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9937 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9940 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9944 // set nodes to merge
9945 // -------------------
9947 if ( face[0] && face[1] ) {
9948 if ( nbNodes[0] != nbNodes[1] ) {
9949 MESSAGE("Diff nb of face nodes");
9950 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9952 #ifdef DEBUG_MATCHING_NODES
9953 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9954 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9955 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9957 int nbN = nbNodes[0];
9959 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9960 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9961 for ( int i = 0 ; i < nbN - 2; ++i ) {
9962 #ifdef DEBUG_MATCHING_NODES
9963 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9965 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9969 // add other links of the face 1 to linkList
9970 // -----------------------------------------
9972 const SMDS_MeshElement* f0 = face[0];
9973 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
9974 for ( int i = 0; i < nbN; i++ )
9976 const SMDS_MeshNode* n2 = f0->GetNode( i );
9977 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
9978 linkSet.insert( SMESH_TLink( n1, n2 ));
9979 if ( !iter_isnew.second ) { // already in a set: no need to process
9980 linkSet.erase( iter_isnew.first );
9982 else // new in set == encountered for the first time: add
9984 #ifdef DEBUG_MATCHING_NODES
9985 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
9986 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
9988 linkList[0].push_back ( NLink( n1, n2 ));
9989 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9994 } // loop on link lists
9999 namespace // automatically find theAffectedElems for DoubleNodes()
10001 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10003 //--------------------------------------------------------------------------------
10004 // Nodes shared by adjacent FissureBorder's.
10005 // 1 node if FissureBorder separates faces
10006 // 2 nodes if FissureBorder separates volumes
10009 const SMDS_MeshNode* _nodes[2];
10012 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10016 _nbNodes = bool( n1 ) + bool( n2 );
10017 if ( _nbNodes == 2 && n1 > n2 )
10018 std::swap( _nodes[0], _nodes[1] );
10020 bool operator<( const SubBorder& other ) const
10022 for ( int i = 0; i < _nbNodes; ++i )
10024 if ( _nodes[i] < other._nodes[i] ) return true;
10025 if ( _nodes[i] > other._nodes[i] ) return false;
10031 //--------------------------------------------------------------------------------
10032 // Map a SubBorder to all FissureBorder it bounds
10033 struct FissureBorder;
10034 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10035 typedef TBorderLinks::iterator TMappedSub;
10037 //--------------------------------------------------------------------------------
10039 * \brief Element border (volume facet or face edge) at a fissure
10041 struct FissureBorder
10043 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10044 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10046 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10047 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10049 FissureBorder( FissureBorder && from ) // move constructor
10051 std::swap( _nodes, from._nodes );
10052 std::swap( _sortedNodes, from._sortedNodes );
10053 _elems[0] = from._elems[0];
10054 _elems[1] = from._elems[1];
10057 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10058 std::vector< const SMDS_MeshElement* > & adjElems)
10059 : _nodes( elemToDuplicate->NbCornerNodes() )
10061 for ( size_t i = 0; i < _nodes.size(); ++i )
10062 _nodes[i] = elemToDuplicate->GetNode( i );
10064 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10065 findAdjacent( type, adjElems );
10068 FissureBorder( const SMDS_MeshNode** nodes,
10069 const size_t nbNodes,
10070 const SMDSAbs_ElementType adjElemsType,
10071 std::vector< const SMDS_MeshElement* > & adjElems)
10072 : _nodes( nodes, nodes + nbNodes )
10074 findAdjacent( adjElemsType, adjElems );
10077 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10078 std::vector< const SMDS_MeshElement* > & adjElems)
10080 _elems[0] = _elems[1] = 0;
10082 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10083 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10084 _elems[i] = adjElems[i];
10087 bool operator<( const FissureBorder& other ) const
10089 return GetSortedNodes() < other.GetSortedNodes();
10092 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10094 if ( _sortedNodes.empty() && !_nodes.empty() )
10096 FissureBorder* me = const_cast<FissureBorder*>( this );
10097 me->_sortedNodes = me->_nodes;
10098 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10100 return _sortedNodes;
10103 size_t NbSub() const
10105 return _nodes.size();
10108 SubBorder Sub(size_t i) const
10110 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10113 void AddSelfTo( TBorderLinks& borderLinks )
10115 _mappedSubs.resize( NbSub() );
10116 for ( size_t i = 0; i < NbSub(); ++i )
10118 TBorderLinks::iterator s2b =
10119 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10120 s2b->second.push_back( this );
10121 _mappedSubs[ i ] = s2b;
10130 const SMDS_MeshElement* GetMarkedElem() const
10132 if ( _nodes.empty() ) return 0; // cleared
10133 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10134 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10138 gp_XYZ GetNorm() const // normal to the border
10141 if ( _nodes.size() == 2 )
10143 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10144 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10146 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10149 gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10150 norm = bordDir ^ avgNorm;
10154 SMESH_NodeXYZ p0( _nodes[0] );
10155 SMESH_NodeXYZ p1( _nodes[1] );
10156 SMESH_NodeXYZ p2( _nodes[2] );
10157 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10159 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10165 void ChooseSide() // mark an _elem located at positive side of fissure
10167 _elems[0]->setIsMarked( true );
10168 gp_XYZ norm = GetNorm();
10169 double maxX = norm.Coord(1);
10170 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10171 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10174 _elems[0]->setIsMarked( false );
10175 _elems[1]->setIsMarked( true );
10179 }; // struct FissureBorder
10181 //--------------------------------------------------------------------------------
10183 * \brief Classifier of elements at fissure edge
10185 class FissureNormal
10187 std::vector< gp_XYZ > _normals;
10191 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10194 _normals.reserve(2);
10195 _normals.push_back( bord.GetNorm() );
10196 if ( _normals.size() == 2 )
10197 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10200 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10203 switch ( _normals.size() ) {
10206 isIn = !isOut( n, _normals[0], elem );
10211 bool in1 = !isOut( n, _normals[0], elem );
10212 bool in2 = !isOut( n, _normals[1], elem );
10213 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10220 //================================================================================
10222 * \brief Classify an element by a plane passing through a node
10224 //================================================================================
10226 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10228 SMESH_NodeXYZ p = n;
10230 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10232 SMESH_NodeXYZ pi = elem->GetNode( i );
10233 sumDot += norm * ( pi - p );
10235 return sumDot < -1e-100;
10238 //================================================================================
10240 * \brief Find FissureBorder's by nodes to duplicate
10242 //================================================================================
10244 void findFissureBorders( const TIDSortedElemSet& theNodes,
10245 std::vector< FissureBorder > & theFissureBorders )
10247 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10248 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10250 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10251 if ( n->NbInverseElements( elemType ) == 0 )
10253 elemType = SMDSAbs_Face;
10254 if ( n->NbInverseElements( elemType ) == 0 )
10257 // unmark elements touching the fissure
10258 for ( ; nIt != theNodes.end(); ++nIt )
10259 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10261 // loop on elements touching the fissure to get their borders belonging to the fissure
10262 std::set< FissureBorder > fissureBorders;
10263 std::vector< const SMDS_MeshElement* > adjElems;
10264 std::vector< const SMDS_MeshNode* > nodes;
10265 SMDS_VolumeTool volTool;
10266 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10268 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10269 while ( invIt->more() )
10271 const SMDS_MeshElement* eInv = invIt->next();
10272 if ( eInv->isMarked() ) continue;
10273 eInv->setIsMarked( true );
10275 if ( elemType == SMDSAbs_Volume )
10277 volTool.Set( eInv );
10278 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10279 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10281 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10282 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10284 bool allOnFissure = true;
10285 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10286 if (( allOnFissure = theNodes.count( nn[ iN ])))
10287 nodes.push_back( nn[ iN ]);
10288 if ( allOnFissure )
10289 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10290 elemType, adjElems )));
10293 else // elemType == SMDSAbs_Face
10295 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10296 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10297 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10299 nn[1] = eInv->GetNode( iN );
10300 onFissure1 = theNodes.count( nn[1] );
10301 if ( onFissure0 && onFissure1 )
10302 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10304 onFissure0 = onFissure1;
10310 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10311 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10312 for ( ; bord != fissureBorders.end(); ++bord )
10314 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10317 } // findFissureBorders()
10319 //================================================================================
10321 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10322 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10323 * \param [in] theNodesNot - nodes not to duplicate
10324 * \param [out] theAffectedElems - the found elements
10326 //================================================================================
10328 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10329 TIDSortedElemSet& theAffectedElems)
10331 if ( theElemsOrNodes.empty() ) return;
10333 // find FissureBorder's
10335 std::vector< FissureBorder > fissure;
10336 std::vector< const SMDS_MeshElement* > elemsByFacet;
10338 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10339 if ( (*elIt)->GetType() == SMDSAbs_Node )
10341 findFissureBorders( theElemsOrNodes, fissure );
10345 fissure.reserve( theElemsOrNodes.size() );
10346 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10347 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10349 if ( fissure.empty() )
10352 // fill borderLinks
10354 TBorderLinks borderLinks;
10356 for ( size_t i = 0; i < fissure.size(); ++i )
10358 fissure[i].AddSelfTo( borderLinks );
10361 // get theAffectedElems
10363 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10364 for ( size_t i = 0; i < fissure.size(); ++i )
10365 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10367 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10368 false, /*markElem=*/true );
10371 std::vector<const SMDS_MeshNode *> facetNodes;
10372 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10373 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10375 // choose a side of fissure
10376 fissure[0].ChooseSide();
10377 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10379 size_t nbCheckedBorders = 0;
10380 while ( nbCheckedBorders < fissure.size() )
10382 // find a FissureBorder to treat
10383 FissureBorder* bord = 0;
10384 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10385 if ( fissure[i].GetMarkedElem() )
10386 bord = & fissure[i];
10387 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10388 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10390 bord = & fissure[i];
10391 bord->ChooseSide();
10392 theAffectedElems.insert( bord->GetMarkedElem() );
10394 if ( !bord ) return;
10395 ++nbCheckedBorders;
10397 // treat FissureBorder's linked to bord
10398 fissureNodes.clear();
10399 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10400 for ( size_t i = 0; i < bord->NbSub(); ++i )
10402 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10403 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10404 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10405 const SubBorder& sb = l2b->first;
10406 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10408 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10410 for ( int j = 0; j < sb._nbNodes; ++j )
10411 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10415 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10416 // until an elem adjacent to a neighbour FissureBorder is found
10417 facetNodes.clear();
10418 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10419 facetNodes.resize( sb._nbNodes + 1 );
10423 // check if bordElem is adjacent to a neighbour FissureBorder
10424 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10426 FissureBorder* bord2 = linkedBorders[j];
10427 if ( bord2 == bord ) continue;
10428 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10431 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10436 // find the next bordElem
10437 const SMDS_MeshElement* nextBordElem = 0;
10438 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10440 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10441 if ( fissureNodes.count( n )) continue;
10443 facetNodes[ sb._nbNodes ] = n;
10444 elemsByFacet.clear();
10445 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10447 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10448 if ( elemsByFacet[ iE ] != bordElem &&
10449 !elemsByFacet[ iE ]->isMarked() )
10451 theAffectedElems.insert( elemsByFacet[ iE ]);
10452 elemsByFacet[ iE ]->setIsMarked( true );
10453 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10454 nextBordElem = elemsByFacet[ iE ];
10458 bordElem = nextBordElem;
10460 } // while ( bordElem )
10462 linkedBorders.clear(); // not to treat this link any more
10464 } // loop on SubBorder's of a FissureBorder
10468 } // loop on FissureBorder's
10471 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10473 // mark nodes of theAffectedElems
10474 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10476 // unmark nodes of the fissure
10477 elIt = theElemsOrNodes.begin();
10478 if ( (*elIt)->GetType() == SMDSAbs_Node )
10479 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10481 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10483 std::vector< gp_XYZ > normVec;
10485 // loop on nodes of the fissure, add elements having marked nodes
10486 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10488 const SMDS_MeshElement* e = (*elIt);
10489 if ( e->GetType() != SMDSAbs_Node )
10490 e->setIsMarked( true ); // avoid adding a fissure element
10492 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10494 const SMDS_MeshNode* n = e->GetNode( iN );
10495 if ( fissEdgeNodes2Norm.count( n ))
10498 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10499 while ( invIt->more() )
10501 const SMDS_MeshElement* eInv = invIt->next();
10502 if ( eInv->isMarked() ) continue;
10503 eInv->setIsMarked( true );
10505 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10506 while( nIt->more() )
10507 if ( nIt->next()->isMarked())
10509 theAffectedElems.insert( eInv );
10510 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10511 n->setIsMarked( false );
10518 // add elements on the fissure edge
10519 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10520 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10522 const SMDS_MeshNode* edgeNode = n2N->first;
10523 const FissureNormal & normals = n2N->second;
10525 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10526 while ( invIt->more() )
10528 const SMDS_MeshElement* eInv = invIt->next();
10529 if ( eInv->isMarked() ) continue;
10530 eInv->setIsMarked( true );
10532 // classify eInv using normals
10533 bool toAdd = normals.IsIn( edgeNode, eInv );
10534 if ( toAdd ) // check if all nodes lie on the fissure edge
10536 bool notOnEdge = false;
10537 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
10538 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10543 theAffectedElems.insert( eInv );
10549 } // findAffectedElems()
10552 //================================================================================
10554 * \brief Create elements equal (on same nodes) to given ones
10555 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10556 * elements of the uppest dimension are duplicated.
10558 //================================================================================
10560 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10562 ClearLastCreated();
10563 SMESHDS_Mesh* mesh = GetMeshDS();
10565 // get an element type and an iterator over elements
10567 SMDSAbs_ElementType type = SMDSAbs_All;
10568 SMDS_ElemIteratorPtr elemIt;
10569 if ( theElements.empty() )
10571 if ( mesh->NbNodes() == 0 )
10573 // get most complex type
10574 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10575 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10576 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10578 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10579 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10582 elemIt = mesh->elementsIterator( type );
10588 //type = (*theElements.begin())->GetType();
10589 elemIt = SMESHUtils::elemSetIterator( theElements );
10592 // un-mark all elements to avoid duplicating just created elements
10593 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10595 // duplicate elements
10597 ElemFeatures elemType;
10599 vector< const SMDS_MeshNode* > nodes;
10600 while ( elemIt->more() )
10602 const SMDS_MeshElement* elem = elemIt->next();
10603 if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10604 ( elem->isMarked() ))
10607 elemType.Init( elem, /*basicOnly=*/false );
10608 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10610 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10611 newElem->setIsMarked( true );
10615 //================================================================================
10617 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10618 \param theElems - the list of elements (edges or faces) to be replicated
10619 The nodes for duplication could be found from these elements
10620 \param theNodesNot - list of nodes to NOT replicate
10621 \param theAffectedElems - the list of elements (cells and edges) to which the
10622 replicated nodes should be associated to.
10623 \return TRUE if operation has been completed successfully, FALSE otherwise
10625 //================================================================================
10627 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10628 const TIDSortedElemSet& theNodesNot,
10629 const TIDSortedElemSet& theAffectedElems )
10631 ClearLastCreated();
10633 if ( theElems.size() == 0 )
10636 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10641 TNodeNodeMap anOldNodeToNewNode;
10642 // duplicate elements and nodes
10643 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10644 // replce nodes by duplications
10645 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10649 //================================================================================
10651 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10652 \param theMeshDS - mesh instance
10653 \param theElems - the elements replicated or modified (nodes should be changed)
10654 \param theNodesNot - nodes to NOT replicate
10655 \param theNodeNodeMap - relation of old node to new created node
10656 \param theIsDoubleElem - flag os to replicate element or modify
10657 \return TRUE if operation has been completed successfully, FALSE otherwise
10659 //================================================================================
10661 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10662 const TIDSortedElemSet& theElems,
10663 const TIDSortedElemSet& theNodesNot,
10664 TNodeNodeMap& theNodeNodeMap,
10665 const bool theIsDoubleElem )
10667 // iterate through element and duplicate them (by nodes duplication)
10669 std::vector<const SMDS_MeshNode*> newNodes;
10670 ElemFeatures elemType;
10672 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10673 for ( ; elemItr != theElems.end(); ++elemItr )
10675 const SMDS_MeshElement* anElem = *elemItr;
10679 // duplicate nodes to duplicate element
10680 bool isDuplicate = false;
10681 newNodes.resize( anElem->NbNodes() );
10682 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10684 while ( anIter->more() )
10686 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10687 const SMDS_MeshNode* aNewNode = aCurrNode;
10688 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10689 if ( n2n != theNodeNodeMap.end() )
10691 aNewNode = n2n->second;
10693 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10696 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10697 copyPosition( aCurrNode, aNewNode );
10698 theNodeNodeMap[ aCurrNode ] = aNewNode;
10699 myLastCreatedNodes.push_back( aNewNode );
10701 isDuplicate |= (aCurrNode != aNewNode);
10702 newNodes[ ind++ ] = aNewNode;
10704 if ( !isDuplicate )
10707 if ( theIsDoubleElem )
10708 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10710 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10717 //================================================================================
10719 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10720 \param theNodes - identifiers of nodes to be doubled
10721 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10722 nodes. If list of element identifiers is empty then nodes are doubled but
10723 they not assigned to elements
10724 \return TRUE if operation has been completed successfully, FALSE otherwise
10726 //================================================================================
10728 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10729 const std::list< int >& theListOfModifiedElems )
10731 ClearLastCreated();
10733 if ( theListOfNodes.size() == 0 )
10736 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10740 // iterate through nodes and duplicate them
10742 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10744 std::list< int >::const_iterator aNodeIter;
10745 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10747 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10753 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10756 copyPosition( aNode, aNewNode );
10757 anOldNodeToNewNode[ aNode ] = aNewNode;
10758 myLastCreatedNodes.push_back( aNewNode );
10762 // Change nodes of elements
10764 std::vector<const SMDS_MeshNode*> aNodeArr;
10766 std::list< int >::const_iterator anElemIter;
10767 for ( anElemIter = theListOfModifiedElems.begin();
10768 anElemIter != theListOfModifiedElems.end();
10771 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10775 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10776 for( size_t i = 0; i < aNodeArr.size(); ++i )
10778 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10779 anOldNodeToNewNode.find( aNodeArr[ i ]);
10780 if ( n2n != anOldNodeToNewNode.end() )
10781 aNodeArr[ i ] = n2n->second;
10783 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10791 //================================================================================
10793 \brief Check if element located inside shape
10794 \return TRUE if IN or ON shape, FALSE otherwise
10796 //================================================================================
10798 template<class Classifier>
10799 bool isInside(const SMDS_MeshElement* theElem,
10800 Classifier& theClassifier,
10801 const double theTol)
10803 gp_XYZ centerXYZ (0, 0, 0);
10804 for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10805 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10807 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10808 theClassifier.Perform(aPnt, theTol);
10809 TopAbs_State aState = theClassifier.State();
10810 return (aState == TopAbs_IN || aState == TopAbs_ON );
10813 //================================================================================
10815 * \brief Classifier of the 3D point on the TopoDS_Face
10816 * with interaface suitable for isInside()
10818 //================================================================================
10820 struct _FaceClassifier
10822 Extrema_ExtPS _extremum;
10823 BRepAdaptor_Surface _surface;
10824 TopAbs_State _state;
10826 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10828 _extremum.Initialize( _surface,
10829 _surface.FirstUParameter(), _surface.LastUParameter(),
10830 _surface.FirstVParameter(), _surface.LastVParameter(),
10831 _surface.Tolerance(), _surface.Tolerance() );
10833 void Perform(const gp_Pnt& aPnt, double theTol)
10836 _state = TopAbs_OUT;
10837 _extremum.Perform(aPnt);
10838 if ( _extremum.IsDone() )
10839 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10840 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10842 TopAbs_State State() const
10849 //================================================================================
10851 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10852 This method is the first step of DoubleNodeElemGroupsInRegion.
10853 \param theElems - list of groups of elements (edges or faces) to be replicated
10854 \param theNodesNot - list of groups of nodes not to replicated
10855 \param theShape - shape to detect affected elements (element which geometric center
10856 located on or inside shape). If the shape is null, detection is done on faces orientations
10857 (select elements with a gravity center on the side given by faces normals).
10858 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10859 The replicated nodes should be associated to affected elements.
10861 \sa DoubleNodeElemGroupsInRegion()
10863 //================================================================================
10865 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10866 const TIDSortedElemSet& theNodesNot,
10867 const TopoDS_Shape& theShape,
10868 TIDSortedElemSet& theAffectedElems)
10870 if ( theShape.IsNull() )
10872 findAffectedElems( theElems, theAffectedElems );
10876 const double aTol = Precision::Confusion();
10877 std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10878 std::unique_ptr<_FaceClassifier> aFaceClassifier;
10879 if ( theShape.ShapeType() == TopAbs_SOLID )
10881 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10882 bsc3d->PerformInfinitePoint(aTol);
10884 else if (theShape.ShapeType() == TopAbs_FACE )
10886 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10889 // iterates on indicated elements and get elements by back references from their nodes
10890 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10891 for ( ; elemItr != theElems.end(); ++elemItr )
10893 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10894 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10895 while ( nodeItr->more() )
10897 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10898 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10900 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10901 while ( backElemItr->more() )
10903 const SMDS_MeshElement* curElem = backElemItr->next();
10904 if ( curElem && theElems.find(curElem) == theElems.end() &&
10906 isInside( curElem, *bsc3d, aTol ) :
10907 isInside( curElem, *aFaceClassifier, aTol )))
10908 theAffectedElems.insert( curElem );
10916 //================================================================================
10918 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10919 \param theElems - group of of elements (edges or faces) to be replicated
10920 \param theNodesNot - group of nodes not to replicate
10921 \param theShape - shape to detect affected elements (element which geometric center
10922 located on or inside shape).
10923 The replicated nodes should be associated to affected elements.
10924 \return TRUE if operation has been completed successfully, FALSE otherwise
10926 //================================================================================
10928 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10929 const TIDSortedElemSet& theNodesNot,
10930 const TopoDS_Shape& theShape )
10932 if ( theShape.IsNull() )
10935 const double aTol = Precision::Confusion();
10936 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10937 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
10938 if ( theShape.ShapeType() == TopAbs_SOLID )
10940 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10941 bsc3d->PerformInfinitePoint(aTol);
10943 else if (theShape.ShapeType() == TopAbs_FACE )
10945 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
10948 // iterates on indicated elements and get elements by back references from their nodes
10949 TIDSortedElemSet anAffected;
10950 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10951 for ( ; elemItr != theElems.end(); ++elemItr )
10953 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10957 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10958 while ( nodeItr->more() )
10960 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10961 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10963 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10964 while ( backElemItr->more() )
10966 const SMDS_MeshElement* curElem = backElemItr->next();
10967 if ( curElem && theElems.find(curElem) == theElems.end() &&
10969 isInside( curElem, *bsc3d, aTol ) :
10970 isInside( curElem, *aFaceClassifier, aTol )))
10971 anAffected.insert( curElem );
10975 return DoubleNodes( theElems, theNodesNot, anAffected );
10979 * \brief compute an oriented angle between two planes defined by four points.
10980 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10981 * @param p0 base of the rotation axe
10982 * @param p1 extremity of the rotation axe
10983 * @param g1 belongs to the first plane
10984 * @param g2 belongs to the second plane
10986 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10988 gp_Vec vref(p0, p1);
10991 gp_Vec n1 = vref.Crossed(v1);
10992 gp_Vec n2 = vref.Crossed(v2);
10994 return n2.AngleWithRef(n1, vref);
10996 catch ( Standard_Failure ) {
10998 return Max( v1.Magnitude(), v2.Magnitude() );
11002 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11003 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11004 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11005 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11006 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11007 * 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.
11008 * 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.
11009 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11010 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11011 * \param theElems - list of groups of volumes, where a group of volume is a set of
11012 * SMDS_MeshElements sorted by Id.
11013 * \param createJointElems - if TRUE, create the elements
11014 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11015 * the boundary between \a theDomains and the rest mesh
11016 * \return TRUE if operation has been completed successfully, FALSE otherwise
11018 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11019 bool createJointElems,
11020 bool onAllBoundaries)
11022 // MESSAGE("----------------------------------------------");
11023 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11024 // MESSAGE("----------------------------------------------");
11026 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11027 meshDS->BuildDownWardConnectivity(true);
11029 SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11031 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11032 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11033 // build the list of nodes shared by 2 or more domains, with their domain indexes
11035 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11036 std::map<int,int>celldom; // cell vtkId --> domain
11037 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11038 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11039 faceDomains.clear();
11041 cellDomains.clear();
11042 nodeDomains.clear();
11043 std::map<int,int> emptyMap;
11044 std::set<int> emptySet;
11047 //MESSAGE(".. Number of domains :"<<theElems.size());
11049 TIDSortedElemSet theRestDomElems;
11050 const int iRestDom = -1;
11051 const int idom0 = onAllBoundaries ? iRestDom : 0;
11052 const int nbDomains = theElems.size();
11054 // Check if the domains do not share an element
11055 for (int idom = 0; idom < nbDomains-1; idom++)
11057 // MESSAGE("... Check of domain #" << idom);
11058 const TIDSortedElemSet& domain = theElems[idom];
11059 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11060 for (; elemItr != domain.end(); ++elemItr)
11062 const SMDS_MeshElement* anElem = *elemItr;
11063 int idombisdeb = idom + 1 ;
11064 // check if the element belongs to a domain further in the list
11065 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11067 const TIDSortedElemSet& domainbis = theElems[idombis];
11068 if ( domainbis.count( anElem ))
11070 MESSAGE(".... Domain #" << idom);
11071 MESSAGE(".... Domain #" << idombis);
11072 throw SALOME_Exception("The domains are not disjoint.");
11079 for (int idom = 0; idom < nbDomains; idom++)
11082 // --- build a map (face to duplicate --> volume to modify)
11083 // with all the faces shared by 2 domains (group of elements)
11084 // and corresponding volume of this domain, for each shared face.
11085 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11087 //MESSAGE("... Neighbors of domain #" << idom);
11088 const TIDSortedElemSet& domain = theElems[idom];
11089 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11090 for (; elemItr != domain.end(); ++elemItr)
11092 const SMDS_MeshElement* anElem = *elemItr;
11095 int vtkId = anElem->GetVtkID();
11096 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11097 int neighborsVtkIds[NBMAXNEIGHBORS];
11098 int downIds[NBMAXNEIGHBORS];
11099 unsigned char downTypes[NBMAXNEIGHBORS];
11100 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11101 for (int n = 0; n < nbNeighbors; n++)
11103 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11104 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11105 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11108 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11110 // MESSAGE("Domain " << idombis);
11111 const TIDSortedElemSet& domainbis = theElems[idombis];
11112 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11114 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11116 DownIdType face(downIds[n], downTypes[n]);
11117 if (!faceDomains[face].count(idom))
11119 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11120 celldom[vtkId] = idom;
11121 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11125 theRestDomElems.insert( elem );
11126 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11127 celldom[neighborsVtkIds[n]] = iRestDom;
11135 //MESSAGE("Number of shared faces " << faceDomains.size());
11136 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11138 // --- explore the shared faces domain by domain,
11139 // explore the nodes of the face and see if they belong to a cell in the domain,
11140 // which has only a node or an edge on the border (not a shared face)
11142 for (int idomain = idom0; idomain < nbDomains; idomain++)
11144 //MESSAGE("Domain " << idomain);
11145 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11146 itface = faceDomains.begin();
11147 for (; itface != faceDomains.end(); ++itface)
11149 const std::map<int, int>& domvol = itface->second;
11150 if (!domvol.count(idomain))
11152 DownIdType face = itface->first;
11153 //MESSAGE(" --- face " << face.cellId);
11154 std::set<int> oldNodes;
11156 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11157 std::set<int>::iterator itn = oldNodes.begin();
11158 for (; itn != oldNodes.end(); ++itn)
11161 //MESSAGE(" node " << oldId);
11162 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11163 for (int i=0; i<l.ncells; i++)
11165 int vtkId = l.cells[i];
11166 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11167 if (!domain.count(anElem))
11169 int vtkType = grid->GetCellType(vtkId);
11170 int downId = grid->CellIdToDownId(vtkId);
11173 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11174 continue; // not OK at this stage of the algorithm:
11175 //no cells created after BuildDownWardConnectivity
11177 DownIdType aCell(downId, vtkType);
11178 cellDomains[aCell][idomain] = vtkId;
11179 celldom[vtkId] = idomain;
11180 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11186 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11187 // for each shared face, get the nodes
11188 // for each node, for each domain of the face, create a clone of the node
11190 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11191 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11192 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11194 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11195 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11196 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11198 //MESSAGE(".. Duplication of the nodes");
11199 for (int idomain = idom0; idomain < nbDomains; idomain++)
11201 itface = faceDomains.begin();
11202 for (; itface != faceDomains.end(); ++itface)
11204 const std::map<int, int>& domvol = itface->second;
11205 if (!domvol.count(idomain))
11207 DownIdType face = itface->first;
11208 //MESSAGE(" --- face " << face.cellId);
11209 std::set<int> oldNodes;
11211 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11212 std::set<int>::iterator itn = oldNodes.begin();
11213 for (; itn != oldNodes.end(); ++itn)
11216 if (nodeDomains[oldId].empty())
11218 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11219 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11221 std::map<int, int>::const_iterator itdom = domvol.begin();
11222 for (; itdom != domvol.end(); ++itdom)
11224 int idom = itdom->first;
11225 //MESSAGE(" domain " << idom);
11226 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11228 if (nodeDomains[oldId].size() >= 2) // a multiple node
11230 vector<int> orderedDoms;
11231 //MESSAGE("multiple node " << oldId);
11232 if (mutipleNodes.count(oldId))
11233 orderedDoms = mutipleNodes[oldId];
11236 map<int,int>::iterator it = nodeDomains[oldId].begin();
11237 for (; it != nodeDomains[oldId].end(); ++it)
11238 orderedDoms.push_back(it->first);
11240 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11241 //stringstream txt;
11242 //for (int i=0; i<orderedDoms.size(); i++)
11243 // txt << orderedDoms[i] << " ";
11244 //MESSAGE("orderedDoms " << txt.str());
11245 mutipleNodes[oldId] = orderedDoms;
11247 double *coords = grid->GetPoint(oldId);
11248 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11249 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11250 int newId = newNode->GetVtkID();
11251 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11252 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11259 //MESSAGE(".. Creation of elements");
11260 for (int idomain = idom0; idomain < nbDomains; idomain++)
11262 itface = faceDomains.begin();
11263 for (; itface != faceDomains.end(); ++itface)
11265 std::map<int, int> domvol = itface->second;
11266 if (!domvol.count(idomain))
11268 DownIdType face = itface->first;
11269 //MESSAGE(" --- face " << face.cellId);
11270 std::set<int> oldNodes;
11272 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11273 int nbMultipleNodes = 0;
11274 std::set<int>::iterator itn = oldNodes.begin();
11275 for (; itn != oldNodes.end(); ++itn)
11278 if (mutipleNodes.count(oldId))
11281 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11283 //MESSAGE("multiple Nodes detected on a shared face");
11284 int downId = itface->first.cellId;
11285 unsigned char cellType = itface->first.cellType;
11286 // --- shared edge or shared face ?
11287 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11290 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11291 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11292 if (mutipleNodes.count(nodes[i]))
11293 if (!mutipleNodesToFace.count(nodes[i]))
11294 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11296 else // shared face (between two volumes)
11298 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11299 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11300 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11301 for (int ie =0; ie < nbEdges; ie++)
11304 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11305 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11307 vector<int> vn0 = mutipleNodes[nodes[0]];
11308 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11310 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11311 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11312 if ( vn0[i0] == vn1[i1] )
11313 doms.push_back( vn0[ i0 ]);
11314 if ( doms.size() > 2 )
11316 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11317 double *coords = grid->GetPoint(nodes[0]);
11318 gp_Pnt p0(coords[0], coords[1], coords[2]);
11319 coords = grid->GetPoint(nodes[nbNodes - 1]);
11320 gp_Pnt p1(coords[0], coords[1], coords[2]);
11322 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11323 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11324 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11325 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11326 for ( size_t id = 0; id < doms.size(); id++ )
11328 int idom = doms[id];
11329 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11330 for ( int ivol = 0; ivol < nbvol; ivol++ )
11332 int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11333 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11334 if (domain.count(elem))
11336 const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11337 domvol[idom] = (SMDS_MeshVolume*) svol;
11338 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11339 double values[3] = { 0,0,0 };
11340 vtkIdType npts = 0;
11341 vtkIdType* pts = 0;
11342 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11343 for ( vtkIdType i = 0; i < npts; ++i )
11345 double *coords = grid->GetPoint( pts[i] );
11346 for ( int j = 0; j < 3; ++j )
11347 values[j] += coords[j] / npts;
11351 gref.SetCoord( values[0], values[1], values[2] );
11352 angleDom[idom] = 0;
11356 gp_Pnt g( values[0], values[1], values[2] );
11357 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11358 //MESSAGE(" angle=" << angleDom[idom]);
11364 map<double, int> sortedDom; // sort domains by angle
11365 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11366 sortedDom[ia->second] = ia->first;
11367 vector<int> vnodes;
11369 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11371 vdom.push_back(ib->second);
11372 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11374 for (int ino = 0; ino < nbNodes; ino++)
11375 vnodes.push_back(nodes[ino]);
11376 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11385 // --- iterate on shared faces (volumes to modify, face to extrude)
11386 // get node id's of the face (id SMDS = id VTK)
11387 // create flat element with old and new nodes if requested
11389 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11390 // (domain1 X domain2) = domain1 + MAXINT*domain2
11392 std::map<int, std::map<long,int> > nodeQuadDomains;
11393 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11395 //MESSAGE(".. Creation of elements: simple junction");
11396 if (createJointElems)
11398 string joints2DName = "joints2D";
11399 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11400 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11401 string joints3DName = "joints3D";
11402 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11403 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11405 itface = faceDomains.begin();
11406 for (; itface != faceDomains.end(); ++itface)
11408 DownIdType face = itface->first;
11409 std::set<int> oldNodes;
11410 std::set<int>::iterator itn;
11412 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11414 std::map<int, int> domvol = itface->second;
11415 std::map<int, int>::iterator itdom = domvol.begin();
11416 int dom1 = itdom->first;
11417 int vtkVolId = itdom->second;
11419 int dom2 = itdom->first;
11420 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11422 stringstream grpname;
11425 grpname << dom1 << "_" << dom2;
11427 grpname << dom2 << "_" << dom1;
11428 string namegrp = grpname.str();
11429 if (!mapOfJunctionGroups.count(namegrp))
11430 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11431 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11433 sgrp->Add(vol->GetID());
11434 if (vol->GetType() == SMDSAbs_Volume)
11435 joints3DGrp->Add(vol->GetID());
11436 else if (vol->GetType() == SMDSAbs_Face)
11437 joints2DGrp->Add(vol->GetID());
11441 // --- create volumes on multiple domain intersection if requested
11442 // iterate on mutipleNodesToFace
11443 // iterate on edgesMultiDomains
11445 //MESSAGE(".. Creation of elements: multiple junction");
11446 if (createJointElems)
11448 // --- iterate on mutipleNodesToFace
11450 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11451 for (; itn != mutipleNodesToFace.end(); ++itn)
11453 int node = itn->first;
11454 vector<int> orderDom = itn->second;
11455 vector<vtkIdType> orderedNodes;
11456 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11457 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11458 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11460 stringstream grpname;
11462 grpname << 0 << "_" << 0;
11463 string namegrp = grpname.str();
11464 if (!mapOfJunctionGroups.count(namegrp))
11465 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11466 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11468 sgrp->Add(face->GetID());
11471 // --- iterate on edgesMultiDomains
11473 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11474 for (; ite != edgesMultiDomains.end(); ++ite)
11476 vector<int> nodes = ite->first;
11477 vector<int> orderDom = ite->second;
11478 vector<vtkIdType> orderedNodes;
11479 if (nodes.size() == 2)
11481 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11482 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11483 if ( orderDom.size() == 3 )
11484 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11485 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11487 for (int idom = orderDom.size()-1; idom >=0; idom--)
11488 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11489 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11491 string namegrp = "jointsMultiples";
11492 if (!mapOfJunctionGroups.count(namegrp))
11493 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11494 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11496 sgrp->Add(vol->GetID());
11500 //INFOS("Quadratic multiple joints not implemented");
11501 // TODO quadratic nodes
11506 // --- list the explicit faces and edges of the mesh that need to be modified,
11507 // i.e. faces and edges built with one or more duplicated nodes.
11508 // associate these faces or edges to their corresponding domain.
11509 // only the first domain found is kept when a face or edge is shared
11511 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11512 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11513 faceOrEdgeDom.clear();
11516 //MESSAGE(".. Modification of elements");
11517 for (int idomain = idom0; idomain < nbDomains; idomain++)
11519 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11520 for (; itnod != nodeDomains.end(); ++itnod)
11522 int oldId = itnod->first;
11523 //MESSAGE(" node " << oldId);
11524 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11525 for (int i = 0; i < l.ncells; i++)
11527 int vtkId = l.cells[i];
11528 int vtkType = grid->GetCellType(vtkId);
11529 int downId = grid->CellIdToDownId(vtkId);
11531 continue; // new cells: not to be modified
11532 DownIdType aCell(downId, vtkType);
11533 int volParents[1000];
11534 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11535 for (int j = 0; j < nbvol; j++)
11536 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11537 if (!feDom.count(vtkId))
11539 feDom[vtkId] = idomain;
11540 faceOrEdgeDom[aCell] = emptyMap;
11541 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11542 //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11543 // << " type " << vtkType << " downId " << downId);
11549 // --- iterate on shared faces (volumes to modify, face to extrude)
11550 // get node id's of the face
11551 // replace old nodes by new nodes in volumes, and update inverse connectivity
11553 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11554 for (int m=0; m<3; m++)
11556 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11557 itface = (*amap).begin();
11558 for (; itface != (*amap).end(); ++itface)
11560 DownIdType face = itface->first;
11561 std::set<int> oldNodes;
11562 std::set<int>::iterator itn;
11564 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11565 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11566 std::map<int, int> localClonedNodeIds;
11568 std::map<int, int> domvol = itface->second;
11569 std::map<int, int>::iterator itdom = domvol.begin();
11570 for (; itdom != domvol.end(); ++itdom)
11572 int idom = itdom->first;
11573 int vtkVolId = itdom->second;
11574 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11575 localClonedNodeIds.clear();
11576 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11579 if (nodeDomains[oldId].count(idom))
11581 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11582 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11585 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11590 // Remove empty groups (issue 0022812)
11591 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11592 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11594 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11595 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11598 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11599 grid->DeleteLinks();
11607 * \brief Double nodes on some external faces and create flat elements.
11608 * Flat elements are mainly used by some types of mechanic calculations.
11610 * Each group of the list must be constituted of faces.
11611 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11612 * @param theElems - list of groups of faces, where a group of faces is a set of
11613 * SMDS_MeshElements sorted by Id.
11614 * @return TRUE if operation has been completed successfully, FALSE otherwise
11616 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11618 // MESSAGE("-------------------------------------------------");
11619 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11620 // MESSAGE("-------------------------------------------------");
11622 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11624 // --- For each group of faces
11625 // duplicate the nodes, create a flat element based on the face
11626 // replace the nodes of the faces by their clones
11628 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11629 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11630 clonedNodes.clear();
11631 intermediateNodes.clear();
11632 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11633 mapOfJunctionGroups.clear();
11635 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11637 const TIDSortedElemSet& domain = theElems[idom];
11638 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11639 for ( ; elemItr != domain.end(); ++elemItr )
11641 const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11644 // MESSAGE("aFace=" << aFace->GetID());
11645 bool isQuad = aFace->IsQuadratic();
11646 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11648 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11650 SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11651 while (nodeIt->more())
11653 const SMDS_MeshNode* node = nodeIt->next();
11654 bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11656 ln2.push_back(node);
11658 ln0.push_back(node);
11660 const SMDS_MeshNode* clone = 0;
11661 if (!clonedNodes.count(node))
11663 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11664 copyPosition( node, clone );
11665 clonedNodes[node] = clone;
11668 clone = clonedNodes[node];
11671 ln3.push_back(clone);
11673 ln1.push_back(clone);
11675 const SMDS_MeshNode* inter = 0;
11676 if (isQuad && (!isMedium))
11678 if (!intermediateNodes.count(node))
11680 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11681 copyPosition( node, inter );
11682 intermediateNodes[node] = inter;
11685 inter = intermediateNodes[node];
11686 ln4.push_back(inter);
11690 // --- extrude the face
11692 vector<const SMDS_MeshNode*> ln;
11693 SMDS_MeshVolume* vol = 0;
11694 vtkIdType aType = aFace->GetVtkType();
11698 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11699 // MESSAGE("vol prism " << vol->GetID());
11700 ln.push_back(ln1[0]);
11701 ln.push_back(ln1[1]);
11702 ln.push_back(ln1[2]);
11705 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11706 // MESSAGE("vol hexa " << vol->GetID());
11707 ln.push_back(ln1[0]);
11708 ln.push_back(ln1[1]);
11709 ln.push_back(ln1[2]);
11710 ln.push_back(ln1[3]);
11712 case VTK_QUADRATIC_TRIANGLE:
11713 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11714 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11715 // MESSAGE("vol quad prism " << vol->GetID());
11716 ln.push_back(ln1[0]);
11717 ln.push_back(ln1[1]);
11718 ln.push_back(ln1[2]);
11719 ln.push_back(ln3[0]);
11720 ln.push_back(ln3[1]);
11721 ln.push_back(ln3[2]);
11723 case VTK_QUADRATIC_QUAD:
11724 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11725 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11726 // ln4[0], ln4[1], ln4[2], ln4[3]);
11727 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11728 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11729 ln4[0], ln4[1], ln4[2], ln4[3]);
11730 // MESSAGE("vol quad hexa " << vol->GetID());
11731 ln.push_back(ln1[0]);
11732 ln.push_back(ln1[1]);
11733 ln.push_back(ln1[2]);
11734 ln.push_back(ln1[3]);
11735 ln.push_back(ln3[0]);
11736 ln.push_back(ln3[1]);
11737 ln.push_back(ln3[2]);
11738 ln.push_back(ln3[3]);
11748 stringstream grpname;
11751 string namegrp = grpname.str();
11752 if (!mapOfJunctionGroups.count(namegrp))
11753 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11754 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11756 sgrp->Add(vol->GetID());
11759 // --- modify the face
11761 const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11768 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11769 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11770 * groups of faces to remove inside the object, (idem edges).
11771 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11773 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11774 const TopoDS_Shape& theShape,
11775 SMESH_NodeSearcher* theNodeSearcher,
11776 const char* groupName,
11777 std::vector<double>& nodesCoords,
11778 std::vector<std::vector<int> >& listOfListOfNodes)
11780 // MESSAGE("--------------------------------");
11781 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11782 // MESSAGE("--------------------------------");
11784 // --- zone of volumes to remove is given :
11785 // 1 either by a geom shape (one or more vertices) and a radius,
11786 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11787 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11788 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11789 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11790 // defined by it's name.
11792 SMESHDS_GroupBase* groupDS = 0;
11793 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11794 while ( groupIt->more() )
11797 SMESH_Group * group = groupIt->next();
11798 if ( !group ) continue;
11799 groupDS = group->GetGroupDS();
11800 if ( !groupDS || groupDS->IsEmpty() ) continue;
11801 std::string grpName = group->GetName();
11802 //MESSAGE("grpName=" << grpName);
11803 if (grpName == groupName)
11809 bool isNodeGroup = false;
11810 bool isNodeCoords = false;
11813 if (groupDS->GetType() != SMDSAbs_Node)
11815 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11818 if (nodesCoords.size() > 0)
11819 isNodeCoords = true; // a list o nodes given by their coordinates
11820 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11822 // --- define groups to build
11824 // --- group of SMDS volumes
11825 string grpvName = groupName;
11826 grpvName += "_vol";
11827 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11830 MESSAGE("group not created " << grpvName);
11833 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11835 // --- group of SMDS faces on the skin
11836 string grpsName = groupName;
11837 grpsName += "_skin";
11838 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11841 MESSAGE("group not created " << grpsName);
11844 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11846 // --- group of SMDS faces internal (several shapes)
11847 string grpiName = groupName;
11848 grpiName += "_internalFaces";
11849 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11852 MESSAGE("group not created " << grpiName);
11855 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11857 // --- group of SMDS faces internal (several shapes)
11858 string grpeiName = groupName;
11859 grpeiName += "_internalEdges";
11860 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11863 MESSAGE("group not created " << grpeiName);
11866 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11868 // --- build downward connectivity
11870 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11871 meshDS->BuildDownWardConnectivity(true);
11872 SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11874 // --- set of volumes detected inside
11876 std::set<int> setOfInsideVol;
11877 std::set<int> setOfVolToCheck;
11879 std::vector<gp_Pnt> gpnts;
11882 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11884 //MESSAGE("group of nodes provided");
11885 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11886 while ( elemIt->more() )
11888 const SMDS_MeshElement* elem = elemIt->next();
11891 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11894 SMDS_MeshElement* vol = 0;
11895 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11896 while (volItr->more())
11898 vol = (SMDS_MeshElement*)volItr->next();
11899 setOfInsideVol.insert(vol->GetVtkID());
11900 sgrp->Add(vol->GetID());
11904 else if (isNodeCoords)
11906 //MESSAGE("list of nodes coordinates provided");
11909 while ( i < nodesCoords.size()-2 )
11911 double x = nodesCoords[i++];
11912 double y = nodesCoords[i++];
11913 double z = nodesCoords[i++];
11914 gp_Pnt p = gp_Pnt(x, y ,z);
11915 gpnts.push_back(p);
11916 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11920 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11922 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11923 TopTools_IndexedMapOfShape vertexMap;
11924 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11925 gp_Pnt p = gp_Pnt(0,0,0);
11926 if (vertexMap.Extent() < 1)
11929 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11931 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11932 p = BRep_Tool::Pnt(vertex);
11933 gpnts.push_back(p);
11934 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11938 if (gpnts.size() > 0)
11940 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11941 //MESSAGE("startNode->nodeId " << nodeId);
11943 double radius2 = radius*radius;
11944 //MESSAGE("radius2 " << radius2);
11946 // --- volumes on start node
11948 setOfVolToCheck.clear();
11949 SMDS_MeshElement* startVol = 0;
11950 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11951 while (volItr->more())
11953 startVol = (SMDS_MeshElement*)volItr->next();
11954 setOfVolToCheck.insert(startVol->GetVtkID());
11956 if (setOfVolToCheck.empty())
11958 MESSAGE("No volumes found");
11962 // --- starting with central volumes then their neighbors, check if they are inside
11963 // or outside the domain, until no more new neighbor volume is inside.
11964 // Fill the group of inside volumes
11966 std::map<int, double> mapOfNodeDistance2;
11967 mapOfNodeDistance2.clear();
11968 std::set<int> setOfOutsideVol;
11969 while (!setOfVolToCheck.empty())
11971 std::set<int>::iterator it = setOfVolToCheck.begin();
11973 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
11974 bool volInside = false;
11975 vtkIdType npts = 0;
11976 vtkIdType* pts = 0;
11977 grid->GetCellPoints(vtkId, npts, pts);
11978 for (int i=0; i<npts; i++)
11980 double distance2 = 0;
11981 if (mapOfNodeDistance2.count(pts[i]))
11983 distance2 = mapOfNodeDistance2[pts[i]];
11984 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
11988 double *coords = grid->GetPoint(pts[i]);
11989 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11991 for ( size_t j = 0; j < gpnts.size(); j++ )
11993 double d2 = aPoint.SquareDistance( gpnts[ j ]);
11994 if (d2 < distance2)
11997 if (distance2 < radius2)
12001 mapOfNodeDistance2[pts[i]] = distance2;
12002 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12004 if (distance2 < radius2)
12006 volInside = true; // one or more nodes inside the domain
12007 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12013 setOfInsideVol.insert(vtkId);
12014 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12015 int neighborsVtkIds[NBMAXNEIGHBORS];
12016 int downIds[NBMAXNEIGHBORS];
12017 unsigned char downTypes[NBMAXNEIGHBORS];
12018 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12019 for (int n = 0; n < nbNeighbors; n++)
12020 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12021 setOfVolToCheck.insert(neighborsVtkIds[n]);
12025 setOfOutsideVol.insert(vtkId);
12026 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12028 setOfVolToCheck.erase(vtkId);
12032 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12033 // If yes, add the volume to the inside set
12035 bool addedInside = true;
12036 std::set<int> setOfVolToReCheck;
12037 while (addedInside)
12039 //MESSAGE(" --------------------------- re check");
12040 addedInside = false;
12041 std::set<int>::iterator itv = setOfInsideVol.begin();
12042 for (; itv != setOfInsideVol.end(); ++itv)
12045 int neighborsVtkIds[NBMAXNEIGHBORS];
12046 int downIds[NBMAXNEIGHBORS];
12047 unsigned char downTypes[NBMAXNEIGHBORS];
12048 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12049 for (int n = 0; n < nbNeighbors; n++)
12050 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12051 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12053 setOfVolToCheck = setOfVolToReCheck;
12054 setOfVolToReCheck.clear();
12055 while (!setOfVolToCheck.empty())
12057 std::set<int>::iterator it = setOfVolToCheck.begin();
12059 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12061 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12062 int countInside = 0;
12063 int neighborsVtkIds[NBMAXNEIGHBORS];
12064 int downIds[NBMAXNEIGHBORS];
12065 unsigned char downTypes[NBMAXNEIGHBORS];
12066 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12067 for (int n = 0; n < nbNeighbors; n++)
12068 if (setOfInsideVol.count(neighborsVtkIds[n]))
12070 //MESSAGE("countInside " << countInside);
12071 if (countInside > 1)
12073 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12074 setOfInsideVol.insert(vtkId);
12075 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12076 addedInside = true;
12079 setOfVolToReCheck.insert(vtkId);
12081 setOfVolToCheck.erase(vtkId);
12085 // --- map of Downward faces at the boundary, inside the global volume
12086 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12087 // fill group of SMDS faces inside the volume (when several volume shapes)
12088 // fill group of SMDS faces on the skin of the global volume (if skin)
12090 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12091 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12092 std::set<int>::iterator it = setOfInsideVol.begin();
12093 for (; it != setOfInsideVol.end(); ++it)
12096 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12097 int neighborsVtkIds[NBMAXNEIGHBORS];
12098 int downIds[NBMAXNEIGHBORS];
12099 unsigned char downTypes[NBMAXNEIGHBORS];
12100 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12101 for (int n = 0; n < nbNeighbors; n++)
12103 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12104 if (neighborDim == 3)
12106 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12108 DownIdType face(downIds[n], downTypes[n]);
12109 boundaryFaces[face] = vtkId;
12111 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12112 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12113 if (vtkFaceId >= 0)
12115 sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12116 // find also the smds edges on this face
12117 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12118 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12119 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12120 for (int i = 0; i < nbEdges; i++)
12122 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12123 if (vtkEdgeId >= 0)
12124 sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12128 else if (neighborDim == 2) // skin of the volume
12130 DownIdType face(downIds[n], downTypes[n]);
12131 skinFaces[face] = vtkId;
12132 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12133 if (vtkFaceId >= 0)
12134 sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12139 // --- identify the edges constituting the wire of each subshape on the skin
12140 // define polylines with the nodes of edges, equivalent to wires
12141 // project polylines on subshapes, and partition, to get geom faces
12143 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12144 std::set<int> emptySet;
12146 std::set<int> shapeIds;
12148 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12149 while (itelem->more())
12151 const SMDS_MeshElement *elem = itelem->next();
12152 int shapeId = elem->getshapeId();
12153 int vtkId = elem->GetVtkID();
12154 if (!shapeIdToVtkIdSet.count(shapeId))
12156 shapeIdToVtkIdSet[shapeId] = emptySet;
12157 shapeIds.insert(shapeId);
12159 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12162 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12163 std::set<DownIdType, DownIdCompare> emptyEdges;
12164 emptyEdges.clear();
12166 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12167 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12169 int shapeId = itShape->first;
12170 //MESSAGE(" --- Shape ID --- "<< shapeId);
12171 shapeIdToEdges[shapeId] = emptyEdges;
12173 std::vector<int> nodesEdges;
12175 std::set<int>::iterator its = itShape->second.begin();
12176 for (; its != itShape->second.end(); ++its)
12179 //MESSAGE(" " << vtkId);
12180 int neighborsVtkIds[NBMAXNEIGHBORS];
12181 int downIds[NBMAXNEIGHBORS];
12182 unsigned char downTypes[NBMAXNEIGHBORS];
12183 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12184 for (int n = 0; n < nbNeighbors; n++)
12186 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12188 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12189 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12190 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12192 DownIdType edge(downIds[n], downTypes[n]);
12193 if (!shapeIdToEdges[shapeId].count(edge))
12195 shapeIdToEdges[shapeId].insert(edge);
12197 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12198 nodesEdges.push_back(vtkNodeId[0]);
12199 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12200 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12206 std::list<int> order;
12208 if (nodesEdges.size() > 0)
12210 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12211 nodesEdges[0] = -1;
12212 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12213 nodesEdges[1] = -1; // do not reuse this edge
12217 int nodeTofind = order.back(); // try first to push back
12219 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12220 if (nodesEdges[i] == nodeTofind)
12222 if ( i == (int) nodesEdges.size() )
12223 found = false; // no follower found on back
12226 if (i%2) // odd ==> use the previous one
12227 if (nodesEdges[i-1] < 0)
12231 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12232 nodesEdges[i-1] = -1;
12234 else // even ==> use the next one
12235 if (nodesEdges[i+1] < 0)
12239 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12240 nodesEdges[i+1] = -1;
12245 // try to push front
12247 nodeTofind = order.front(); // try to push front
12248 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12249 if ( nodesEdges[i] == nodeTofind )
12251 if ( i == (int)nodesEdges.size() )
12253 found = false; // no predecessor found on front
12256 if (i%2) // odd ==> use the previous one
12257 if (nodesEdges[i-1] < 0)
12261 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12262 nodesEdges[i-1] = -1;
12264 else // even ==> use the next one
12265 if (nodesEdges[i+1] < 0)
12269 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12270 nodesEdges[i+1] = -1;
12276 std::vector<int> nodes;
12277 nodes.push_back(shapeId);
12278 std::list<int>::iterator itl = order.begin();
12279 for (; itl != order.end(); itl++)
12281 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12282 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12284 listOfListOfNodes.push_back(nodes);
12287 // partition geom faces with blocFissure
12288 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12289 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12295 //================================================================================
12297 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12298 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12299 * \return TRUE if operation has been completed successfully, FALSE otherwise
12301 //================================================================================
12303 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12305 // iterates on volume elements and detect all free faces on them
12306 SMESHDS_Mesh* aMesh = GetMeshDS();
12310 ElemFeatures faceType( SMDSAbs_Face );
12311 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12312 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12315 const SMDS_MeshVolume* volume = vIt->next();
12316 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12317 vTool.SetExternalNormal();
12318 const int iQuad = volume->IsQuadratic();
12319 faceType.SetQuad( iQuad );
12320 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12322 if (!vTool.IsFreeFace(iface))
12325 vector<const SMDS_MeshNode *> nodes;
12326 int nbFaceNodes = vTool.NbFaceNodes(iface);
12327 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12329 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12330 nodes.push_back(faceNodes[inode]);
12332 if (iQuad) // add medium nodes
12334 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12335 nodes.push_back(faceNodes[inode]);
12336 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12337 nodes.push_back(faceNodes[8]);
12339 // add new face based on volume nodes
12340 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12342 nbExisted++; // face already exists
12346 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12351 return ( nbFree == ( nbExisted + nbCreated ));
12356 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12358 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12360 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12363 //================================================================================
12365 * \brief Creates missing boundary elements
12366 * \param elements - elements whose boundary is to be checked
12367 * \param dimension - defines type of boundary elements to create
12368 * \param group - a group to store created boundary elements in
12369 * \param targetMesh - a mesh to store created boundary elements in
12370 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12371 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12372 * boundary elements will be copied into the targetMesh
12373 * \param toAddExistingBondary - if true, not only new but also pre-existing
12374 * boundary elements will be added into the new group
12375 * \param aroundElements - if true, elements will be created on boundary of given
12376 * elements else, on boundary of the whole mesh.
12377 * \return nb of added boundary elements
12379 //================================================================================
12381 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12382 Bnd_Dimension dimension,
12383 SMESH_Group* group/*=0*/,
12384 SMESH_Mesh* targetMesh/*=0*/,
12385 bool toCopyElements/*=false*/,
12386 bool toCopyExistingBoundary/*=false*/,
12387 bool toAddExistingBondary/*= false*/,
12388 bool aroundElements/*= false*/)
12390 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12391 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12392 // hope that all elements are of the same type, do not check them all
12393 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12394 throw SALOME_Exception(LOCALIZED("wrong element type"));
12397 toCopyElements = toCopyExistingBoundary = false;
12399 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12400 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12401 int nbAddedBnd = 0;
12403 // editor adding present bnd elements and optionally holding elements to add to the group
12404 SMESH_MeshEditor* presentEditor;
12405 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12406 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12408 SMESH_MesherHelper helper( *myMesh );
12409 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12410 SMDS_VolumeTool vTool;
12411 TIDSortedElemSet avoidSet;
12412 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12415 typedef vector<const SMDS_MeshNode*> TConnectivity;
12416 TConnectivity tgtNodes;
12417 ElemFeatures elemKind( missType ), elemToCopy;
12419 vector<const SMDS_MeshElement*> presentBndElems;
12420 vector<TConnectivity> missingBndElems;
12421 vector<int> freeFacets;
12422 TConnectivity nodes, elemNodes;
12424 SMDS_ElemIteratorPtr eIt;
12425 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12426 else eIt = SMESHUtils::elemSetIterator( elements );
12428 while ( eIt->more() )
12430 const SMDS_MeshElement* elem = eIt->next();
12431 const int iQuad = elem->IsQuadratic();
12432 elemKind.SetQuad( iQuad );
12434 // ------------------------------------------------------------------------------------
12435 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12436 // ------------------------------------------------------------------------------------
12437 presentBndElems.clear();
12438 missingBndElems.clear();
12439 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12440 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12442 const SMDS_MeshElement* otherVol = 0;
12443 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12445 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12446 ( !aroundElements || elements.count( otherVol )))
12448 freeFacets.push_back( iface );
12450 if ( missType == SMDSAbs_Face )
12451 vTool.SetExternalNormal();
12452 for ( size_t i = 0; i < freeFacets.size(); ++i )
12454 int iface = freeFacets[i];
12455 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12456 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12457 if ( missType == SMDSAbs_Edge ) // boundary edges
12459 nodes.resize( 2+iQuad );
12460 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12462 for ( size_t j = 0; j < nodes.size(); ++j )
12463 nodes[ j ] = nn[ i+j ];
12464 if ( const SMDS_MeshElement* edge =
12465 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12466 presentBndElems.push_back( edge );
12468 missingBndElems.push_back( nodes );
12471 else // boundary face
12474 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12475 nodes.push_back( nn[inode] ); // add corner nodes
12477 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12478 nodes.push_back( nn[inode] ); // add medium nodes
12479 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12481 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12483 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12484 SMDSAbs_Face, /*noMedium=*/false ))
12485 presentBndElems.push_back( f );
12487 missingBndElems.push_back( nodes );
12489 if ( targetMesh != myMesh )
12491 // add 1D elements on face boundary to be added to a new mesh
12492 const SMDS_MeshElement* edge;
12493 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12496 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12498 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12499 if ( edge && avoidSet.insert( edge ).second )
12500 presentBndElems.push_back( edge );
12506 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12508 avoidSet.clear(), avoidSet.insert( elem );
12509 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12510 SMDS_MeshElement::iterator() );
12511 elemNodes.push_back( elemNodes[0] );
12512 nodes.resize( 2 + iQuad );
12513 const int nbLinks = elem->NbCornerNodes();
12514 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12516 nodes[0] = elemNodes[iN];
12517 nodes[1] = elemNodes[iN+1+iQuad];
12518 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12519 continue; // not free link
12521 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12522 if ( const SMDS_MeshElement* edge =
12523 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12524 presentBndElems.push_back( edge );
12526 missingBndElems.push_back( nodes );
12530 // ---------------------------------
12531 // 2. Add missing boundary elements
12532 // ---------------------------------
12533 if ( targetMesh != myMesh )
12534 // instead of making a map of nodes in this mesh and targetMesh,
12535 // we create nodes with same IDs.
12536 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12538 TConnectivity& srcNodes = missingBndElems[i];
12539 tgtNodes.resize( srcNodes.size() );
12540 for ( inode = 0; inode < srcNodes.size(); ++inode )
12541 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12542 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12544 /*noMedium=*/false))
12546 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12550 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12552 TConnectivity& nodes = missingBndElems[ i ];
12553 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12555 /*noMedium=*/false))
12557 SMDS_MeshElement* newElem =
12558 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12559 nbAddedBnd += bool( newElem );
12561 // try to set a new element to a shape
12562 if ( myMesh->HasShapeToMesh() )
12565 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12566 const size_t nbN = nodes.size() / (iQuad+1 );
12567 for ( inode = 0; inode < nbN && ok; ++inode )
12569 pair<int, TopAbs_ShapeEnum> i_stype =
12570 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12571 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12572 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12574 if ( ok && mediumShapes.size() > 1 )
12576 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12577 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12578 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12580 if (( ok = ( stype_i->first != stype_i_0.first )))
12581 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12582 aMesh->IndexToShape( stype_i_0.second ));
12585 if ( ok && mediumShapes.begin()->first == missShapeType )
12586 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12590 // ----------------------------------
12591 // 3. Copy present boundary elements
12592 // ----------------------------------
12593 if ( toCopyExistingBoundary )
12594 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12596 const SMDS_MeshElement* e = presentBndElems[i];
12597 tgtNodes.resize( e->NbNodes() );
12598 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12599 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12600 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12602 else // store present elements to add them to a group
12603 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12605 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12608 } // loop on given elements
12610 // ---------------------------------------------
12611 // 4. Fill group with boundary elements
12612 // ---------------------------------------------
12615 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12616 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12617 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12619 tgtEditor.myLastCreatedElems.clear();
12620 tgtEditor2.myLastCreatedElems.clear();
12622 // -----------------------
12623 // 5. Copy given elements
12624 // -----------------------
12625 if ( toCopyElements && targetMesh != myMesh )
12627 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12628 else eIt = SMESHUtils::elemSetIterator( elements );
12629 while (eIt->more())
12631 const SMDS_MeshElement* elem = eIt->next();
12632 tgtNodes.resize( elem->NbNodes() );
12633 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12634 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12635 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12637 tgtEditor.myLastCreatedElems.clear();
12643 //================================================================================
12645 * \brief Copy node position and set \a to node on the same geometry
12647 //================================================================================
12649 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12650 const SMDS_MeshNode* to )
12652 if ( !from || !to ) return;
12654 SMDS_PositionPtr pos = from->GetPosition();
12655 if ( !pos || from->getshapeId() < 1 ) return;
12657 switch ( pos->GetTypeOfPosition() )
12659 case SMDS_TOP_3DSPACE: break;
12661 case SMDS_TOP_FACE:
12663 SMDS_FacePositionPtr fPos = pos;
12664 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12665 fPos->GetUParameter(), fPos->GetVParameter() );
12668 case SMDS_TOP_EDGE:
12670 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12671 SMDS_EdgePositionPtr ePos = pos;
12672 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12675 case SMDS_TOP_VERTEX:
12677 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12680 case SMDS_TOP_UNSPEC: