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 SMDS_VolumeTool vTool( aPolyedre );
1097 const int nbFaces = vTool.NbFaces();
1098 vector<int> quantities( nbFaces );
1099 vector<const SMDS_MeshNode *> poly_nodes;
1101 // check if all facets are oriented equally
1102 bool sameOri = true;
1103 vector<int>& facetOri = quantities; // keep orientation in quantities so far
1104 for (int iface = 0; iface < nbFaces; iface++)
1106 facetOri[ iface ] = vTool.IsFaceExternal( iface );
1107 if ( facetOri[ iface ] != facetOri[ 0 ])
1111 // reverse faces of the polyhedron
1112 int neededOri = sameOri ? 1 - facetOri[0] : 1;
1113 poly_nodes.reserve( vTool.NbNodes() );
1114 for ( int iface = 0; iface < nbFaces; iface++ )
1116 int nbFaceNodes = vTool.NbFaceNodes( iface );
1117 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( iface );
1118 bool toReverse = ( facetOri[ iface ] != neededOri );
1120 quantities[ iface ] = nbFaceNodes;
1123 for ( int inode = nbFaceNodes - 1; inode >= 0; inode-- )
1124 poly_nodes.push_back( nodes[ inode ]);
1126 poly_nodes.insert( poly_nodes.end(), nodes, nodes + nbFaceNodes );
1128 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1130 else // other elements
1132 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1133 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1134 if ( interlace.empty() )
1136 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1140 SMDS_MeshCell::applyInterlace( interlace, nodes );
1142 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1147 //================================================================================
1149 * \brief Reorient faces.
1150 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1151 * \param theDirection - desired direction of normal of \a theFace
1152 * \param theFace - one of \a theFaces that should be oriented according to
1153 * \a theDirection and whose orientation defines orientation of other faces
1154 * \return number of reoriented faces.
1156 //================================================================================
1158 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1159 const gp_Dir& theDirection,
1160 const SMDS_MeshElement * theFace)
1163 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1165 if ( theFaces.empty() )
1167 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
1168 while ( fIt->more() )
1169 theFaces.insert( theFaces.end(), fIt->next() );
1172 // orient theFace according to theDirection
1174 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1175 if ( normal * theDirection.XYZ() < 0 )
1176 nbReori += Reorient( theFace );
1178 // Orient other faces
1180 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1181 TIDSortedElemSet avoidSet;
1182 set< SMESH_TLink > checkedLinks;
1183 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1185 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1186 theFaces.erase( theFace );
1187 startFaces.insert( theFace );
1189 int nodeInd1, nodeInd2;
1190 const SMDS_MeshElement* otherFace;
1191 vector< const SMDS_MeshElement* > facesNearLink;
1192 vector< std::pair< int, int > > nodeIndsOfFace;
1194 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1195 while ( !startFaces.empty() )
1197 startFace = startFaces.begin();
1198 theFace = *startFace;
1199 startFaces.erase( startFace );
1200 if ( !visitedFaces.insert( theFace ).second )
1204 avoidSet.insert(theFace);
1206 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1208 const int nbNodes = theFace->NbCornerNodes();
1209 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1211 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1212 linkIt_isNew = checkedLinks.insert( link );
1213 if ( !linkIt_isNew.second )
1215 // link has already been checked and won't be encountered more
1216 // if the group (theFaces) is manifold
1217 //checkedLinks.erase( linkIt_isNew.first );
1221 facesNearLink.clear();
1222 nodeIndsOfFace.clear();
1223 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1225 &nodeInd1, &nodeInd2 )))
1226 if ( otherFace != theFace)
1228 facesNearLink.push_back( otherFace );
1229 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1230 avoidSet.insert( otherFace );
1232 if ( facesNearLink.size() > 1 )
1234 // NON-MANIFOLD mesh shell !
1235 // select a face most co-directed with theFace,
1236 // other faces won't be visited this time
1238 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1239 double proj, maxProj = -1;
1240 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1241 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1242 if (( proj = Abs( NF * NOF )) > maxProj ) {
1244 otherFace = facesNearLink[i];
1245 nodeInd1 = nodeIndsOfFace[i].first;
1246 nodeInd2 = nodeIndsOfFace[i].second;
1249 // not to visit rejected faces
1250 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1251 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1252 visitedFaces.insert( facesNearLink[i] );
1254 else if ( facesNearLink.size() == 1 )
1256 otherFace = facesNearLink[0];
1257 nodeInd1 = nodeIndsOfFace.back().first;
1258 nodeInd2 = nodeIndsOfFace.back().second;
1260 if ( otherFace && otherFace != theFace)
1262 // link must be reverse in otherFace if orientation to otherFace
1263 // is same as that of theFace
1264 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1266 nbReori += Reorient( otherFace );
1268 startFaces.insert( otherFace );
1271 std::swap( link.first, link.second ); // reverse the link
1277 //================================================================================
1279 * \brief Reorient faces basing on orientation of adjacent volumes.
1280 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1281 * \param theVolumes - reference volumes.
1282 * \param theOutsideNormal - to orient faces to have their normal
1283 * pointing either \a outside or \a inside the adjacent volumes.
1284 * \return number of reoriented faces.
1286 //================================================================================
1288 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1289 TIDSortedElemSet & theVolumes,
1290 const bool theOutsideNormal)
1294 SMDS_ElemIteratorPtr faceIt;
1295 if ( theFaces.empty() )
1296 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1298 faceIt = SMESHUtils::elemSetIterator( theFaces );
1300 vector< const SMDS_MeshNode* > faceNodes;
1301 TIDSortedElemSet checkedVolumes;
1302 set< const SMDS_MeshNode* > faceNodesSet;
1303 SMDS_VolumeTool volumeTool;
1305 while ( faceIt->more() ) // loop on given faces
1307 const SMDS_MeshElement* face = faceIt->next();
1308 if ( face->GetType() != SMDSAbs_Face )
1311 const size_t nbCornersNodes = face->NbCornerNodes();
1312 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1314 checkedVolumes.clear();
1315 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1316 while ( vIt->more() )
1318 const SMDS_MeshElement* volume = vIt->next();
1320 if ( !checkedVolumes.insert( volume ).second )
1322 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1325 // is volume adjacent?
1326 bool allNodesCommon = true;
1327 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1328 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1329 if ( !allNodesCommon )
1332 // get nodes of a corresponding volume facet
1333 faceNodesSet.clear();
1334 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1335 volumeTool.Set( volume );
1336 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1337 if ( facetID < 0 ) continue;
1338 volumeTool.SetExternalNormal();
1339 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1341 // compare order of faceNodes and facetNodes
1342 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1344 for ( int i = 0; i < 2; ++i )
1346 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1347 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1348 if ( faceNodes[ iN ] == n )
1354 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1355 if ( isOutside != theOutsideNormal )
1356 nbReori += Reorient( face );
1358 } // loop on given faces
1363 //=======================================================================
1364 //function : getBadRate
1366 //=======================================================================
1368 static double getBadRate (const SMDS_MeshElement* theElem,
1369 SMESH::Controls::NumericalFunctorPtr& theCrit)
1371 SMESH::Controls::TSequenceOfXYZ P;
1372 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1374 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1375 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1378 //=======================================================================
1379 //function : QuadToTri
1380 //purpose : Cut quadrangles into triangles.
1381 // theCrit is used to select a diagonal to cut
1382 //=======================================================================
1384 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1385 SMESH::Controls::NumericalFunctorPtr theCrit)
1389 if ( !theCrit.get() )
1392 SMESHDS_Mesh * aMesh = GetMeshDS();
1393 Handle(Geom_Surface) surface;
1394 SMESH_MesherHelper helper( *GetMesh() );
1396 myLastCreatedElems.reserve( theElems.size() * 2 );
1398 TIDSortedElemSet::iterator itElem;
1399 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1401 const SMDS_MeshElement* elem = *itElem;
1402 if ( !elem || elem->GetType() != SMDSAbs_Face )
1404 if ( elem->NbCornerNodes() != 4 )
1407 // retrieve element nodes
1408 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1410 // compare two sets of possible triangles
1411 double aBadRate1, aBadRate2; // to what extent a set is bad
1412 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1413 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1414 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1416 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1417 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1418 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1420 const int aShapeId = FindShape( elem );
1421 const SMDS_MeshElement* newElem1 = 0;
1422 const SMDS_MeshElement* newElem2 = 0;
1424 if ( !elem->IsQuadratic() ) // split linear quadrangle
1426 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1427 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1428 if ( aBadRate1 <= aBadRate2 ) {
1429 // tr1 + tr2 is better
1430 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1431 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1434 // tr3 + tr4 is better
1435 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1436 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1439 else // split quadratic quadrangle
1441 helper.SetIsQuadratic( true );
1442 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1444 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1445 if ( aNodes.size() == 9 )
1447 helper.SetIsBiQuadratic( true );
1448 if ( aBadRate1 <= aBadRate2 )
1449 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1451 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1453 // create a new element
1454 if ( aBadRate1 <= aBadRate2 ) {
1455 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1456 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1459 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1460 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1464 // care of a new element
1466 myLastCreatedElems.push_back(newElem1);
1467 myLastCreatedElems.push_back(newElem2);
1468 AddToSameGroups( newElem1, elem, aMesh );
1469 AddToSameGroups( newElem2, elem, aMesh );
1471 // put a new triangle on the same shape
1473 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1474 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1476 aMesh->RemoveElement( elem );
1481 //=======================================================================
1483 * \brief Split each of given quadrangles into 4 triangles.
1484 * \param theElems - The faces to be split. If empty all faces are split.
1486 //=======================================================================
1488 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1491 myLastCreatedElems.reserve( theElems.size() * 4 );
1493 SMESH_MesherHelper helper( *GetMesh() );
1494 helper.SetElementsOnShape( true );
1496 SMDS_ElemIteratorPtr faceIt;
1497 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1498 else faceIt = SMESHUtils::elemSetIterator( theElems );
1501 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1503 vector< const SMDS_MeshNode* > nodes;
1504 SMESHDS_SubMesh* subMeshDS = 0;
1506 Handle(Geom_Surface) surface;
1507 TopLoc_Location loc;
1509 while ( faceIt->more() )
1511 const SMDS_MeshElement* quad = faceIt->next();
1512 if ( !quad || quad->NbCornerNodes() != 4 )
1515 // get a surface the quad is on
1517 if ( quad->getshapeId() < 1 )
1520 helper.SetSubShape( 0 );
1523 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1525 helper.SetSubShape( quad->getshapeId() );
1526 if ( !helper.GetSubShape().IsNull() &&
1527 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1529 F = TopoDS::Face( helper.GetSubShape() );
1530 surface = BRep_Tool::Surface( F, loc );
1531 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1535 helper.SetSubShape( 0 );
1540 // create a central node
1542 const SMDS_MeshNode* nCentral;
1543 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1545 if ( nodes.size() == 9 )
1547 nCentral = nodes.back();
1554 for ( ; iN < nodes.size(); ++iN )
1555 xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1557 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1558 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1560 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1561 xyz[0], xyz[1], xyz[2], xyz[3],
1562 xyz[4], xyz[5], xyz[6], xyz[7] );
1566 for ( ; iN < nodes.size(); ++iN )
1567 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1569 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1570 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1572 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1573 uv[0], uv[1], uv[2], uv[3],
1574 uv[4], uv[5], uv[6], uv[7] );
1576 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1580 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1581 uv[8].X(), uv[8].Y() );
1582 myLastCreatedNodes.push_back( nCentral );
1585 // create 4 triangles
1587 helper.SetIsQuadratic ( nodes.size() > 4 );
1588 helper.SetIsBiQuadratic( nodes.size() == 9 );
1589 if ( helper.GetIsQuadratic() )
1590 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1592 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1594 for ( int i = 0; i < 4; ++i )
1596 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1599 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1600 myLastCreatedElems.push_back( tria );
1605 //=======================================================================
1606 //function : BestSplit
1607 //purpose : Find better diagonal for cutting.
1608 //=======================================================================
1610 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1611 SMESH::Controls::NumericalFunctorPtr theCrit)
1618 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1621 if( theQuad->NbNodes()==4 ||
1622 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1624 // retrieve element nodes
1625 const SMDS_MeshNode* aNodes [4];
1626 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1628 //while (itN->more())
1630 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1632 // compare two sets of possible triangles
1633 double aBadRate1, aBadRate2; // to what extent a set is bad
1634 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1635 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1636 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1638 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1639 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1640 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1641 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1642 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1643 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1644 return 1; // diagonal 1-3
1646 return 2; // diagonal 2-4
1653 // Methods of splitting volumes into tetra
1655 const int theHexTo5_1[5*4+1] =
1657 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1659 const int theHexTo5_2[5*4+1] =
1661 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1663 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1665 const int theHexTo6_1[6*4+1] =
1667 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
1669 const int theHexTo6_2[6*4+1] =
1671 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
1673 const int theHexTo6_3[6*4+1] =
1675 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
1677 const int theHexTo6_4[6*4+1] =
1679 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
1681 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1683 const int thePyraTo2_1[2*4+1] =
1685 0, 1, 2, 4, 0, 2, 3, 4, -1
1687 const int thePyraTo2_2[2*4+1] =
1689 1, 2, 3, 4, 1, 3, 0, 4, -1
1691 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1693 const int thePentaTo3_1[3*4+1] =
1695 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1697 const int thePentaTo3_2[3*4+1] =
1699 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1701 const int thePentaTo3_3[3*4+1] =
1703 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1705 const int thePentaTo3_4[3*4+1] =
1707 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1709 const int thePentaTo3_5[3*4+1] =
1711 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1713 const int thePentaTo3_6[3*4+1] =
1715 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1717 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1718 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1720 // Methods of splitting hexahedron into prisms
1722 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1724 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
1726 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1728 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
1730 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1732 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
1735 const int theHexTo2Prisms_BT_1[6*2+1] =
1737 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1739 const int theHexTo2Prisms_BT_2[6*2+1] =
1741 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1743 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1745 const int theHexTo2Prisms_LR_1[6*2+1] =
1747 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1749 const int theHexTo2Prisms_LR_2[6*2+1] =
1751 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1753 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1755 const int theHexTo2Prisms_FB_1[6*2+1] =
1757 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1759 const int theHexTo2Prisms_FB_2[6*2+1] =
1761 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1763 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1766 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1769 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1770 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1771 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1772 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1778 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1779 bool _baryNode; //!< additional node is to be created at cell barycenter
1780 bool _ownConn; //!< to delete _connectivity in destructor
1781 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1783 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1784 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1785 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1786 bool hasFacet( const TTriangleFacet& facet ) const
1788 if ( _nbCorners == 4 )
1790 const int* tetConn = _connectivity;
1791 for ( ; tetConn[0] >= 0; tetConn += 4 )
1792 if (( facet.contains( tetConn[0] ) +
1793 facet.contains( tetConn[1] ) +
1794 facet.contains( tetConn[2] ) +
1795 facet.contains( tetConn[3] )) == 3 )
1798 else // prism, _nbCorners == 6
1800 const int* prismConn = _connectivity;
1801 for ( ; prismConn[0] >= 0; prismConn += 6 )
1803 if (( facet.contains( prismConn[0] ) &&
1804 facet.contains( prismConn[1] ) &&
1805 facet.contains( prismConn[2] ))
1807 ( facet.contains( prismConn[3] ) &&
1808 facet.contains( prismConn[4] ) &&
1809 facet.contains( prismConn[5] )))
1817 //=======================================================================
1819 * \brief return TSplitMethod for the given element to split into tetrahedra
1821 //=======================================================================
1823 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1825 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1827 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1828 // an edge and a face barycenter; tertaherdons are based on triangles and
1829 // a volume barycenter
1830 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1832 // Find out how adjacent volumes are split
1834 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1835 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1836 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1838 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1839 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1840 if ( nbNodes < 4 ) continue;
1842 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1843 const int* nInd = vol.GetFaceNodesIndices( iF );
1846 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1847 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1848 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1849 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1853 int iCom = 0; // common node of triangle faces to split into
1854 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1856 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1857 nInd[ iQ * ( (iCom+1)%nbNodes )],
1858 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1859 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1860 nInd[ iQ * ( (iCom+2)%nbNodes )],
1861 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1862 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1864 triaSplits.push_back( t012 );
1865 triaSplits.push_back( t023 );
1870 if ( !triaSplits.empty() )
1871 hasAdjacentSplits = true;
1874 // Among variants of split method select one compliant with adjacent volumes
1876 TSplitMethod method;
1877 if ( !vol.Element()->IsPoly() && !is24TetMode )
1879 int nbVariants = 2, nbTet = 0;
1880 const int** connVariants = 0;
1881 switch ( vol.Element()->GetEntityType() )
1883 case SMDSEntity_Hexa:
1884 case SMDSEntity_Quad_Hexa:
1885 case SMDSEntity_TriQuad_Hexa:
1886 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1887 connVariants = theHexTo5, nbTet = 5;
1889 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1891 case SMDSEntity_Pyramid:
1892 case SMDSEntity_Quad_Pyramid:
1893 connVariants = thePyraTo2; nbTet = 2;
1895 case SMDSEntity_Penta:
1896 case SMDSEntity_Quad_Penta:
1897 case SMDSEntity_BiQuad_Penta:
1898 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1903 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1905 // check method compliancy with adjacent tetras,
1906 // all found splits must be among facets of tetras described by this method
1907 method = TSplitMethod( nbTet, connVariants[variant] );
1908 if ( hasAdjacentSplits && method._nbSplits > 0 )
1910 bool facetCreated = true;
1911 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1913 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1914 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1915 facetCreated = method.hasFacet( *facet );
1917 if ( !facetCreated )
1918 method = TSplitMethod(0); // incompatible method
1922 if ( method._nbSplits < 1 )
1924 // No standard method is applicable, use a generic solution:
1925 // each facet of a volume is split into triangles and
1926 // each of triangles and a volume barycenter form a tetrahedron.
1928 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1930 int* connectivity = new int[ maxTetConnSize + 1 ];
1931 method._connectivity = connectivity;
1932 method._ownConn = true;
1933 method._baryNode = !isHex27; // to create central node or not
1936 int baryCenInd = vol.NbNodes() - int( isHex27 );
1937 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1939 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1940 const int* nInd = vol.GetFaceNodesIndices( iF );
1941 // find common node of triangle facets of tetra to create
1942 int iCommon = 0; // index in linear numeration
1943 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1944 if ( !triaSplits.empty() )
1947 const TTriangleFacet* facet = &triaSplits.front();
1948 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1949 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1950 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1953 else if ( nbNodes > 3 && !is24TetMode )
1955 // find the best method of splitting into triangles by aspect ratio
1956 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1957 map< double, int > badness2iCommon;
1958 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1959 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1960 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1963 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1965 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1966 nodes[ iQ*((iLast-1)%nbNodes)],
1967 nodes[ iQ*((iLast )%nbNodes)]);
1968 badness += getBadRate( &tria, aspectRatio );
1970 badness2iCommon.insert( make_pair( badness, iCommon ));
1972 // use iCommon with lowest badness
1973 iCommon = badness2iCommon.begin()->second;
1975 if ( iCommon >= nbNodes )
1976 iCommon = 0; // something wrong
1978 // fill connectivity of tetrahedra based on a current face
1979 int nbTet = nbNodes - 2;
1980 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1985 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1986 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1990 method._faceBaryNode[ iF ] = 0;
1991 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1994 for ( int i = 0; i < nbTet; ++i )
1996 int i1 = i, i2 = (i+1) % nbNodes;
1997 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1998 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1999 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2000 connectivity[ connSize++ ] = faceBaryCenInd;
2001 connectivity[ connSize++ ] = baryCenInd;
2006 for ( int i = 0; i < nbTet; ++i )
2008 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2009 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2010 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2011 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2012 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2013 connectivity[ connSize++ ] = baryCenInd;
2016 method._nbSplits += nbTet;
2018 } // loop on volume faces
2020 connectivity[ connSize++ ] = -1;
2022 } // end of generic solution
2026 //=======================================================================
2028 * \brief return TSplitMethod to split haxhedron into prisms
2030 //=======================================================================
2032 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2033 const int methodFlags,
2034 const int facetToSplit)
2036 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2038 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2040 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2042 static TSplitMethod to4methods[4]; // order BT, LR, FB
2043 if ( to4methods[iF]._nbSplits == 0 )
2047 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2048 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2049 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2052 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2053 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2054 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2057 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2058 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2059 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2061 default: return to4methods[3];
2063 to4methods[iF]._nbSplits = 4;
2064 to4methods[iF]._nbCorners = 6;
2066 return to4methods[iF];
2068 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2070 TSplitMethod method;
2072 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2074 const int nbVariants = 2, nbSplits = 2;
2075 const int** connVariants = 0;
2077 case 0: connVariants = theHexTo2Prisms_BT; break;
2078 case 1: connVariants = theHexTo2Prisms_LR; break;
2079 case 2: connVariants = theHexTo2Prisms_FB; break;
2080 default: return method;
2083 // look for prisms adjacent via facetToSplit and an opposite one
2084 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2086 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2087 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2088 if ( nbNodes != 4 ) return method;
2090 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2091 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2092 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2094 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2096 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2101 // there are adjacent prism
2102 for ( int variant = 0; variant < nbVariants; ++variant )
2104 // check method compliancy with adjacent prisms,
2105 // the found prism facets must be among facets of prisms described by current method
2106 method._nbSplits = nbSplits;
2107 method._nbCorners = 6;
2108 method._connectivity = connVariants[ variant ];
2109 if ( method.hasFacet( *t ))
2114 // No adjacent prisms. Select a variant with a best aspect ratio.
2116 double badness[2] = { 0., 0. };
2117 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2118 const SMDS_MeshNode** nodes = vol.GetNodes();
2119 for ( int variant = 0; variant < nbVariants; ++variant )
2120 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2122 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2123 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2125 method._connectivity = connVariants[ variant ];
2126 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2127 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2128 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2130 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2133 badness[ variant ] += getBadRate( &tria, aspectRatio );
2135 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2137 method._nbSplits = nbSplits;
2138 method._nbCorners = 6;
2139 method._connectivity = connVariants[ iBetter ];
2144 //================================================================================
2146 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2148 //================================================================================
2150 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2151 const SMDSAbs_GeometryType geom ) const
2153 // find the tetrahedron including the three nodes of facet
2154 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2155 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2156 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2157 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2158 while ( volIt1->more() )
2160 const SMDS_MeshElement* v = volIt1->next();
2161 if ( v->GetGeomType() != geom )
2163 const int lastCornerInd = v->NbCornerNodes() - 1;
2164 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2165 continue; // medium node not allowed
2166 const int ind2 = v->GetNodeIndex( n2 );
2167 if ( ind2 < 0 || lastCornerInd < ind2 )
2169 const int ind3 = v->GetNodeIndex( n3 );
2170 if ( ind3 < 0 || lastCornerInd < ind3 )
2177 //=======================================================================
2179 * \brief A key of a face of volume
2181 //=======================================================================
2183 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2185 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2187 TIDSortedNodeSet sortedNodes;
2188 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2189 int nbNodes = vol.NbFaceNodes( iF );
2190 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2191 for ( int i = 0; i < nbNodes; i += iQ )
2192 sortedNodes.insert( fNodes[i] );
2193 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2194 first.first = (*(n++))->GetID();
2195 first.second = (*(n++))->GetID();
2196 second.first = (*(n++))->GetID();
2197 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2202 //=======================================================================
2203 //function : SplitVolumes
2204 //purpose : Split volume elements into tetrahedra or prisms.
2205 // If facet ID < 0, element is split into tetrahedra,
2206 // else a hexahedron is split into prisms so that the given facet is
2207 // split into triangles
2208 //=======================================================================
2210 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2211 const int theMethodFlags)
2213 SMDS_VolumeTool volTool;
2214 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2215 fHelper.ToFixNodeParameters( true );
2217 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2218 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2220 SMESH_SequenceOfElemPtr newNodes, newElems;
2222 // map face of volume to it's baricenrtic node
2223 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2225 vector<const SMDS_MeshElement* > splitVols;
2227 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2228 for ( ; elem2facet != theElems.end(); ++elem2facet )
2230 const SMDS_MeshElement* elem = elem2facet->first;
2231 const int facetToSplit = elem2facet->second;
2232 if ( elem->GetType() != SMDSAbs_Volume )
2234 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2235 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2238 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2240 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2241 getTetraSplitMethod( volTool, theMethodFlags ) :
2242 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2243 if ( splitMethod._nbSplits < 1 ) continue;
2245 // find submesh to add new tetras to
2246 if ( !subMesh || !subMesh->Contains( elem ))
2248 int shapeID = FindShape( elem );
2249 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2250 subMesh = GetMeshDS()->MeshElements( shapeID );
2253 if ( elem->IsQuadratic() )
2256 // add quadratic links to the helper
2257 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2259 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2260 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2261 for ( int iN = 0; iN < nbN; iN += iQ )
2262 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2264 helper.SetIsQuadratic( true );
2269 helper.SetIsQuadratic( false );
2271 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2272 volTool.GetNodes() + elem->NbNodes() );
2273 helper.SetElementsOnShape( true );
2274 if ( splitMethod._baryNode )
2276 // make a node at barycenter
2277 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2278 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2279 nodes.push_back( gcNode );
2280 newNodes.push_back( gcNode );
2282 if ( !splitMethod._faceBaryNode.empty() )
2284 // make or find baricentric nodes of faces
2285 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2286 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2288 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2289 volFace2BaryNode.insert
2290 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2293 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2294 newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2296 nodes.push_back( iF_n->second = f_n->second );
2301 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2302 const int* volConn = splitMethod._connectivity;
2303 if ( splitMethod._nbCorners == 4 ) // tetra
2304 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2305 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2306 nodes[ volConn[1] ],
2307 nodes[ volConn[2] ],
2308 nodes[ volConn[3] ]));
2310 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2311 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2312 nodes[ volConn[1] ],
2313 nodes[ volConn[2] ],
2314 nodes[ volConn[3] ],
2315 nodes[ volConn[4] ],
2316 nodes[ volConn[5] ]));
2318 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2320 // Split faces on sides of the split volume
2322 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2323 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2325 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2326 if ( nbNodes < 4 ) continue;
2328 // find an existing face
2329 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2330 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2331 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2332 /*noMedium=*/false))
2335 helper.SetElementsOnShape( false );
2336 vector< const SMDS_MeshElement* > triangles;
2338 // find submesh to add new triangles in
2339 if ( !fSubMesh || !fSubMesh->Contains( face ))
2341 int shapeID = FindShape( face );
2342 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2344 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2345 if ( iF_n != splitMethod._faceBaryNode.end() )
2347 const SMDS_MeshNode *baryNode = iF_n->second;
2348 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2350 const SMDS_MeshNode* n1 = fNodes[iN];
2351 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2352 const SMDS_MeshNode *n3 = baryNode;
2353 if ( !volTool.IsFaceExternal( iF ))
2355 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2357 if ( fSubMesh ) // update position of the bary node on geometry
2360 subMesh->RemoveNode( baryNode );
2361 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2362 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2363 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2365 fHelper.SetSubShape( s );
2366 gp_XY uv( 1e100, 1e100 );
2368 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2369 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2372 // node is too far from the surface
2373 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2374 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2375 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2382 // among possible triangles create ones described by split method
2383 const int* nInd = volTool.GetFaceNodesIndices( iF );
2384 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2385 int iCom = 0; // common node of triangle faces to split into
2386 list< TTriangleFacet > facets;
2387 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2389 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2390 nInd[ iQ * ( (iCom+1)%nbNodes )],
2391 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2392 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2393 nInd[ iQ * ( (iCom+2)%nbNodes )],
2394 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2395 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2397 facets.push_back( t012 );
2398 facets.push_back( t023 );
2399 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2400 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2401 nInd[ iQ * ((iLast-1)%nbNodes )],
2402 nInd[ iQ * ((iLast )%nbNodes )]));
2406 list< TTriangleFacet >::iterator facet = facets.begin();
2407 if ( facet == facets.end() )
2409 for ( ; facet != facets.end(); ++facet )
2411 if ( !volTool.IsFaceExternal( iF ))
2412 swap( facet->_n2, facet->_n3 );
2413 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2414 volNodes[ facet->_n2 ],
2415 volNodes[ facet->_n3 ]));
2418 for ( size_t i = 0; i < triangles.size(); ++i )
2420 if ( !triangles[ i ]) continue;
2422 fSubMesh->AddElement( triangles[ i ]);
2423 newElems.push_back( triangles[ i ]);
2425 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2426 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2428 } // while a face based on facet nodes exists
2429 } // loop on volume faces to split them into triangles
2431 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2433 if ( geomType == SMDSEntity_TriQuad_Hexa )
2435 // remove medium nodes that could become free
2436 for ( int i = 20; i < volTool.NbNodes(); ++i )
2437 if ( volNodes[i]->NbInverseElements() == 0 )
2438 GetMeshDS()->RemoveNode( volNodes[i] );
2440 } // loop on volumes to split
2442 myLastCreatedNodes = newNodes;
2443 myLastCreatedElems = newElems;
2446 //=======================================================================
2447 //function : GetHexaFacetsToSplit
2448 //purpose : For hexahedra that will be split into prisms, finds facets to
2449 // split into triangles. Only hexahedra adjacent to the one closest
2450 // to theFacetNormal.Location() are returned.
2451 //param [in,out] theHexas - the hexahedra
2452 //param [in] theFacetNormal - facet normal
2453 //param [out] theFacets - the hexahedra and found facet IDs
2454 //=======================================================================
2456 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2457 const gp_Ax1& theFacetNormal,
2458 TFacetOfElem & theFacets)
2460 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2462 // Find a hexa closest to the location of theFacetNormal
2464 const SMDS_MeshElement* startHex;
2466 // get SMDS_ElemIteratorPtr on theHexas
2467 typedef const SMDS_MeshElement* TValue;
2468 typedef TIDSortedElemSet::iterator TSetIterator;
2469 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2470 typedef SMDS_MeshElement::GeomFilter TFilter;
2471 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2472 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2473 ( new TElemSetIter( theHexas.begin(),
2475 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2477 SMESH_ElementSearcher* searcher =
2478 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2480 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2485 throw SALOME_Exception( THIS_METHOD "startHex not found");
2488 // Select a facet of startHex by theFacetNormal
2490 SMDS_VolumeTool vTool( startHex );
2491 double norm[3], dot, maxDot = 0;
2493 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2494 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2496 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2504 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2506 // Fill theFacets starting from facetID of startHex
2508 // facets used for searching of volumes adjacent to already treated ones
2509 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2510 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2511 TFacetMap facetsToCheck;
2513 set<const SMDS_MeshNode*> facetNodes;
2514 const SMDS_MeshElement* curHex;
2516 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2520 // move in two directions from startHex via facetID
2521 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2524 int curFacet = facetID;
2525 if ( is2nd ) // do not treat startHex twice
2527 vTool.Set( curHex );
2528 if ( vTool.IsFreeFace( curFacet, &curHex ))
2534 vTool.GetFaceNodes( curFacet, facetNodes );
2535 vTool.Set( curHex );
2536 curFacet = vTool.GetFaceIndex( facetNodes );
2541 // store a facet to split
2542 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2544 theFacets.insert( make_pair( curHex, -1 ));
2547 if ( !allHex && !theHexas.count( curHex ))
2550 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2551 theFacets.insert( make_pair( curHex, curFacet ));
2552 if ( !facetIt2isNew.second )
2555 // remember not-to-split facets in facetsToCheck
2556 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2557 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2559 if ( iF == curFacet && iF == oppFacet )
2561 TVolumeFaceKey facetKey ( vTool, iF );
2562 TElemFacets elemFacet( facetIt2isNew.first, iF );
2563 pair< TFacetMap::iterator, bool > it2isnew =
2564 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2565 if ( !it2isnew.second )
2566 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2568 // pass to a volume adjacent via oppFacet
2569 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2575 // get a new curFacet
2576 vTool.GetFaceNodes( oppFacet, facetNodes );
2577 vTool.Set( curHex );
2578 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2581 } // move in two directions from startHex via facetID
2583 // Find a new startHex by facetsToCheck
2587 TFacetMap::iterator fIt = facetsToCheck.begin();
2588 while ( !startHex && fIt != facetsToCheck.end() )
2590 const TElemFacets& elemFacets = fIt->second;
2591 const SMDS_MeshElement* hex = elemFacets.first->first;
2592 int splitFacet = elemFacets.first->second;
2593 int lateralFacet = elemFacets.second;
2594 facetsToCheck.erase( fIt );
2595 fIt = facetsToCheck.begin();
2598 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2599 curHex->GetGeomType() != SMDSGeom_HEXA )
2601 if ( !allHex && !theHexas.count( curHex ))
2606 // find a facet of startHex to split
2608 set<const SMDS_MeshNode*> lateralNodes;
2609 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2610 vTool.GetFaceNodes( splitFacet, facetNodes );
2611 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2612 vTool.Set( startHex );
2613 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2615 // look for a facet of startHex having common nodes with facetNodes
2616 // but not lateralFacet
2617 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2619 if ( iF == lateralFacet )
2621 int nbCommonNodes = 0;
2622 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2623 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2624 nbCommonNodes += facetNodes.count( nn[ iN ]);
2626 if ( nbCommonNodes >= 2 )
2633 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2635 } // while ( startHex )
2642 //================================================================================
2644 * \brief Selects nodes of several elements according to a given interlace
2645 * \param [in] srcNodes - nodes to select from
2646 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2647 * \param [in] interlace - indices of nodes for all elements
2648 * \param [in] nbElems - nb of elements
2649 * \param [in] nbNodes - nb of nodes in each element
2650 * \param [in] mesh - the mesh
2651 * \param [out] elemQueue - a list to push elements found by the selected nodes
2652 * \param [in] type - type of elements to look for
2654 //================================================================================
2656 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2657 vector< const SMDS_MeshNode* >* tgtNodesVec,
2658 const int* interlace,
2661 SMESHDS_Mesh* mesh = 0,
2662 list< const SMDS_MeshElement* >* elemQueue=0,
2663 SMDSAbs_ElementType type=SMDSAbs_All)
2665 for ( int iE = 0; iE < nbElems; ++iE )
2667 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2668 const int* select = & interlace[iE*nbNodes];
2669 elemNodes.resize( nbNodes );
2670 for ( int iN = 0; iN < nbNodes; ++iN )
2671 elemNodes[iN] = srcNodes[ select[ iN ]];
2673 const SMDS_MeshElement* e;
2675 for ( int iE = 0; iE < nbElems; ++iE )
2676 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2677 elemQueue->push_back( e );
2681 //=======================================================================
2683 * Split bi-quadratic elements into linear ones without creation of additional nodes
2684 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2685 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2686 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2687 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2688 * will be split in order to keep the mesh conformal.
2689 * \param elems - elements to split
2691 //=======================================================================
2693 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2695 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2696 vector<const SMDS_MeshElement* > splitElems;
2697 list< const SMDS_MeshElement* > elemQueue;
2698 list< const SMDS_MeshElement* >::iterator elemIt;
2700 SMESHDS_Mesh * mesh = GetMeshDS();
2701 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2702 int nbElems, nbNodes;
2704 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2705 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2708 elemQueue.push_back( *elemSetIt );
2709 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2711 const SMDS_MeshElement* elem = *elemIt;
2712 switch( elem->GetEntityType() )
2714 case SMDSEntity_TriQuad_Hexa: // HEX27
2716 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2717 nbElems = nbNodes = 8;
2718 elemType = & hexaType;
2720 // get nodes for new elements
2721 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2722 { 1,9,20,8, 17,22,26,21 },
2723 { 2,10,20,9, 18,23,26,22 },
2724 { 3,11,20,10, 19,24,26,23 },
2725 { 16,21,26,24, 4,12,25,15 },
2726 { 17,22,26,21, 5,13,25,12 },
2727 { 18,23,26,22, 6,14,25,13 },
2728 { 19,24,26,23, 7,15,25,14 }};
2729 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2731 // add boundary faces to elemQueue
2732 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2733 { 4,5,6,7, 12,13,14,15, 25 },
2734 { 0,1,5,4, 8,17,12,16, 21 },
2735 { 1,2,6,5, 9,18,13,17, 22 },
2736 { 2,3,7,6, 10,19,14,18, 23 },
2737 { 3,0,4,7, 11,16,15,19, 24 }};
2738 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2740 // add boundary segments to elemQueue
2741 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2742 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2743 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2744 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2747 case SMDSEntity_BiQuad_Triangle: // TRIA7
2749 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2752 elemType = & quadType;
2754 // get nodes for new elements
2755 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2756 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2758 // add boundary segments to elemQueue
2759 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2760 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2763 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2765 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2768 elemType = & quadType;
2770 // get nodes for new elements
2771 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2772 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2774 // add boundary segments to elemQueue
2775 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2776 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2779 case SMDSEntity_Quad_Edge:
2781 if ( elemIt == elemQueue.begin() )
2782 continue; // an elem is in theElems
2783 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2786 elemType = & segType;
2788 // get nodes for new elements
2789 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2790 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2794 } // switch( elem->GetEntityType() )
2796 // Create new elements
2798 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2802 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2803 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2804 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2805 //elemType->SetID( -1 );
2807 for ( int iE = 0; iE < nbElems; ++iE )
2808 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2811 ReplaceElemInGroups( elem, splitElems, mesh );
2814 for ( size_t i = 0; i < splitElems.size(); ++i )
2815 subMesh->AddElement( splitElems[i] );
2820 //=======================================================================
2821 //function : AddToSameGroups
2822 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2823 //=======================================================================
2825 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2826 const SMDS_MeshElement* elemInGroups,
2827 SMESHDS_Mesh * aMesh)
2829 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2830 if (!groups.empty()) {
2831 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2832 for ( ; grIt != groups.end(); grIt++ ) {
2833 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2834 if ( group && group->Contains( elemInGroups ))
2835 group->SMDSGroup().Add( elemToAdd );
2841 //=======================================================================
2842 //function : RemoveElemFromGroups
2843 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2844 //=======================================================================
2845 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2846 SMESHDS_Mesh * aMesh)
2848 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2849 if (!groups.empty())
2851 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2852 for (; GrIt != groups.end(); GrIt++)
2854 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2855 if (!grp || grp->IsEmpty()) continue;
2856 grp->SMDSGroup().Remove(removeelem);
2861 //================================================================================
2863 * \brief Replace elemToRm by elemToAdd in the all groups
2865 //================================================================================
2867 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2868 const SMDS_MeshElement* elemToAdd,
2869 SMESHDS_Mesh * aMesh)
2871 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2872 if (!groups.empty()) {
2873 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2874 for ( ; grIt != groups.end(); grIt++ ) {
2875 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2876 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2877 group->SMDSGroup().Add( elemToAdd );
2882 //================================================================================
2884 * \brief Replace elemToRm by elemToAdd in the all groups
2886 //================================================================================
2888 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2889 const vector<const SMDS_MeshElement*>& elemToAdd,
2890 SMESHDS_Mesh * aMesh)
2892 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2893 if (!groups.empty())
2895 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2896 for ( ; grIt != groups.end(); grIt++ ) {
2897 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2898 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2899 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2900 group->SMDSGroup().Add( elemToAdd[ i ] );
2905 //=======================================================================
2906 //function : QuadToTri
2907 //purpose : Cut quadrangles into triangles.
2908 // theCrit is used to select a diagonal to cut
2909 //=======================================================================
2911 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2912 const bool the13Diag)
2915 myLastCreatedElems.reserve( theElems.size() * 2 );
2917 SMESHDS_Mesh * aMesh = GetMeshDS();
2918 Handle(Geom_Surface) surface;
2919 SMESH_MesherHelper helper( *GetMesh() );
2921 TIDSortedElemSet::iterator itElem;
2922 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2924 const SMDS_MeshElement* elem = *itElem;
2925 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2928 if ( elem->NbNodes() == 4 ) {
2929 // retrieve element nodes
2930 const SMDS_MeshNode* aNodes [4];
2931 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2933 while ( itN->more() )
2934 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2936 int aShapeId = FindShape( elem );
2937 const SMDS_MeshElement* newElem1 = 0;
2938 const SMDS_MeshElement* newElem2 = 0;
2940 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2941 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2944 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2945 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2947 myLastCreatedElems.push_back(newElem1);
2948 myLastCreatedElems.push_back(newElem2);
2949 // put a new triangle on the same shape and add to the same groups
2952 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2953 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2955 AddToSameGroups( newElem1, elem, aMesh );
2956 AddToSameGroups( newElem2, elem, aMesh );
2957 aMesh->RemoveElement( elem );
2960 // Quadratic quadrangle
2962 else if ( elem->NbNodes() >= 8 )
2964 // get surface elem is on
2965 int aShapeId = FindShape( elem );
2966 if ( aShapeId != helper.GetSubShapeID() ) {
2970 shape = aMesh->IndexToShape( aShapeId );
2971 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2972 TopoDS_Face face = TopoDS::Face( shape );
2973 surface = BRep_Tool::Surface( face );
2974 if ( !surface.IsNull() )
2975 helper.SetSubShape( shape );
2979 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
2980 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2981 for ( int i = 0; itN->more(); ++i )
2982 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2984 const SMDS_MeshNode* centrNode = aNodes[8];
2985 if ( centrNode == 0 )
2987 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2988 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
2990 myLastCreatedNodes.push_back(centrNode);
2993 // create a new element
2994 const SMDS_MeshElement* newElem1 = 0;
2995 const SMDS_MeshElement* newElem2 = 0;
2997 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2998 aNodes[6], aNodes[7], centrNode );
2999 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3000 centrNode, aNodes[4], aNodes[5] );
3003 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3004 aNodes[7], aNodes[4], centrNode );
3005 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3006 centrNode, aNodes[5], aNodes[6] );
3008 myLastCreatedElems.push_back(newElem1);
3009 myLastCreatedElems.push_back(newElem2);
3010 // put a new triangle on the same shape and add to the same groups
3013 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3014 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3016 AddToSameGroups( newElem1, elem, aMesh );
3017 AddToSameGroups( newElem2, elem, aMesh );
3018 aMesh->RemoveElement( elem );
3025 //=======================================================================
3026 //function : getAngle
3028 //=======================================================================
3030 double getAngle(const SMDS_MeshElement * tr1,
3031 const SMDS_MeshElement * tr2,
3032 const SMDS_MeshNode * n1,
3033 const SMDS_MeshNode * n2)
3035 double angle = 2. * M_PI; // bad angle
3038 SMESH::Controls::TSequenceOfXYZ P1, P2;
3039 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3040 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3043 if(!tr1->IsQuadratic())
3044 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3046 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3047 if ( N1.SquareMagnitude() <= gp::Resolution() )
3049 if(!tr2->IsQuadratic())
3050 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3052 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3053 if ( N2.SquareMagnitude() <= gp::Resolution() )
3056 // find the first diagonal node n1 in the triangles:
3057 // take in account a diagonal link orientation
3058 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3059 for ( int t = 0; t < 2; t++ ) {
3060 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3061 int i = 0, iDiag = -1;
3062 while ( it->more()) {
3063 const SMDS_MeshElement *n = it->next();
3064 if ( n == n1 || n == n2 ) {
3068 if ( i - iDiag == 1 )
3069 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3078 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3081 angle = N1.Angle( N2 );
3086 // =================================================
3087 // class generating a unique ID for a pair of nodes
3088 // and able to return nodes by that ID
3089 // =================================================
3093 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3094 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3097 long GetLinkID (const SMDS_MeshNode * n1,
3098 const SMDS_MeshNode * n2) const
3100 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3103 bool GetNodes (const long theLinkID,
3104 const SMDS_MeshNode* & theNode1,
3105 const SMDS_MeshNode* & theNode2) const
3107 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3108 if ( !theNode1 ) return false;
3109 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3110 if ( !theNode2 ) return false;
3116 const SMESHDS_Mesh* myMesh;
3121 //=======================================================================
3122 //function : TriToQuad
3123 //purpose : Fuse neighbour triangles into quadrangles.
3124 // theCrit is used to select a neighbour to fuse with.
3125 // theMaxAngle is a max angle between element normals at which
3126 // fusion is still performed.
3127 //=======================================================================
3129 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3130 SMESH::Controls::NumericalFunctorPtr theCrit,
3131 const double theMaxAngle)
3134 myLastCreatedElems.reserve( theElems.size() / 2 );
3136 if ( !theCrit.get() )
3139 SMESHDS_Mesh * aMesh = GetMeshDS();
3141 // Prepare data for algo: build
3142 // 1. map of elements with their linkIDs
3143 // 2. map of linkIDs with their elements
3145 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3146 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3147 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3148 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3150 TIDSortedElemSet::iterator itElem;
3151 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3153 const SMDS_MeshElement* elem = *itElem;
3154 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3155 bool IsTria = ( elem->NbCornerNodes()==3 );
3156 if (!IsTria) continue;
3158 // retrieve element nodes
3159 const SMDS_MeshNode* aNodes [4];
3160 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3163 aNodes[ i++ ] = itN->next();
3164 aNodes[ 3 ] = aNodes[ 0 ];
3167 for ( i = 0; i < 3; i++ ) {
3168 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3169 // check if elements sharing a link can be fused
3170 itLE = mapLi_listEl.find( link );
3171 if ( itLE != mapLi_listEl.end() ) {
3172 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3174 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3175 //if ( FindShape( elem ) != FindShape( elem2 ))
3176 // continue; // do not fuse triangles laying on different shapes
3177 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3178 continue; // avoid making badly shaped quads
3179 (*itLE).second.push_back( elem );
3182 mapLi_listEl[ link ].push_back( elem );
3184 mapEl_setLi [ elem ].insert( link );
3187 // Clean the maps from the links shared by a sole element, ie
3188 // links to which only one element is bound in mapLi_listEl
3190 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3191 int nbElems = (*itLE).second.size();
3192 if ( nbElems < 2 ) {
3193 const SMDS_MeshElement* elem = (*itLE).second.front();
3194 SMESH_TLink link = (*itLE).first;
3195 mapEl_setLi[ elem ].erase( link );
3196 if ( mapEl_setLi[ elem ].empty() )
3197 mapEl_setLi.erase( elem );
3201 // Algo: fuse triangles into quadrangles
3203 while ( ! mapEl_setLi.empty() ) {
3204 // Look for the start element:
3205 // the element having the least nb of shared links
3206 const SMDS_MeshElement* startElem = 0;
3208 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3209 int nbLinks = (*itEL).second.size();
3210 if ( nbLinks < minNbLinks ) {
3211 startElem = (*itEL).first;
3212 minNbLinks = nbLinks;
3213 if ( minNbLinks == 1 )
3218 // search elements to fuse starting from startElem or links of elements
3219 // fused earlyer - startLinks
3220 list< SMESH_TLink > startLinks;
3221 while ( startElem || !startLinks.empty() ) {
3222 while ( !startElem && !startLinks.empty() ) {
3223 // Get an element to start, by a link
3224 SMESH_TLink linkId = startLinks.front();
3225 startLinks.pop_front();
3226 itLE = mapLi_listEl.find( linkId );
3227 if ( itLE != mapLi_listEl.end() ) {
3228 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3229 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3230 for ( ; itE != listElem.end() ; itE++ )
3231 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3233 mapLi_listEl.erase( itLE );
3238 // Get candidates to be fused
3239 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3240 const SMESH_TLink *link12 = 0, *link13 = 0;
3242 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3243 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3244 ASSERT( !setLi.empty() );
3245 set< SMESH_TLink >::iterator itLi;
3246 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3248 const SMESH_TLink & link = (*itLi);
3249 itLE = mapLi_listEl.find( link );
3250 if ( itLE == mapLi_listEl.end() )
3253 const SMDS_MeshElement* elem = (*itLE).second.front();
3255 elem = (*itLE).second.back();
3256 mapLi_listEl.erase( itLE );
3257 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3268 // add other links of elem to list of links to re-start from
3269 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3270 set< SMESH_TLink >::iterator it;
3271 for ( it = links.begin(); it != links.end(); it++ ) {
3272 const SMESH_TLink& link2 = (*it);
3273 if ( link2 != link )
3274 startLinks.push_back( link2 );
3278 // Get nodes of possible quadrangles
3279 const SMDS_MeshNode *n12 [4], *n13 [4];
3280 bool Ok12 = false, Ok13 = false;
3281 const SMDS_MeshNode *linkNode1, *linkNode2;
3283 linkNode1 = link12->first;
3284 linkNode2 = link12->second;
3285 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3289 linkNode1 = link13->first;
3290 linkNode2 = link13->second;
3291 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3295 // Choose a pair to fuse
3296 if ( Ok12 && Ok13 ) {
3297 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3298 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3299 double aBadRate12 = getBadRate( &quad12, theCrit );
3300 double aBadRate13 = getBadRate( &quad13, theCrit );
3301 if ( aBadRate13 < aBadRate12 )
3308 // and remove fused elems and remove links from the maps
3309 mapEl_setLi.erase( tr1 );
3312 mapEl_setLi.erase( tr2 );
3313 mapLi_listEl.erase( *link12 );
3314 if ( tr1->NbNodes() == 3 )
3316 const SMDS_MeshElement* newElem = 0;
3317 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3318 myLastCreatedElems.push_back(newElem);
3319 AddToSameGroups( newElem, tr1, aMesh );
3320 int aShapeId = tr1->getshapeId();
3322 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3323 aMesh->RemoveElement( tr1 );
3324 aMesh->RemoveElement( tr2 );
3327 vector< const SMDS_MeshNode* > N1;
3328 vector< const SMDS_MeshNode* > N2;
3329 getNodesFromTwoTria(tr1,tr2,N1,N2);
3330 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3331 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3332 // i.e. first nodes from both arrays form a new diagonal
3333 const SMDS_MeshNode* aNodes[8];
3342 const SMDS_MeshElement* newElem = 0;
3343 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3344 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3345 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3347 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3348 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3349 myLastCreatedElems.push_back(newElem);
3350 AddToSameGroups( newElem, tr1, aMesh );
3351 int aShapeId = tr1->getshapeId();
3353 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3354 aMesh->RemoveElement( tr1 );
3355 aMesh->RemoveElement( tr2 );
3356 // remove middle node (9)
3357 if ( N1[4]->NbInverseElements() == 0 )
3358 aMesh->RemoveNode( N1[4] );
3359 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3360 aMesh->RemoveNode( N1[6] );
3361 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3362 aMesh->RemoveNode( N2[6] );
3367 mapEl_setLi.erase( tr3 );
3368 mapLi_listEl.erase( *link13 );
3369 if ( tr1->NbNodes() == 3 ) {
3370 const SMDS_MeshElement* newElem = 0;
3371 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3372 myLastCreatedElems.push_back(newElem);
3373 AddToSameGroups( newElem, tr1, aMesh );
3374 int aShapeId = tr1->getshapeId();
3376 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3377 aMesh->RemoveElement( tr1 );
3378 aMesh->RemoveElement( tr3 );
3381 vector< const SMDS_MeshNode* > N1;
3382 vector< const SMDS_MeshNode* > N2;
3383 getNodesFromTwoTria(tr1,tr3,N1,N2);
3384 // now we receive following N1 and N2 (using numeration as above image)
3385 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3386 // i.e. first nodes from both arrays form a new diagonal
3387 const SMDS_MeshNode* aNodes[8];
3396 const SMDS_MeshElement* newElem = 0;
3397 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3398 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3399 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3401 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3402 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3403 myLastCreatedElems.push_back(newElem);
3404 AddToSameGroups( newElem, tr1, aMesh );
3405 int aShapeId = tr1->getshapeId();
3407 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3408 aMesh->RemoveElement( tr1 );
3409 aMesh->RemoveElement( tr3 );
3410 // remove middle node (9)
3411 if ( N1[4]->NbInverseElements() == 0 )
3412 aMesh->RemoveNode( N1[4] );
3413 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3414 aMesh->RemoveNode( N1[6] );
3415 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3416 aMesh->RemoveNode( N2[6] );
3420 // Next element to fuse: the rejected one
3422 startElem = Ok12 ? tr3 : tr2;
3424 } // if ( startElem )
3425 } // while ( startElem || !startLinks.empty() )
3426 } // while ( ! mapEl_setLi.empty() )
3431 //================================================================================
3433 * \brief Return nodes linked to the given one
3434 * \param theNode - the node
3435 * \param linkedNodes - the found nodes
3436 * \param type - the type of elements to check
3438 * Medium nodes are ignored
3440 //================================================================================
3442 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3443 TIDSortedElemSet & linkedNodes,
3444 SMDSAbs_ElementType type )
3446 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3447 while ( elemIt->more() )
3449 const SMDS_MeshElement* elem = elemIt->next();
3450 if(elem->GetType() == SMDSAbs_0DElement)
3453 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3454 if ( elem->GetType() == SMDSAbs_Volume )
3456 SMDS_VolumeTool vol( elem );
3457 while ( nodeIt->more() ) {
3458 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3459 if ( theNode != n && vol.IsLinked( theNode, n ))
3460 linkedNodes.insert( n );
3465 for ( int i = 0; nodeIt->more(); ++i ) {
3466 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3467 if ( n == theNode ) {
3468 int iBefore = i - 1;
3470 if ( elem->IsQuadratic() ) {
3471 int nb = elem->NbNodes() / 2;
3472 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3473 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3475 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3476 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3483 //=======================================================================
3484 //function : laplacianSmooth
3485 //purpose : pulls theNode toward the center of surrounding nodes directly
3486 // connected to that node along an element edge
3487 //=======================================================================
3489 void laplacianSmooth(const SMDS_MeshNode* theNode,
3490 const Handle(Geom_Surface)& theSurface,
3491 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3493 // find surrounding nodes
3495 TIDSortedElemSet nodeSet;
3496 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3498 // compute new coodrs
3500 double coord[] = { 0., 0., 0. };
3501 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3502 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3503 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3504 if ( theSurface.IsNull() ) { // smooth in 3D
3505 coord[0] += node->X();
3506 coord[1] += node->Y();
3507 coord[2] += node->Z();
3509 else { // smooth in 2D
3510 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3511 gp_XY* uv = theUVMap[ node ];
3512 coord[0] += uv->X();
3513 coord[1] += uv->Y();
3516 int nbNodes = nodeSet.size();
3519 coord[0] /= nbNodes;
3520 coord[1] /= nbNodes;
3522 if ( !theSurface.IsNull() ) {
3523 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3524 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3525 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3531 coord[2] /= nbNodes;
3535 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3538 //=======================================================================
3539 //function : centroidalSmooth
3540 //purpose : pulls theNode toward the element-area-weighted centroid of the
3541 // surrounding elements
3542 //=======================================================================
3544 void centroidalSmooth(const SMDS_MeshNode* theNode,
3545 const Handle(Geom_Surface)& theSurface,
3546 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3548 gp_XYZ aNewXYZ(0.,0.,0.);
3549 SMESH::Controls::Area anAreaFunc;
3550 double totalArea = 0.;
3555 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3556 while ( elemIt->more() )
3558 const SMDS_MeshElement* elem = elemIt->next();
3561 gp_XYZ elemCenter(0.,0.,0.);
3562 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3563 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3564 int nn = elem->NbNodes();
3565 if(elem->IsQuadratic()) nn = nn/2;
3567 //while ( itN->more() ) {
3569 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3571 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3572 aNodePoints.push_back( aP );
3573 if ( !theSurface.IsNull() ) { // smooth in 2D
3574 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3575 gp_XY* uv = theUVMap[ aNode ];
3576 aP.SetCoord( uv->X(), uv->Y(), 0. );
3580 double elemArea = anAreaFunc.GetValue( aNodePoints );
3581 totalArea += elemArea;
3583 aNewXYZ += elemCenter * elemArea;
3585 aNewXYZ /= totalArea;
3586 if ( !theSurface.IsNull() ) {
3587 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3588 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3593 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3596 //=======================================================================
3597 //function : getClosestUV
3598 //purpose : return UV of closest projection
3599 //=======================================================================
3601 static bool getClosestUV (Extrema_GenExtPS& projector,
3602 const gp_Pnt& point,
3605 projector.Perform( point );
3606 if ( projector.IsDone() ) {
3607 double u, v, minVal = DBL_MAX;
3608 for ( int i = projector.NbExt(); i > 0; i-- )
3609 if ( projector.SquareDistance( i ) < minVal ) {
3610 minVal = projector.SquareDistance( i );
3611 projector.Point( i ).Parameter( u, v );
3613 result.SetCoord( u, v );
3619 //=======================================================================
3621 //purpose : Smooth theElements during theNbIterations or until a worst
3622 // element has aspect ratio <= theTgtAspectRatio.
3623 // Aspect Ratio varies in range [1.0, inf].
3624 // If theElements is empty, the whole mesh is smoothed.
3625 // theFixedNodes contains additionally fixed nodes. Nodes built
3626 // on edges and boundary nodes are always fixed.
3627 //=======================================================================
3629 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3630 set<const SMDS_MeshNode*> & theFixedNodes,
3631 const SmoothMethod theSmoothMethod,
3632 const int theNbIterations,
3633 double theTgtAspectRatio,
3638 if ( theTgtAspectRatio < 1.0 )
3639 theTgtAspectRatio = 1.0;
3641 const double disttol = 1.e-16;
3643 SMESH::Controls::AspectRatio aQualityFunc;
3645 SMESHDS_Mesh* aMesh = GetMeshDS();
3647 if ( theElems.empty() ) {
3648 // add all faces to theElems
3649 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3650 while ( fIt->more() ) {
3651 const SMDS_MeshElement* face = fIt->next();
3652 theElems.insert( theElems.end(), face );
3655 // get all face ids theElems are on
3656 set< int > faceIdSet;
3657 TIDSortedElemSet::iterator itElem;
3659 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3660 int fId = FindShape( *itElem );
3661 // check that corresponding submesh exists and a shape is face
3663 faceIdSet.find( fId ) == faceIdSet.end() &&
3664 aMesh->MeshElements( fId )) {
3665 TopoDS_Shape F = aMesh->IndexToShape( fId );
3666 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3667 faceIdSet.insert( fId );
3670 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3672 // ===============================================
3673 // smooth elements on each TopoDS_Face separately
3674 // ===============================================
3676 SMESH_MesherHelper helper( *GetMesh() );
3678 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3679 for ( ; fId != faceIdSet.rend(); ++fId )
3681 // get face surface and submesh
3682 Handle(Geom_Surface) surface;
3683 SMESHDS_SubMesh* faceSubMesh = 0;
3686 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3687 bool isUPeriodic = false, isVPeriodic = false;
3690 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3691 surface = BRep_Tool::Surface( face );
3692 faceSubMesh = aMesh->MeshElements( *fId );
3693 fToler2 = BRep_Tool::Tolerance( face );
3694 fToler2 *= fToler2 * 10.;
3695 isUPeriodic = surface->IsUPeriodic();
3696 // if ( isUPeriodic )
3697 // surface->UPeriod();
3698 isVPeriodic = surface->IsVPeriodic();
3699 // if ( isVPeriodic )
3700 // surface->VPeriod();
3701 surface->Bounds( u1, u2, v1, v2 );
3702 helper.SetSubShape( face );
3704 // ---------------------------------------------------------
3705 // for elements on a face, find movable and fixed nodes and
3706 // compute UV for them
3707 // ---------------------------------------------------------
3708 bool checkBoundaryNodes = false;
3709 bool isQuadratic = false;
3710 set<const SMDS_MeshNode*> setMovableNodes;
3711 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3712 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3713 list< const SMDS_MeshElement* > elemsOnFace;
3715 Extrema_GenExtPS projector;
3716 GeomAdaptor_Surface surfAdaptor;
3717 if ( !surface.IsNull() ) {
3718 surfAdaptor.Load( surface );
3719 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3721 int nbElemOnFace = 0;
3722 itElem = theElems.begin();
3723 // loop on not yet smoothed elements: look for elems on a face
3724 while ( itElem != theElems.end() )
3726 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3727 break; // all elements found
3729 const SMDS_MeshElement* elem = *itElem;
3730 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3731 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3735 elemsOnFace.push_back( elem );
3736 theElems.erase( itElem++ );
3740 isQuadratic = elem->IsQuadratic();
3742 // get movable nodes of elem
3743 const SMDS_MeshNode* node;
3744 SMDS_TypeOfPosition posType;
3745 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3746 int nn = 0, nbn = elem->NbNodes();
3747 if(elem->IsQuadratic())
3749 while ( nn++ < nbn ) {
3750 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3751 const SMDS_PositionPtr& pos = node->GetPosition();
3752 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3753 if (posType != SMDS_TOP_EDGE &&
3754 posType != SMDS_TOP_VERTEX &&
3755 theFixedNodes.find( node ) == theFixedNodes.end())
3757 // check if all faces around the node are on faceSubMesh
3758 // because a node on edge may be bound to face
3760 if ( faceSubMesh ) {
3761 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3762 while ( eIt->more() && all ) {
3763 const SMDS_MeshElement* e = eIt->next();
3764 all = faceSubMesh->Contains( e );
3768 setMovableNodes.insert( node );
3770 checkBoundaryNodes = true;
3772 if ( posType == SMDS_TOP_3DSPACE )
3773 checkBoundaryNodes = true;
3776 if ( surface.IsNull() )
3779 // get nodes to check UV
3780 list< const SMDS_MeshNode* > uvCheckNodes;
3781 const SMDS_MeshNode* nodeInFace = 0;
3782 itN = elem->nodesIterator();
3783 nn = 0; nbn = elem->NbNodes();
3784 if(elem->IsQuadratic())
3786 while ( nn++ < nbn ) {
3787 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3788 if ( node->GetPosition()->GetDim() == 2 )
3790 if ( uvMap.find( node ) == uvMap.end() )
3791 uvCheckNodes.push_back( node );
3792 // add nodes of elems sharing node
3793 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3794 // while ( eIt->more() ) {
3795 // const SMDS_MeshElement* e = eIt->next();
3796 // if ( e != elem ) {
3797 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3798 // while ( nIt->more() ) {
3799 // const SMDS_MeshNode* n =
3800 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3801 // if ( uvMap.find( n ) == uvMap.end() )
3802 // uvCheckNodes.push_back( n );
3808 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3809 for ( ; n != uvCheckNodes.end(); ++n ) {
3812 const SMDS_PositionPtr& pos = node->GetPosition();
3813 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3817 bool toCheck = true;
3818 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3820 // compute not existing UV
3821 bool project = ( posType == SMDS_TOP_3DSPACE );
3822 // double dist1 = DBL_MAX, dist2 = 0;
3823 // if ( posType != SMDS_TOP_3DSPACE ) {
3824 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3825 // project = dist1 > fToler2;
3827 if ( project ) { // compute new UV
3829 gp_Pnt pNode = SMESH_NodeXYZ( node );
3830 if ( !getClosestUV( projector, pNode, newUV )) {
3831 MESSAGE("Node Projection Failed " << node);
3835 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3837 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3839 // if ( posType != SMDS_TOP_3DSPACE )
3840 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3841 // if ( dist2 < dist1 )
3845 // store UV in the map
3846 listUV.push_back( uv );
3847 uvMap.insert( make_pair( node, &listUV.back() ));
3849 } // loop on not yet smoothed elements
3851 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3852 checkBoundaryNodes = true;
3854 // fix nodes on mesh boundary
3856 if ( checkBoundaryNodes ) {
3857 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3858 map< SMESH_TLink, int >::iterator link_nb;
3859 // put all elements links to linkNbMap
3860 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3861 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3862 const SMDS_MeshElement* elem = (*elemIt);
3863 int nbn = elem->NbCornerNodes();
3864 // loop on elem links: insert them in linkNbMap
3865 for ( int iN = 0; iN < nbn; ++iN ) {
3866 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3867 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3868 SMESH_TLink link( n1, n2 );
3869 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3873 // remove nodes that are in links encountered only once from setMovableNodes
3874 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3875 if ( link_nb->second == 1 ) {
3876 setMovableNodes.erase( link_nb->first.node1() );
3877 setMovableNodes.erase( link_nb->first.node2() );
3882 // -----------------------------------------------------
3883 // for nodes on seam edge, compute one more UV ( uvMap2 );
3884 // find movable nodes linked to nodes on seam and which
3885 // are to be smoothed using the second UV ( uvMap2 )
3886 // -----------------------------------------------------
3888 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3889 if ( !surface.IsNull() ) {
3890 TopExp_Explorer eExp( face, TopAbs_EDGE );
3891 for ( ; eExp.More(); eExp.Next() ) {
3892 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3893 if ( !BRep_Tool::IsClosed( edge, face ))
3895 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3896 if ( !sm ) continue;
3897 // find out which parameter varies for a node on seam
3900 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3901 if ( pcurve.IsNull() ) continue;
3902 uv1 = pcurve->Value( f );
3904 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3905 if ( pcurve.IsNull() ) continue;
3906 uv2 = pcurve->Value( f );
3907 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3909 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3910 std::swap( uv1, uv2 );
3911 // get nodes on seam and its vertices
3912 list< const SMDS_MeshNode* > seamNodes;
3913 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3914 while ( nSeamIt->more() ) {
3915 const SMDS_MeshNode* node = nSeamIt->next();
3916 if ( !isQuadratic || !IsMedium( node ))
3917 seamNodes.push_back( node );
3919 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3920 for ( ; vExp.More(); vExp.Next() ) {
3921 sm = aMesh->MeshElements( vExp.Current() );
3923 nSeamIt = sm->GetNodes();
3924 while ( nSeamIt->more() )
3925 seamNodes.push_back( nSeamIt->next() );
3928 // loop on nodes on seam
3929 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3930 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3931 const SMDS_MeshNode* nSeam = *noSeIt;
3932 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3933 if ( n_uv == uvMap.end() )
3936 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3937 // set the second UV
3938 listUV.push_back( *n_uv->second );
3939 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3940 if ( uvMap2.empty() )
3941 uvMap2 = uvMap; // copy the uvMap contents
3942 uvMap2[ nSeam ] = &listUV.back();
3944 // collect movable nodes linked to ones on seam in nodesNearSeam
3945 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3946 while ( eIt->more() ) {
3947 const SMDS_MeshElement* e = eIt->next();
3948 int nbUseMap1 = 0, nbUseMap2 = 0;
3949 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3950 int nn = 0, nbn = e->NbNodes();
3951 if(e->IsQuadratic()) nbn = nbn/2;
3952 while ( nn++ < nbn )
3954 const SMDS_MeshNode* n =
3955 static_cast<const SMDS_MeshNode*>( nIt->next() );
3957 setMovableNodes.find( n ) == setMovableNodes.end() )
3959 // add only nodes being closer to uv2 than to uv1
3960 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3961 // 0.5 * ( n->Y() + nSeam->Y() ),
3962 // 0.5 * ( n->Z() + nSeam->Z() ));
3964 // getClosestUV( projector, pMid, uv );
3965 double x = uvMap[ n ]->Coord( iPar );
3966 if ( Abs( uv1.Coord( iPar ) - x ) >
3967 Abs( uv2.Coord( iPar ) - x )) {
3968 nodesNearSeam.insert( n );
3974 // for centroidalSmooth all element nodes must
3975 // be on one side of a seam
3976 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3977 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3979 while ( nn++ < nbn ) {
3980 const SMDS_MeshNode* n =
3981 static_cast<const SMDS_MeshNode*>( nIt->next() );
3982 setMovableNodes.erase( n );
3986 } // loop on nodes on seam
3987 } // loop on edge of a face
3988 } // if ( !face.IsNull() )
3990 if ( setMovableNodes.empty() ) {
3991 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3992 continue; // goto next face
4000 double maxRatio = -1., maxDisplacement = -1.;
4001 set<const SMDS_MeshNode*>::iterator nodeToMove;
4002 for ( it = 0; it < theNbIterations; it++ ) {
4003 maxDisplacement = 0.;
4004 nodeToMove = setMovableNodes.begin();
4005 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4006 const SMDS_MeshNode* node = (*nodeToMove);
4007 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4010 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4011 if ( theSmoothMethod == LAPLACIAN )
4012 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4014 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4016 // node displacement
4017 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4018 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4019 if ( aDispl > maxDisplacement )
4020 maxDisplacement = aDispl;
4022 // no node movement => exit
4023 //if ( maxDisplacement < 1.e-16 ) {
4024 if ( maxDisplacement < disttol ) {
4025 MESSAGE("-- no node movement --");
4029 // check elements quality
4031 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4032 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4033 const SMDS_MeshElement* elem = (*elemIt);
4034 if ( !elem || elem->GetType() != SMDSAbs_Face )
4036 SMESH::Controls::TSequenceOfXYZ aPoints;
4037 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4038 double aValue = aQualityFunc.GetValue( aPoints );
4039 if ( aValue > maxRatio )
4043 if ( maxRatio <= theTgtAspectRatio ) {
4044 //MESSAGE("-- quality achieved --");
4047 if (it+1 == theNbIterations) {
4048 //MESSAGE("-- Iteration limit exceeded --");
4050 } // smoothing iterations
4052 // MESSAGE(" Face id: " << *fId <<
4053 // " Nb iterstions: " << it <<
4054 // " Displacement: " << maxDisplacement <<
4055 // " Aspect Ratio " << maxRatio);
4057 // ---------------------------------------
4058 // new nodes positions are computed,
4059 // record movement in DS and set new UV
4060 // ---------------------------------------
4061 nodeToMove = setMovableNodes.begin();
4062 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4063 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4064 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4065 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4066 if ( node_uv != uvMap.end() ) {
4067 gp_XY* uv = node_uv->second;
4069 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4073 // move medium nodes of quadratic elements
4076 vector<const SMDS_MeshNode*> nodes;
4078 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4079 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4081 const SMDS_MeshElement* QF = *elemIt;
4082 if ( QF->IsQuadratic() )
4084 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4085 SMDS_MeshElement::iterator() );
4086 nodes.push_back( nodes[0] );
4088 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4090 if ( !surface.IsNull() )
4092 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4093 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4094 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4095 xyz = surface->Value( uv.X(), uv.Y() );
4098 xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4100 if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4101 // we have to move a medium node
4102 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4108 } // loop on face ids
4114 //=======================================================================
4115 //function : isReverse
4116 //purpose : Return true if normal of prevNodes is not co-directied with
4117 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4118 // iNotSame is where prevNodes and nextNodes are different.
4119 // If result is true then future volume orientation is OK
4120 //=======================================================================
4122 bool isReverse(const SMDS_MeshElement* face,
4123 const vector<const SMDS_MeshNode*>& prevNodes,
4124 const vector<const SMDS_MeshNode*>& nextNodes,
4128 SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4129 SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4130 gp_XYZ extrDir( pN - pP ), faceNorm;
4131 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4133 return faceNorm * extrDir < 0.0;
4136 //================================================================================
4138 * \brief Assure that theElemSets[0] holds elements, not nodes
4140 //================================================================================
4142 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4144 if ( !theElemSets[0].empty() &&
4145 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4147 std::swap( theElemSets[0], theElemSets[1] );
4149 else if ( !theElemSets[1].empty() &&
4150 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4152 std::swap( theElemSets[0], theElemSets[1] );
4157 //=======================================================================
4159 * \brief Create elements by sweeping an element
4160 * \param elem - element to sweep
4161 * \param newNodesItVec - nodes generated from each node of the element
4162 * \param newElems - generated elements
4163 * \param nbSteps - number of sweeping steps
4164 * \param srcElements - to append elem for each generated element
4166 //=======================================================================
4168 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4169 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4170 list<const SMDS_MeshElement*>& newElems,
4171 const size_t nbSteps,
4172 SMESH_SequenceOfElemPtr& srcElements)
4174 SMESHDS_Mesh* aMesh = GetMeshDS();
4176 const int nbNodes = elem->NbNodes();
4177 const int nbCorners = elem->NbCornerNodes();
4178 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4179 polyhedron creation !!! */
4180 // Loop on elem nodes:
4181 // find new nodes and detect same nodes indices
4182 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4183 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4184 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4185 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4187 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4188 vector<int> sames(nbNodes);
4189 vector<bool> isSingleNode(nbNodes);
4191 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4192 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4193 const SMDS_MeshNode* node = nnIt->first;
4194 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4195 if ( listNewNodes.empty() )
4198 itNN [ iNode ] = listNewNodes.begin();
4199 prevNod[ iNode ] = node;
4200 nextNod[ iNode ] = listNewNodes.front();
4202 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4203 corner node of linear */
4204 if ( prevNod[ iNode ] != nextNod [ iNode ])
4205 nbDouble += !isSingleNode[iNode];
4207 if( iNode < nbCorners ) { // check corners only
4208 if ( prevNod[ iNode ] == nextNod [ iNode ])
4209 sames[nbSame++] = iNode;
4211 iNotSameNode = iNode;
4215 if ( nbSame == nbNodes || nbSame > 2) {
4216 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4220 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4222 // fix nodes order to have bottom normal external
4223 if ( baseType == SMDSEntity_Polygon )
4225 std::reverse( itNN.begin(), itNN.end() );
4226 std::reverse( prevNod.begin(), prevNod.end() );
4227 std::reverse( midlNod.begin(), midlNod.end() );
4228 std::reverse( nextNod.begin(), nextNod.end() );
4229 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4233 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4234 SMDS_MeshCell::applyInterlace( ind, itNN );
4235 SMDS_MeshCell::applyInterlace( ind, prevNod );
4236 SMDS_MeshCell::applyInterlace( ind, nextNod );
4237 SMDS_MeshCell::applyInterlace( ind, midlNod );
4238 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4241 sames[nbSame] = iNotSameNode;
4242 for ( int j = 0; j <= nbSame; ++j )
4243 for ( size_t i = 0; i < ind.size(); ++i )
4244 if ( ind[i] == sames[j] )
4249 iNotSameNode = sames[nbSame];
4253 else if ( elem->GetType() == SMDSAbs_Edge )
4255 // orient a new face same as adjacent one
4257 const SMDS_MeshElement* e;
4258 TIDSortedElemSet dummy;
4259 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4260 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4261 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4263 // there is an adjacent face, check order of nodes in it
4264 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4267 std::swap( itNN[0], itNN[1] );
4268 std::swap( prevNod[0], prevNod[1] );
4269 std::swap( nextNod[0], nextNod[1] );
4270 std::swap( isSingleNode[0], isSingleNode[1] );
4272 sames[0] = 1 - sames[0];
4273 iNotSameNode = 1 - iNotSameNode;
4278 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4280 iSameNode = sames[ nbSame-1 ];
4281 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4282 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4283 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4286 if ( baseType == SMDSEntity_Polygon )
4288 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4289 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4291 else if ( baseType == SMDSEntity_Quad_Polygon )
4293 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4294 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4297 // make new elements
4298 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4301 for ( iNode = 0; iNode < nbNodes; iNode++ )
4303 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4304 nextNod[ iNode ] = *itNN[ iNode ]++;
4307 SMDS_MeshElement* aNewElem = 0;
4308 /*if(!elem->IsPoly())*/ {
4309 switch ( baseType ) {
4311 case SMDSEntity_Node: { // sweep NODE
4312 if ( nbSame == 0 ) {
4313 if ( isSingleNode[0] )
4314 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4316 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4322 case SMDSEntity_Edge: { // sweep EDGE
4323 if ( nbDouble == 0 )
4325 if ( nbSame == 0 ) // ---> quadrangle
4326 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4327 nextNod[ 1 ], nextNod[ 0 ] );
4328 else // ---> triangle
4329 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4330 nextNod[ iNotSameNode ] );
4332 else // ---> polygon
4334 vector<const SMDS_MeshNode*> poly_nodes;
4335 poly_nodes.push_back( prevNod[0] );
4336 poly_nodes.push_back( prevNod[1] );
4337 if ( prevNod[1] != nextNod[1] )
4339 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4340 poly_nodes.push_back( nextNod[1] );
4342 if ( prevNod[0] != nextNod[0] )
4344 poly_nodes.push_back( nextNod[0] );
4345 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4347 switch ( poly_nodes.size() ) {
4349 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4352 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4353 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4356 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4361 case SMDSEntity_Triangle: // TRIANGLE --->
4363 if ( nbDouble > 0 ) break;
4364 if ( nbSame == 0 ) // ---> pentahedron
4365 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4366 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4368 else if ( nbSame == 1 ) // ---> pyramid
4369 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4370 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4371 nextNod[ iSameNode ]);
4373 else // 2 same nodes: ---> tetrahedron
4374 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4375 nextNod[ iNotSameNode ]);
4378 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4382 if ( nbDouble+nbSame == 2 )
4384 if(nbSame==0) { // ---> quadratic quadrangle
4385 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4386 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4388 else { //(nbSame==1) // ---> quadratic triangle
4390 return; // medium node on axis
4392 else if(sames[0]==0)
4393 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4394 prevNod[2], midlNod[1], nextNod[2] );
4396 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4397 prevNod[2], nextNod[2], midlNod[0]);
4400 else if ( nbDouble == 3 )
4402 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4403 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4404 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4411 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4412 if ( nbDouble > 0 ) break;
4414 if ( nbSame == 0 ) // ---> hexahedron
4415 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4416 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4418 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4419 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4420 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4421 nextNod[ iSameNode ]);
4422 newElems.push_back( aNewElem );
4423 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4424 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4425 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4427 else if ( nbSame == 2 ) { // ---> pentahedron
4428 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4429 // iBeforeSame is same too
4430 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4431 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4432 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4434 // iAfterSame is same too
4435 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4436 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4437 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4441 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4442 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4443 if ( nbDouble+nbSame != 3 ) break;
4445 // ---> pentahedron with 15 nodes
4446 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4447 nextNod[0], nextNod[1], nextNod[2],
4448 prevNod[3], prevNod[4], prevNod[5],
4449 nextNod[3], nextNod[4], nextNod[5],
4450 midlNod[0], midlNod[1], midlNod[2]);
4452 else if(nbSame==1) {
4453 // ---> 2d order pyramid of 13 nodes
4454 int apex = iSameNode;
4455 int i0 = ( apex + 1 ) % nbCorners;
4456 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4460 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4461 nextNod[i0], nextNod[i1], prevNod[apex],
4462 prevNod[i01], midlNod[i0],
4463 nextNod[i01], midlNod[i1],
4464 prevNod[i1a], prevNod[i0a],
4465 nextNod[i0a], nextNod[i1a]);
4467 else if(nbSame==2) {
4468 // ---> 2d order tetrahedron of 10 nodes
4469 int n1 = iNotSameNode;
4470 int n2 = ( n1 + 1 ) % nbCorners;
4471 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4475 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4476 prevNod[n12], prevNod[n23], prevNod[n31],
4477 midlNod[n1], nextNod[n12], nextNod[n31]);
4481 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4483 if ( nbDouble != 4 ) break;
4484 // ---> hexahedron with 20 nodes
4485 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4486 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4487 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4488 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4489 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4491 else if(nbSame==1) {
4492 // ---> pyramid + pentahedron - can not be created since it is needed
4493 // additional middle node at the center of face
4494 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4497 else if( nbSame == 2 ) {
4498 if ( nbDouble != 2 ) break;
4499 // ---> 2d order Pentahedron with 15 nodes
4501 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4502 // iBeforeSame is same too
4509 // iAfterSame is same too
4519 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4520 prevNod[n4], prevNod[n5], nextNod[n5],
4521 prevNod[n12], midlNod[n2], nextNod[n12],
4522 prevNod[n45], midlNod[n5], nextNod[n45],
4523 prevNod[n14], prevNod[n25], nextNod[n25]);
4527 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4529 if( nbSame == 0 && nbDouble == 9 ) {
4530 // ---> tri-quadratic hexahedron with 27 nodes
4531 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4532 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4533 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4534 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4535 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4536 prevNod[8], // bottom center
4537 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4538 nextNod[8], // top center
4539 midlNod[8]);// elem center
4547 case SMDSEntity_Polygon: { // sweep POLYGON
4549 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4550 // ---> hexagonal prism
4551 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4552 prevNod[3], prevNod[4], prevNod[5],
4553 nextNod[0], nextNod[1], nextNod[2],
4554 nextNod[3], nextNod[4], nextNod[5]);
4558 case SMDSEntity_Ball:
4563 } // switch ( baseType )
4566 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4568 if ( baseType != SMDSEntity_Polygon )
4570 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4571 SMDS_MeshCell::applyInterlace( ind, prevNod );
4572 SMDS_MeshCell::applyInterlace( ind, nextNod );
4573 SMDS_MeshCell::applyInterlace( ind, midlNod );
4574 SMDS_MeshCell::applyInterlace( ind, itNN );
4575 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4576 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4578 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4579 vector<int> quantities (nbNodes + 2);
4580 polyedre_nodes.clear();
4584 for (int inode = 0; inode < nbNodes; inode++)
4585 polyedre_nodes.push_back( prevNod[inode] );
4586 quantities.push_back( nbNodes );
4589 polyedre_nodes.push_back( nextNod[0] );
4590 for (int inode = nbNodes; inode-1; --inode )
4591 polyedre_nodes.push_back( nextNod[inode-1] );
4592 quantities.push_back( nbNodes );
4600 const int iQuad = elem->IsQuadratic();
4601 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4603 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4604 int inextface = (iface+1+iQuad) % nbNodes;
4605 int imid = (iface+1) % nbNodes;
4606 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4607 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4608 polyedre_nodes.push_back( prevNod[iface] ); // 1
4609 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4611 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4612 polyedre_nodes.push_back( nextNod[iface] ); // 2
4614 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4615 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4617 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4618 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4620 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4621 if ( nbFaceNodes > 2 )
4622 quantities.push_back( nbFaceNodes );
4623 else // degenerated face
4624 polyedre_nodes.resize( prevNbNodes );
4626 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4628 } // try to create a polyherdal prism
4631 newElems.push_back( aNewElem );
4632 myLastCreatedElems.push_back(aNewElem);
4633 srcElements.push_back( elem );
4636 // set new prev nodes
4637 for ( iNode = 0; iNode < nbNodes; iNode++ )
4638 prevNod[ iNode ] = nextNod[ iNode ];
4643 //=======================================================================
4645 * \brief Create 1D and 2D elements around swept elements
4646 * \param mapNewNodes - source nodes and ones generated from them
4647 * \param newElemsMap - source elements and ones generated from them
4648 * \param elemNewNodesMap - nodes generated from each node of each element
4649 * \param elemSet - all swept elements
4650 * \param nbSteps - number of sweeping steps
4651 * \param srcElements - to append elem for each generated element
4653 //=======================================================================
4655 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4656 TTElemOfElemListMap & newElemsMap,
4657 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4658 TIDSortedElemSet& elemSet,
4660 SMESH_SequenceOfElemPtr& srcElements)
4662 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4663 SMESHDS_Mesh* aMesh = GetMeshDS();
4665 // Find nodes belonging to only one initial element - sweep them into edges.
4667 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4668 for ( ; nList != mapNewNodes.end(); nList++ )
4670 const SMDS_MeshNode* node =
4671 static_cast<const SMDS_MeshNode*>( nList->first );
4672 if ( newElemsMap.count( node ))
4673 continue; // node was extruded into edge
4674 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4675 int nbInitElems = 0;
4676 const SMDS_MeshElement* el = 0;
4677 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4678 while ( eIt->more() && nbInitElems < 2 ) {
4679 const SMDS_MeshElement* e = eIt->next();
4680 SMDSAbs_ElementType type = e->GetType();
4681 if ( type == SMDSAbs_Volume ||
4685 if ( type > highType ) {
4692 if ( nbInitElems == 1 ) {
4693 bool NotCreateEdge = el && el->IsMediumNode(node);
4694 if(!NotCreateEdge) {
4695 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4696 list<const SMDS_MeshElement*> newEdges;
4697 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4702 // Make a ceiling for each element ie an equal element of last new nodes.
4703 // Find free links of faces - make edges and sweep them into faces.
4705 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4707 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4708 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4709 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4711 const SMDS_MeshElement* elem = itElem->first;
4712 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4714 if(itElem->second.size()==0) continue;
4716 const bool isQuadratic = elem->IsQuadratic();
4718 if ( elem->GetType() == SMDSAbs_Edge ) {
4719 // create a ceiling edge
4720 if ( !isQuadratic ) {
4721 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4722 vecNewNodes[ 1 ]->second.back())) {
4723 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4724 vecNewNodes[ 1 ]->second.back()));
4725 srcElements.push_back( elem );
4729 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4730 vecNewNodes[ 1 ]->second.back(),
4731 vecNewNodes[ 2 ]->second.back())) {
4732 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4733 vecNewNodes[ 1 ]->second.back(),
4734 vecNewNodes[ 2 ]->second.back()));
4735 srcElements.push_back( elem );
4739 if ( elem->GetType() != SMDSAbs_Face )
4742 bool hasFreeLinks = false;
4744 TIDSortedElemSet avoidSet;
4745 avoidSet.insert( elem );
4747 set<const SMDS_MeshNode*> aFaceLastNodes;
4748 int iNode, nbNodes = vecNewNodes.size();
4749 if ( !isQuadratic ) {
4750 // loop on the face nodes
4751 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4752 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4753 // look for free links of the face
4754 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4755 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4756 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4757 // check if a link n1-n2 is free
4758 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4759 hasFreeLinks = true;
4760 // make a new edge and a ceiling for a new edge
4761 const SMDS_MeshElement* edge;
4762 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4763 myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4764 srcElements.push_back( myLastCreatedElems.back() );
4766 n1 = vecNewNodes[ iNode ]->second.back();
4767 n2 = vecNewNodes[ iNext ]->second.back();
4768 if ( !aMesh->FindEdge( n1, n2 )) {
4769 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4770 srcElements.push_back( edge );
4775 else { // elem is quadratic face
4776 int nbn = nbNodes/2;
4777 for ( iNode = 0; iNode < nbn; iNode++ ) {
4778 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4779 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4780 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4781 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4782 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4783 // check if a link is free
4784 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4785 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4786 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4787 hasFreeLinks = true;
4788 // make an edge and a ceiling for a new edge
4790 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4791 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4792 srcElements.push_back( elem );
4794 n1 = vecNewNodes[ iNode ]->second.back();
4795 n2 = vecNewNodes[ iNext ]->second.back();
4796 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4797 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4798 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4799 srcElements.push_back( elem );
4803 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4804 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4808 // sweep free links into faces
4810 if ( hasFreeLinks ) {
4811 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4812 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4814 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4815 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4816 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4817 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4818 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4820 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4821 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4822 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4824 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4825 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4826 std::advance( v, volNb );
4827 // find indices of free faces of a volume and their source edges
4828 list< int > freeInd;
4829 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4830 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4831 int iF, nbF = vTool.NbFaces();
4832 for ( iF = 0; iF < nbF; iF ++ ) {
4833 if ( vTool.IsFreeFace( iF ) &&
4834 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4835 initNodeSet != faceNodeSet) // except an initial face
4837 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4839 if ( faceNodeSet == initNodeSetNoCenter )
4841 freeInd.push_back( iF );
4842 // find source edge of a free face iF
4843 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4844 vector<const SMDS_MeshNode*>::iterator lastCommom;
4845 commonNodes.resize( nbNodes, 0 );
4846 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4847 initNodeSet.begin(), initNodeSet.end(),
4848 commonNodes.begin());
4849 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4850 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4852 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4854 if ( !srcEdges.back() )
4856 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4857 << iF << " of volume #" << vTool.ID() << endl;
4862 if ( freeInd.empty() )
4865 // create wall faces for all steps;
4866 // if such a face has been already created by sweep of edge,
4867 // assure that its orientation is OK
4868 for ( int iStep = 0; iStep < nbSteps; iStep++ )
4870 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4871 vTool.SetExternalNormal();
4872 const int nextShift = vTool.IsForward() ? +1 : -1;
4873 list< int >::iterator ind = freeInd.begin();
4874 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4875 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4877 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4878 int nbn = vTool.NbFaceNodes( *ind );
4879 const SMDS_MeshElement * f = 0;
4880 if ( nbn == 3 ) ///// triangle
4882 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4884 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4886 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4888 nodes[ 1 + nextShift ] };
4890 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4892 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4896 else if ( nbn == 4 ) ///// quadrangle
4898 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4900 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4902 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4903 nodes[ 2 ], nodes[ 2+nextShift ] };
4905 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4907 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4908 newOrder[ 2 ], newOrder[ 3 ]));
4911 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4913 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4915 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4917 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4919 nodes[2 + 2*nextShift],
4920 nodes[3 - 2*nextShift],
4922 nodes[3 + 2*nextShift]};
4924 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4926 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4934 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4936 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4937 nodes[1], nodes[3], nodes[5], nodes[7] );
4939 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4941 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4942 nodes[4 - 2*nextShift],
4944 nodes[4 + 2*nextShift],
4946 nodes[5 - 2*nextShift],
4948 nodes[5 + 2*nextShift] };
4950 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4952 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4953 newOrder[ 2 ], newOrder[ 3 ],
4954 newOrder[ 4 ], newOrder[ 5 ],
4955 newOrder[ 6 ], newOrder[ 7 ]));
4958 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4960 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4961 SMDSAbs_Face, /*noMedium=*/false);
4963 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4965 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4966 nodes[4 - 2*nextShift],
4968 nodes[4 + 2*nextShift],
4970 nodes[5 - 2*nextShift],
4972 nodes[5 + 2*nextShift],
4975 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4977 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4978 newOrder[ 2 ], newOrder[ 3 ],
4979 newOrder[ 4 ], newOrder[ 5 ],
4980 newOrder[ 6 ], newOrder[ 7 ],
4984 else //////// polygon
4986 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4987 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4989 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4991 if ( !vTool.IsForward() )
4992 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4994 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4996 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5000 while ( srcElements.size() < myLastCreatedElems.size() )
5001 srcElements.push_back( *srcEdge );
5003 } // loop on free faces
5005 // go to the next volume
5007 while ( iVol++ < nbVolumesByStep ) v++;
5010 } // loop on volumes of one step
5011 } // sweep free links into faces
5013 // Make a ceiling face with a normal external to a volume
5015 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5016 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5017 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5019 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5020 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5021 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5025 lastVol.SetExternalNormal();
5026 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5027 const int nbn = lastVol.NbFaceNodes( iF );
5028 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5029 if ( !hasFreeLinks ||
5030 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5032 const vector<int>& interlace =
5033 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5034 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5036 AddElement( nodeVec, anyFace.Init( elem ));
5038 while ( srcElements.size() < myLastCreatedElems.size() )
5039 srcElements.push_back( elem );
5042 } // loop on swept elements
5045 //=======================================================================
5046 //function : RotationSweep
5048 //=======================================================================
5050 SMESH_MeshEditor::PGroupIDs
5051 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5052 const gp_Ax1& theAxis,
5053 const double theAngle,
5054 const int theNbSteps,
5055 const double theTol,
5056 const bool theMakeGroups,
5057 const bool theMakeWalls)
5061 setElemsFirst( theElemSets );
5062 myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5063 myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5065 // source elements for each generated one
5066 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5067 srcElems.reserve( theElemSets[0].size() );
5068 srcNodes.reserve( theElemSets[1].size() );
5071 aTrsf.SetRotation( theAxis, theAngle );
5073 aTrsf2.SetRotation( theAxis, theAngle/2. );
5075 gp_Lin aLine( theAxis );
5076 double aSqTol = theTol * theTol;
5078 SMESHDS_Mesh* aMesh = GetMeshDS();
5080 TNodeOfNodeListMap mapNewNodes;
5081 TElemOfVecOfNnlmiMap mapElemNewNodes;
5082 TTElemOfElemListMap newElemsMap;
5084 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5085 myMesh->NbFaces(ORDER_QUADRATIC) +
5086 myMesh->NbVolumes(ORDER_QUADRATIC) );
5087 // loop on theElemSets
5088 TIDSortedElemSet::iterator itElem;
5089 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5091 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5092 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5093 const SMDS_MeshElement* elem = *itElem;
5094 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5096 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5097 newNodesItVec.reserve( elem->NbNodes() );
5099 // loop on elem nodes
5100 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5101 while ( itN->more() )
5103 const SMDS_MeshNode* node = cast2Node( itN->next() );
5105 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5107 aXYZ.Coord( coord[0], coord[1], coord[2] );
5108 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5110 // check if a node has been already sweeped
5111 TNodeOfNodeListMapItr nIt =
5112 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5113 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5114 if ( listNewNodes.empty() )
5116 // check if we are to create medium nodes between corner ones
5117 bool needMediumNodes = false;
5118 if ( isQuadraticMesh )
5120 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5121 while (it->more() && !needMediumNodes )
5123 const SMDS_MeshElement* invElem = it->next();
5124 if ( invElem != elem && !theElems.count( invElem )) continue;
5125 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5126 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5127 needMediumNodes = true;
5132 const SMDS_MeshNode * newNode = node;
5133 for ( int i = 0; i < theNbSteps; i++ ) {
5135 if ( needMediumNodes ) // create a medium node
5137 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5138 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5139 myLastCreatedNodes.push_back(newNode);
5140 srcNodes.push_back( node );
5141 listNewNodes.push_back( newNode );
5142 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5145 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5147 // create a corner node
5148 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5149 myLastCreatedNodes.push_back(newNode);
5150 srcNodes.push_back( node );
5151 listNewNodes.push_back( newNode );
5154 listNewNodes.push_back( newNode );
5155 // if ( needMediumNodes )
5156 // listNewNodes.push_back( newNode );
5160 newNodesItVec.push_back( nIt );
5162 // make new elements
5163 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5168 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5170 PGroupIDs newGroupIDs;
5171 if ( theMakeGroups )
5172 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5177 //=======================================================================
5178 //function : ExtrusParam
5179 //purpose : standard construction
5180 //=======================================================================
5182 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5183 const int theNbSteps,
5184 const std::list<double>& theScales,
5185 const std::list<double>& theAngles,
5186 const gp_XYZ* theBasePoint,
5188 const double theTolerance):
5190 myBaseP( Precision::Infinite(), 0, 0 ),
5191 myFlags( theFlags ),
5192 myTolerance( theTolerance ),
5193 myElemsToUse( NULL )
5195 mySteps = new TColStd_HSequenceOfReal;
5196 const double stepSize = theStep.Magnitude();
5197 for (int i=1; i<=theNbSteps; i++ )
5198 mySteps->Append( stepSize );
5200 if ( !theScales.empty() )
5202 if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5203 linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5205 // add medium scales
5206 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5207 myScales.reserve( theNbSteps * 2 );
5208 myScales.push_back( 0.5 * ( *s1 + 1. ));
5209 myScales.push_back( *s1 );
5210 for ( ; s2 != theScales.end(); s1 = s2++ )
5212 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5213 myScales.push_back( *s2 );
5217 if ( !theAngles.empty() )
5219 std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5220 if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5221 linearAngleVariation( theNbSteps, angles );
5223 // accumulate angles
5226 std::list<double>::iterator a1 = angles.begin(), a2;
5227 for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5232 while ( nbAngles++ < theNbSteps )
5233 angles.push_back( angles.back() );
5235 // add medium angles
5236 a2 = angles.begin(), a1 = a2++;
5237 myAngles.push_back( 0.5 * *a1 );
5238 myAngles.push_back( *a1 );
5239 for ( ; a2 != angles.end(); a1 = a2++ )
5241 myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5242 myAngles.push_back( *a2 );
5248 myBaseP = *theBasePoint;
5251 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5252 ( theTolerance > 0 ))
5254 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5258 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5262 //=======================================================================
5263 //function : ExtrusParam
5264 //purpose : steps are given explicitly
5265 //=======================================================================
5267 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5268 Handle(TColStd_HSequenceOfReal) theSteps,
5270 const double theTolerance):
5272 mySteps( theSteps ),
5273 myFlags( theFlags ),
5274 myTolerance( theTolerance ),
5275 myElemsToUse( NULL )
5277 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5278 ( theTolerance > 0 ))
5280 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5284 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5288 //=======================================================================
5289 //function : ExtrusParam
5290 //purpose : for extrusion by normal
5291 //=======================================================================
5293 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5294 const int theNbSteps,
5298 mySteps( new TColStd_HSequenceOfReal ),
5299 myFlags( theFlags ),
5301 myElemsToUse( NULL )
5303 for (int i = 0; i < theNbSteps; i++ )
5304 mySteps->Append( theStepSize );
5308 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5312 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5316 //=======================================================================
5317 //function : ExtrusParam
5318 //purpose : for extrusion along path
5319 //=======================================================================
5321 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5322 const gp_Pnt* theBasePoint,
5323 const std::list<double>& theScales,
5324 const bool theMakeGroups )
5325 : myBaseP( Precision::Infinite(), 0, 0 ),
5326 myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5327 myPathPoints( thePoints )
5331 myBaseP = theBasePoint->XYZ();
5334 if ( !theScales.empty() )
5336 // add medium scales
5337 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5338 myScales.reserve( thePoints.size() * 2 );
5339 myScales.push_back( 0.5 * ( 1. + *s1 ));
5340 myScales.push_back( *s1 );
5341 for ( ; s2 != theScales.end(); s1 = s2++ )
5343 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5344 myScales.push_back( *s2 );
5348 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5351 //=======================================================================
5352 //function : ExtrusParam::SetElementsToUse
5353 //purpose : stores elements to use for extrusion by normal, depending on
5354 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5355 // define myBaseP for scaling
5356 //=======================================================================
5358 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5359 const TIDSortedElemSet& nodes )
5361 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5363 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5365 myBaseP.SetCoord( 0.,0.,0. );
5366 TIDSortedElemSet newNodes;
5368 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5369 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5371 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5372 TIDSortedElemSet::const_iterator itElem = elements.begin();
5373 for ( ; itElem != elements.end(); itElem++ )
5375 const SMDS_MeshElement* elem = *itElem;
5376 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5377 while ( itN->more() ) {
5378 const SMDS_MeshElement* node = itN->next();
5379 if ( newNodes.insert( node ).second )
5380 myBaseP += SMESH_NodeXYZ( node );
5384 myBaseP /= newNodes.size();
5388 //=======================================================================
5389 //function : ExtrusParam::beginStepIter
5390 //purpose : prepare iteration on steps
5391 //=======================================================================
5393 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5395 myWithMediumNodes = withMediumNodes;
5399 //=======================================================================
5400 //function : ExtrusParam::moreSteps
5401 //purpose : are there more steps?
5402 //=======================================================================
5404 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5406 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5408 //=======================================================================
5409 //function : ExtrusParam::nextStep
5410 //purpose : returns the next step
5411 //=======================================================================
5413 double SMESH_MeshEditor::ExtrusParam::nextStep()
5416 if ( !myCurSteps.empty() )
5418 res = myCurSteps.back();
5419 myCurSteps.pop_back();
5421 else if ( myNextStep <= mySteps->Length() )
5423 myCurSteps.push_back( mySteps->Value( myNextStep ));
5425 if ( myWithMediumNodes )
5427 myCurSteps.back() /= 2.;
5428 myCurSteps.push_back( myCurSteps.back() );
5435 //=======================================================================
5436 //function : ExtrusParam::makeNodesByDir
5437 //purpose : create nodes for standard extrusion
5438 //=======================================================================
5440 int SMESH_MeshEditor::ExtrusParam::
5441 makeNodesByDir( SMESHDS_Mesh* mesh,
5442 const SMDS_MeshNode* srcNode,
5443 std::list<const SMDS_MeshNode*> & newNodes,
5444 const bool makeMediumNodes)
5446 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5449 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5451 p += myDir.XYZ() * nextStep();
5452 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5453 newNodes.push_back( newNode );
5456 if ( !myScales.empty() || !myAngles.empty() )
5458 gp_XYZ center = myBaseP;
5459 gp_Ax1 ratationAxis( center, myDir );
5462 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5463 size_t i = !makeMediumNodes;
5464 for ( beginStepIter( makeMediumNodes );
5466 ++nIt, i += 1 + !makeMediumNodes )
5468 center += myDir.XYZ() * nextStep();
5470 gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5472 if ( i < myScales.size() )
5474 xyz = ( myScales[i] * ( xyz - center )) + center;
5477 if ( !myAngles.empty() )
5479 rotation.SetRotation( ratationAxis, myAngles[i] );
5480 rotation.Transforms( xyz );
5484 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5492 //=======================================================================
5493 //function : ExtrusParam::makeNodesByDirAndSew
5494 //purpose : create nodes for standard extrusion with sewing
5495 //=======================================================================
5497 int SMESH_MeshEditor::ExtrusParam::
5498 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5499 const SMDS_MeshNode* srcNode,
5500 std::list<const SMDS_MeshNode*> & newNodes,
5501 const bool makeMediumNodes)
5503 gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5506 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5508 P1 += myDir.XYZ() * nextStep();
5510 // try to search in sequence of existing nodes
5511 // if myNodes.size()>0 we 'nave to use given sequence
5512 // else - use all nodes of mesh
5513 const SMDS_MeshNode * node = 0;
5514 if ( myNodes.Length() > 0 )
5516 for ( int i = 1; i <= myNodes.Length(); i++ )
5518 SMESH_NodeXYZ P2 = myNodes.Value(i);
5519 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5521 node = myNodes.Value(i);
5528 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5531 SMESH_NodeXYZ P2 = itn->next();
5532 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5541 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5543 newNodes.push_back( node );
5550 //=======================================================================
5551 //function : ExtrusParam::makeNodesByNormal2D
5552 //purpose : create nodes for extrusion using normals of faces
5553 //=======================================================================
5555 int SMESH_MeshEditor::ExtrusParam::
5556 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5557 const SMDS_MeshNode* srcNode,
5558 std::list<const SMDS_MeshNode*> & newNodes,
5559 const bool makeMediumNodes)
5561 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5563 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5565 // get normals to faces sharing srcNode
5566 vector< gp_XYZ > norms, baryCenters;
5567 gp_XYZ norm, avgNorm( 0,0,0 );
5568 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5569 while ( faceIt->more() )
5571 const SMDS_MeshElement* face = faceIt->next();
5572 if ( myElemsToUse && !myElemsToUse->count( face ))
5574 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5576 norms.push_back( norm );
5578 if ( !alongAvgNorm )
5582 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5583 bc += SMESH_NodeXYZ( nIt->next() );
5584 baryCenters.push_back( bc / nbN );
5589 if ( norms.empty() ) return 0;
5591 double normSize = avgNorm.Modulus();
5592 if ( normSize < std::numeric_limits<double>::min() )
5595 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5598 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5601 avgNorm /= normSize;
5604 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5607 double stepSize = nextStep();
5609 if ( norms.size() > 1 )
5611 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5613 // translate plane of a face
5614 baryCenters[ iF ] += norms[ iF ] * stepSize;
5616 // find point of intersection of the face plane located at baryCenters[ iF ]
5617 // and avgNorm located at pNew
5618 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5619 double dot = ( norms[ iF ] * avgNorm );
5620 if ( dot < std::numeric_limits<double>::min() )
5621 dot = stepSize * 1e-3;
5622 double step = -( norms[ iF ] * pNew + d ) / dot;
5623 pNew += step * avgNorm;
5628 pNew += stepSize * avgNorm;
5632 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5633 newNodes.push_back( newNode );
5638 //=======================================================================
5639 //function : ExtrusParam::makeNodesByNormal1D
5640 //purpose : create nodes for extrusion using normals of edges
5641 //=======================================================================
5643 int SMESH_MeshEditor::ExtrusParam::
5644 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5645 const SMDS_MeshNode* srcNode,
5646 std::list<const SMDS_MeshNode*> & newNodes,
5647 const bool makeMediumNodes)
5649 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5653 //=======================================================================
5654 //function : ExtrusParam::makeNodesAlongTrack
5655 //purpose : create nodes for extrusion along path
5656 //=======================================================================
5658 int SMESH_MeshEditor::ExtrusParam::
5659 makeNodesAlongTrack( SMESHDS_Mesh* mesh,
5660 const SMDS_MeshNode* srcNode,
5661 std::list<const SMDS_MeshNode*> & newNodes,
5662 const bool makeMediumNodes)
5664 const Standard_Real aTolAng=1.e-4;
5666 gp_Pnt aV0x = myBaseP;
5667 gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
5669 const PathPoint& aPP0 = myPathPoints[0];
5670 gp_Pnt aP0x = aPP0.myPnt;
5671 gp_Dir aDT0x= aPP0.myTgt;
5673 std::vector< gp_Pnt > centers;
5674 centers.reserve( NbSteps() * 2 );
5676 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5678 for ( size_t j = 1; j < myPathPoints.size(); ++j )
5680 const PathPoint& aPP = myPathPoints[j];
5681 const gp_Pnt& aP1x = aPP.myPnt;
5682 const gp_Dir& aDT1x = aPP.myTgt;
5685 gp_Vec aV01x( aP0x, aP1x );
5686 aTrsf.SetTranslation( aV01x );
5687 gp_Pnt aV1x = aV0x.Transformed( aTrsf );
5688 gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
5690 // rotation 1 [ T1,T0 ]
5691 Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
5692 if ( fabs( aAngleT1T0 ) > aTolAng )
5694 gp_Dir aDT1T0 = aDT1x ^ aDT0x;
5695 aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
5697 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5701 if ( aPP.myAngle != 0. )
5703 aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
5704 aPN1 = aPN1.Transformed( aTrsfRot );
5708 if ( makeMediumNodes )
5710 // create additional node
5711 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
5712 const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
5713 newNodes.push_back( newNode );
5716 const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5717 newNodes.push_back( newNode );
5719 centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
5720 centers.push_back( aV1x );
5729 if ( !myScales.empty() )
5732 std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
5733 for ( size_t i = !makeMediumNodes;
5734 i < myScales.size() && node != newNodes.end();
5735 i += ( 1 + !makeMediumNodes ), ++node )
5737 aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
5738 gp_Pnt aN = SMESH_NodeXYZ( *node );
5739 gp_Pnt aP = aN.Transformed( aTrsfScale );
5740 mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
5744 return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
5747 //=======================================================================
5748 //function : ExtrusionSweep
5750 //=======================================================================
5752 SMESH_MeshEditor::PGroupIDs
5753 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5754 const gp_Vec& theStep,
5755 const int theNbSteps,
5756 TTElemOfElemListMap& newElemsMap,
5758 const double theTolerance)
5760 std::list<double> dummy;
5761 ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
5762 theFlags, theTolerance );
5763 return ExtrusionSweep( theElems, aParams, newElemsMap );
5769 //=======================================================================
5770 //function : getOriFactor
5771 //purpose : Return -1 or 1 depending on if order of given nodes corresponds to
5772 // edge curve orientation
5773 //=======================================================================
5775 double getOriFactor( const TopoDS_Edge& edge,
5776 const SMDS_MeshNode* n1,
5777 const SMDS_MeshNode* n2,
5778 SMESH_MesherHelper& helper)
5780 double u1 = helper.GetNodeU( edge, n1, n2 );
5781 double u2 = helper.GetNodeU( edge, n2, n1 );
5782 return u1 < u2 ? 1. : -1.;
5786 //=======================================================================
5787 //function : ExtrusionSweep
5789 //=======================================================================
5791 SMESH_MeshEditor::PGroupIDs
5792 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5793 ExtrusParam& theParams,
5794 TTElemOfElemListMap& newElemsMap)
5798 setElemsFirst( theElemSets );
5799 myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5800 myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5802 // source elements for each generated one
5803 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5804 srcElems.reserve( theElemSets[0].size() );
5805 srcNodes.reserve( theElemSets[1].size() );
5807 const int nbSteps = theParams.NbSteps();
5808 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5810 TNodeOfNodeListMap mapNewNodes;
5811 TElemOfVecOfNnlmiMap mapElemNewNodes;
5813 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5814 myMesh->NbFaces(ORDER_QUADRATIC) +
5815 myMesh->NbVolumes(ORDER_QUADRATIC) );
5817 TIDSortedElemSet::iterator itElem;
5818 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5820 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5821 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5823 // check element type
5824 const SMDS_MeshElement* elem = *itElem;
5825 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5828 const size_t nbNodes = elem->NbNodes();
5829 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5830 newNodesItVec.reserve( nbNodes );
5832 // loop on elem nodes
5833 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
5834 while ( itN->more() )
5836 // check if a node has been already sweeped
5837 const SMDS_MeshNode* node = itN->next();
5838 TNodeOfNodeListMap::iterator nIt =
5839 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5840 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5841 if ( listNewNodes.empty() )
5845 // check if we are to create medium nodes between corner ones
5846 bool needMediumNodes = false;
5847 if ( isQuadraticMesh )
5849 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5850 while (it->more() && !needMediumNodes )
5852 const SMDS_MeshElement* invElem = it->next();
5853 if ( invElem != elem && !theElems.count( invElem )) continue;
5854 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5855 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5856 needMediumNodes = true;
5859 // create nodes for all steps
5860 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5862 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5863 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5865 myLastCreatedNodes.push_back( *newNodesIt );
5866 srcNodes.push_back( node );
5871 if ( theParams.ToMakeBoundary() )
5873 GetMeshDS()->Modified();
5874 throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
5876 break; // newNodesItVec will be shorter than nbNodes
5879 newNodesItVec.push_back( nIt );
5881 // make new elements
5882 if ( newNodesItVec.size() == nbNodes )
5883 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5887 if ( theParams.ToMakeBoundary() ) {
5888 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5890 PGroupIDs newGroupIDs;
5891 if ( theParams.ToMakeGroups() )
5892 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5897 //=======================================================================
5898 //function : ExtrusionAlongTrack
5900 //=======================================================================
5901 SMESH_MeshEditor::Extrusion_Error
5902 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5903 SMESH_Mesh* theTrackMesh,
5904 SMDS_ElemIteratorPtr theTrackIterator,
5905 const SMDS_MeshNode* theN1,
5906 std::list<double>& theAngles,
5907 const bool theAngleVariation,
5908 std::list<double>& theScales,
5909 const bool theScaleVariation,
5910 const gp_Pnt* theRefPoint,
5911 const bool theMakeGroups)
5916 if ( theElements[0].empty() && theElements[1].empty() )
5917 return EXTR_NO_ELEMENTS;
5919 ASSERT( theTrackMesh );
5920 if ( ! theTrackIterator || !theTrackIterator->more() )
5921 return EXTR_NO_ELEMENTS;
5923 // 2. Get ordered nodes
5924 SMESH_MeshAlgos::TElemGroupVector branchEdges;
5925 SMESH_MeshAlgos::TNodeGroupVector branchNods;
5926 SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
5927 if ( branchEdges.empty() )
5928 return EXTR_PATH_NOT_EDGE;
5930 if ( branchEdges.size() > 1 )
5931 return EXTR_BAD_PATH_SHAPE;
5933 std::vector< const SMDS_MeshNode* >& pathNodes = branchNods[0];
5934 std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
5935 if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
5936 return EXTR_BAD_STARTING_NODE;
5938 if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
5940 // add medium nodes to pathNodes
5941 std::vector< const SMDS_MeshNode* > pathNodes2;
5942 std::vector< const SMDS_MeshElement* > pathEdges2;
5943 pathNodes2.reserve( pathNodes.size() * 2 );
5944 pathEdges2.reserve( pathEdges.size() * 2 );
5945 for ( size_t i = 0; i < pathEdges.size(); ++i )
5947 pathNodes2.push_back( pathNodes[i] );
5948 pathEdges2.push_back( pathEdges[i] );
5949 if ( pathEdges[i]->IsQuadratic() )
5951 pathNodes2.push_back( pathEdges[i]->GetNode(2) );
5952 pathEdges2.push_back( pathEdges[i] );
5955 pathNodes2.push_back( pathNodes.back() );
5956 pathEdges.swap( pathEdges2 );
5957 pathNodes.swap( pathNodes2 );
5960 // 3. Get path data at pathNodes
5962 std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
5964 if ( theAngleVariation )
5965 linearAngleVariation( points.size()-1, theAngles );
5966 if ( theScaleVariation )
5967 linearScaleVariation( points.size()-1, theScales );
5969 theAngles.push_front( 0 ); // for the 1st point that is not transformed
5970 std::list<double>::iterator angle = theAngles.begin();
5972 SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
5974 std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
5975 std::map< int, double >::iterator id2factor;
5976 SMESH_MesherHelper pathHelper( *theTrackMesh );
5977 gp_Pnt p; gp_Vec tangent;
5978 const double tol2 = gp::Resolution() * gp::Resolution();
5980 for ( size_t i = 0; i < pathNodes.size(); ++i )
5982 ExtrusParam::PathPoint & point = points[ i ];
5984 point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
5986 if ( angle != theAngles.end() )
5987 point.myAngle = *angle++;
5989 tangent.SetCoord( 0,0,0 );
5990 const int shapeID = pathNodes[ i ]->GetShapeID();
5991 const TopoDS_Shape& shape = pathMeshDS->IndexToShape( shapeID );
5992 TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
5993 switch ( shapeType )
5997 TopoDS_Edge edge = TopoDS::Edge( shape );
5998 id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
5999 if ( id2factor->second == 0 )
6001 if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6002 else id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6004 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6005 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6006 curve->D1( u, p, tangent );
6007 tangent *= id2factor->second;
6013 PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6014 while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6016 int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6017 for ( int di = -1; di <= 0; ++di )
6020 if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6022 TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6023 id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6024 if ( id2factor->second == 0 )
6027 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6029 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6031 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6032 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6034 curve->D1( u, p, du );
6035 double size2 = du.SquareMagnitude();
6036 if ( du.SquareMagnitude() > tol2 )
6038 tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6050 for ( int di = -1; di <= 1; di += 2 )
6053 if ( j < pathNodes.size() )
6055 gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6056 double size2 = dir.SquareMagnitude();
6058 tangent += dir.Divided( Sqrt( size2 )) * di;
6062 } // switch ( shapeType )
6064 if ( tangent.SquareMagnitude() < tol2 )
6065 return EXTR_CANT_GET_TANGENT;
6067 point.myTgt = tangent;
6069 } // loop on pathNodes
6072 ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6073 TTElemOfElemListMap newElemsMap;
6075 ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6080 //=======================================================================
6081 //function : linearAngleVariation
6082 //purpose : spread values over nbSteps
6083 //=======================================================================
6085 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6086 list<double>& Angles)
6088 int nbAngles = Angles.size();
6089 if( nbSteps > nbAngles && nbAngles > 0 )
6091 vector<double> theAngles(nbAngles);
6092 theAngles.assign( Angles.begin(), Angles.end() );
6095 double rAn2St = double( nbAngles ) / double( nbSteps );
6096 double angPrev = 0, angle;
6097 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6099 double angCur = rAn2St * ( iSt+1 );
6100 double angCurFloor = floor( angCur );
6101 double angPrevFloor = floor( angPrev );
6102 if ( angPrevFloor == angCurFloor )
6103 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6105 int iP = int( angPrevFloor );
6106 double angPrevCeil = ceil(angPrev);
6107 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6109 int iC = int( angCurFloor );
6110 if ( iC < nbAngles )
6111 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6113 iP = int( angPrevCeil );
6115 angle += theAngles[ iC ];
6117 res.push_back(angle);
6124 //=======================================================================
6125 //function : linearScaleVariation
6126 //purpose : spread values over nbSteps
6127 //=======================================================================
6129 void SMESH_MeshEditor::linearScaleVariation(const int theNbSteps,
6130 std::list<double>& theScales)
6132 int nbScales = theScales.size();
6133 std::vector<double> myScales;
6134 myScales.reserve( theNbSteps );
6135 std::list<double>::const_iterator scale = theScales.begin();
6136 double prevScale = 1.0;
6137 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6139 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6140 int stDelta = Max( 1, iStep - myScales.size());
6141 double scDelta = ( *scale - prevScale ) / stDelta;
6142 for ( int iStep = 0; iStep < stDelta; ++iStep )
6144 myScales.push_back( prevScale + scDelta );
6145 prevScale = myScales.back();
6149 theScales.assign( myScales.begin(), myScales.end() );
6152 //================================================================================
6154 * \brief Move or copy theElements applying theTrsf to their nodes
6155 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6156 * \param theTrsf - transformation to apply
6157 * \param theCopy - if true, create translated copies of theElems
6158 * \param theMakeGroups - if true and theCopy, create translated groups
6159 * \param theTargetMesh - mesh to copy translated elements into
6160 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6162 //================================================================================
6164 SMESH_MeshEditor::PGroupIDs
6165 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6166 const gp_Trsf& theTrsf,
6168 const bool theMakeGroups,
6169 SMESH_Mesh* theTargetMesh)
6172 myLastCreatedElems.reserve( theElems.size() );
6174 bool needReverse = false;
6175 string groupPostfix;
6176 switch ( theTrsf.Form() ) {
6179 groupPostfix = "mirrored";
6182 groupPostfix = "mirrored";
6186 groupPostfix = "mirrored";
6189 groupPostfix = "rotated";
6191 case gp_Translation:
6192 groupPostfix = "translated";
6195 groupPostfix = "scaled";
6197 case gp_CompoundTrsf: // different scale by axis
6198 groupPostfix = "scaled";
6201 needReverse = false;
6202 groupPostfix = "transformed";
6205 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6206 SMESHDS_Mesh* aMesh = GetMeshDS();
6208 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6209 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6210 SMESH_MeshEditor::ElemFeatures elemType;
6212 // map old node to new one
6213 TNodeNodeMap nodeMap;
6215 // elements sharing moved nodes; those of them which have all
6216 // nodes mirrored but are not in theElems are to be reversed
6217 TIDSortedElemSet inverseElemSet;
6219 // source elements for each generated one
6220 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6222 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6223 TIDSortedElemSet orphanNode;
6225 if ( theElems.empty() ) // transform the whole mesh
6228 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6229 while ( eIt->more() ) theElems.insert( eIt->next() );
6231 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6232 while ( nIt->more() )
6234 const SMDS_MeshNode* node = nIt->next();
6235 if ( node->NbInverseElements() == 0)
6236 orphanNode.insert( node );
6240 // loop on elements to transform nodes : first orphan nodes then elems
6241 TIDSortedElemSet::iterator itElem;
6242 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6243 for (int i=0; i<2; i++)
6244 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6246 const SMDS_MeshElement* elem = *itElem;
6250 // loop on elem nodes
6252 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6253 while ( itN->more() )
6255 const SMDS_MeshNode* node = cast2Node( itN->next() );
6256 // check if a node has been already transformed
6257 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6258 nodeMap.insert( make_pair ( node, node ));
6259 if ( !n2n_isnew.second )
6262 node->GetXYZ( coord );
6263 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6264 if ( theTargetMesh ) {
6265 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6266 n2n_isnew.first->second = newNode;
6267 myLastCreatedNodes.push_back(newNode);
6268 srcNodes.push_back( node );
6270 else if ( theCopy ) {
6271 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6272 n2n_isnew.first->second = newNode;
6273 myLastCreatedNodes.push_back(newNode);
6274 srcNodes.push_back( node );
6277 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6278 // node position on shape becomes invalid
6279 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6280 ( SMDS_SpacePosition::originSpacePosition() );
6283 // keep inverse elements
6284 if ( !theCopy && !theTargetMesh && needReverse ) {
6285 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6286 while ( invElemIt->more() ) {
6287 const SMDS_MeshElement* iel = invElemIt->next();
6288 inverseElemSet.insert( iel );
6292 } // loop on elems in { &orphanNode, &theElems };
6294 // either create new elements or reverse mirrored ones
6295 if ( !theCopy && !needReverse && !theTargetMesh )
6298 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6300 // Replicate or reverse elements
6302 std::vector<int> iForw;
6303 vector<const SMDS_MeshNode*> nodes;
6304 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6306 const SMDS_MeshElement* elem = *itElem;
6307 if ( !elem ) continue;
6309 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6310 size_t nbNodes = elem->NbNodes();
6311 if ( geomType == SMDSGeom_NONE ) continue; // node
6313 nodes.resize( nbNodes );
6315 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6317 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6321 bool allTransformed = true;
6322 int nbFaces = aPolyedre->NbFaces();
6323 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6325 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6326 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6328 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6329 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6330 if ( nodeMapIt == nodeMap.end() )
6331 allTransformed = false; // not all nodes transformed
6333 nodes.push_back((*nodeMapIt).second);
6335 if ( needReverse && allTransformed )
6336 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6338 if ( !allTransformed )
6339 continue; // not all nodes transformed
6341 else // ----------------------- the rest element types
6343 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6344 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6345 const vector<int>& i = needReverse ? iRev : iForw;
6347 // find transformed nodes
6349 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6350 while ( itN->more() ) {
6351 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6352 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6353 if ( nodeMapIt == nodeMap.end() )
6354 break; // not all nodes transformed
6355 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6357 if ( iNode != nbNodes )
6358 continue; // not all nodes transformed
6362 // copy in this or a new mesh
6363 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6364 srcElems.push_back( elem );
6367 // reverse element as it was reversed by transformation
6369 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6372 } // loop on elements
6374 if ( editor && editor != this )
6375 myLastCreatedElems.swap( editor->myLastCreatedElems );
6377 PGroupIDs newGroupIDs;
6379 if ( ( theMakeGroups && theCopy ) ||
6380 ( theMakeGroups && theTargetMesh ) )
6381 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6386 //================================================================================
6388 * \brief Make an offset mesh from a source 2D mesh
6389 * \param [in] theElements - source faces
6390 * \param [in] theValue - offset value
6391 * \param [out] theTgtMesh - a mesh to add offset elements to
6392 * \param [in] theMakeGroups - to generate groups
6393 * \return PGroupIDs - IDs of created groups. NULL means failure
6395 //================================================================================
6397 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6398 const double theValue,
6399 SMESH_Mesh* theTgtMesh,
6400 const bool theMakeGroups,
6401 const bool theCopyElements,
6402 const bool theFixSelfIntersection)
6404 SMESHDS_Mesh* meshDS = GetMeshDS();
6405 SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6406 SMESH_MeshEditor tgtEditor( theTgtMesh );
6408 SMDS_ElemIteratorPtr eIt;
6409 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6410 else eIt = SMESHUtils::elemSetIterator( theElements );
6412 SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6413 SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6414 std::unique_ptr< SMDS_Mesh > offsetMesh
6415 ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6416 theFixSelfIntersection,
6417 new2OldFaces, new2OldNodes ));
6418 if ( offsetMesh->NbElements() == 0 )
6419 return PGroupIDs(); // MakeOffset() failed
6422 if ( theTgtMesh == myMesh && !theCopyElements )
6424 // clear the source elements
6425 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6426 else eIt = SMESHUtils::elemSetIterator( theElements );
6427 while ( eIt->more() )
6428 meshDS->RemoveFreeElement( eIt->next(), 0 );
6431 // offsetMesh->Modified();
6432 // offsetMesh->CompactMesh(); // make IDs start from 1
6434 // source elements for each generated one
6435 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6436 srcElems.reserve( new2OldFaces.size() );
6437 srcNodes.reserve( new2OldNodes.size() );
6440 myLastCreatedElems.reserve( new2OldFaces.size() );
6441 myLastCreatedNodes.reserve( new2OldNodes.size() );
6443 // copy offsetMesh to theTgtMesh
6445 int idShift = meshDS->MaxNodeID();
6446 for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6447 if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6450 if ( n->NbInverseElements() > 0 )
6453 const SMDS_MeshNode* n2 =
6454 tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6455 myLastCreatedNodes.push_back( n2 );
6456 srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6460 ElemFeatures elemType;
6461 for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6462 if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6465 elemType.myNodes.clear();
6466 for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6468 const SMDS_MeshNode* n2 = nIt->next();
6469 elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6471 tgtEditor.AddElement( elemType.myNodes, elemType );
6472 srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6475 myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6477 PGroupIDs newGroupIDs;
6478 if ( theMakeGroups )
6479 newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6481 newGroupIDs.reset( new std::list< int > );
6486 //=======================================================================
6488 * \brief Create groups of elements made during transformation
6489 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6490 * \param elemGens - elements making corresponding myLastCreatedElems
6491 * \param postfix - to push_back to names of new groups
6492 * \param targetMesh - mesh to create groups in
6493 * \param topPresent - is there are "top" elements that are created by sweeping
6495 //=======================================================================
6497 SMESH_MeshEditor::PGroupIDs
6498 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6499 const SMESH_SequenceOfElemPtr& elemGens,
6500 const std::string& postfix,
6501 SMESH_Mesh* targetMesh,
6502 const bool topPresent)
6504 PGroupIDs newGroupIDs( new list<int> );
6505 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6507 // Sort existing groups by types and collect their names
6509 // containers to store an old group and generated new ones;
6510 // 1st new group is for result elems of different type than a source one;
6511 // 2nd new group is for same type result elems ("top" group at extrusion)
6513 using boost::make_tuple;
6514 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6515 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6516 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6518 set< string > groupNames;
6520 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6521 if ( !groupIt->more() ) return newGroupIDs;
6523 int newGroupID = mesh->GetGroupIds().back()+1;
6524 while ( groupIt->more() )
6526 SMESH_Group * group = groupIt->next();
6527 if ( !group ) continue;
6528 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6529 if ( !groupDS || groupDS->IsEmpty() ) continue;
6530 groupNames.insert ( group->GetName() );
6531 groupDS->SetStoreName( group->GetName() );
6532 const SMDSAbs_ElementType type = groupDS->GetType();
6533 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6534 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6535 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6536 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6539 // Loop on nodes and elements to add them in new groups
6541 vector< const SMDS_MeshElement* > resultElems;
6542 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6544 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6545 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6546 if ( gens.size() != elems.size() )
6547 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6549 // loop on created elements
6550 for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6552 const SMDS_MeshElement* sourceElem = gens[ iElem ];
6553 if ( !sourceElem ) {
6554 MESSAGE("generateGroups(): NULL source element");
6557 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6558 if ( groupsOldNew.empty() ) { // no groups of this type at all
6559 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6560 ++iElem; // skip all elements made by sourceElem
6563 // collect all elements made by the iElem-th sourceElem
6564 resultElems.clear();
6565 if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6566 if ( resElem != sourceElem )
6567 resultElems.push_back( resElem );
6568 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6569 if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6570 if ( resElem != sourceElem )
6571 resultElems.push_back( resElem );
6573 const SMDS_MeshElement* topElem = 0;
6574 if ( isNodes ) // there must be a top element
6576 topElem = resultElems.back();
6577 resultElems.pop_back();
6581 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6582 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6583 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6585 topElem = *resElemIt;
6586 *resElemIt = 0; // erase *resElemIt
6590 // add resultElems to groups originted from ones the sourceElem belongs to
6591 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6592 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6594 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6595 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6597 // fill in a new group
6598 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6599 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6600 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6602 newGroup.Add( *resElemIt );
6604 // fill a "top" group
6607 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6608 newTopGroup.Add( topElem );
6612 } // loop on created elements
6613 }// loop on nodes and elements
6615 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6617 list<int> topGrouIds;
6618 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6620 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6621 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6622 orderedOldNewGroups[i]->get<2>() };
6623 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6625 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6626 if ( newGroupDS->IsEmpty() )
6628 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6633 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6636 const bool isTop = ( topPresent &&
6637 newGroupDS->GetType() == oldGroupDS->GetType() &&
6640 string name = oldGroupDS->GetStoreName();
6641 { // remove trailing whitespaces (issue 22599)
6642 size_t size = name.size();
6643 while ( size > 1 && isspace( name[ size-1 ]))
6645 if ( size != name.size() )
6647 name.resize( size );
6648 oldGroupDS->SetStoreName( name.c_str() );
6651 if ( !targetMesh ) {
6652 string suffix = ( isTop ? "top": postfix.c_str() );
6656 while ( !groupNames.insert( name ).second ) // name exists
6657 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6662 newGroupDS->SetStoreName( name.c_str() );
6664 // make a SMESH_Groups
6665 mesh->AddGroup( newGroupDS );
6667 topGrouIds.push_back( newGroupDS->GetID() );
6669 newGroupIDs->push_back( newGroupDS->GetID() );
6673 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6678 //================================================================================
6680 * * \brief Return list of group of nodes close to each other within theTolerance
6681 * * Search among theNodes or in the whole mesh if theNodes is empty using
6682 * * an Octree algorithm
6683 * \param [in,out] theNodes - the nodes to treat
6684 * \param [in] theTolerance - the tolerance
6685 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
6686 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
6687 * corner and medium nodes in separate groups
6689 //================================================================================
6691 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6692 const double theTolerance,
6693 TListOfListOfNodes & theGroupsOfNodes,
6694 bool theSeparateCornersAndMedium)
6698 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
6699 myMesh->NbFaces ( ORDER_QUADRATIC ) +
6700 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
6701 theSeparateCornersAndMedium = false;
6703 TIDSortedNodeSet& corners = theNodes;
6704 TIDSortedNodeSet medium;
6706 if ( theNodes.empty() ) // get all nodes in the mesh
6708 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
6709 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
6710 if ( theSeparateCornersAndMedium )
6711 while ( nIt->more() )
6713 const SMDS_MeshNode* n = nIt->next();
6714 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
6715 nodeSet->insert( nodeSet->end(), n );
6718 while ( nIt->more() )
6719 theNodes.insert( theNodes.end(), nIt->next() );
6721 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
6723 TIDSortedNodeSet::iterator nIt = corners.begin();
6724 while ( nIt != corners.end() )
6725 if ( SMESH_MesherHelper::IsMedium( *nIt ))
6727 medium.insert( medium.end(), *nIt );
6728 corners.erase( nIt++ );
6736 if ( !corners.empty() )
6737 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
6738 if ( !medium.empty() )
6739 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
6742 //=======================================================================
6743 //function : SimplifyFace
6744 //purpose : split a chain of nodes into several closed chains
6745 //=======================================================================
6747 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6748 vector<const SMDS_MeshNode *>& poly_nodes,
6749 vector<int>& quantities) const
6751 int nbNodes = faceNodes.size();
6752 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
6756 size_t prevNbQuant = quantities.size();
6758 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
6759 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
6760 map< const SMDS_MeshNode*, int >::iterator nInd;
6762 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
6763 simpleNodes.push_back( faceNodes[0] );
6764 for ( int iCur = 1; iCur < nbNodes; iCur++ )
6766 if ( faceNodes[ iCur ] != simpleNodes.back() )
6768 int index = simpleNodes.size();
6769 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
6770 int prevIndex = nInd->second;
6771 if ( prevIndex < index )
6774 int loopLen = index - prevIndex;
6777 // store the sub-loop
6778 quantities.push_back( loopLen );
6779 for ( int i = prevIndex; i < index; i++ )
6780 poly_nodes.push_back( simpleNodes[ i ]);
6782 simpleNodes.resize( prevIndex+1 );
6786 simpleNodes.push_back( faceNodes[ iCur ]);
6791 if ( simpleNodes.size() > 2 )
6793 quantities.push_back( simpleNodes.size() );
6794 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
6797 return quantities.size() - prevNbQuant;
6800 //=======================================================================
6801 //function : MergeNodes
6802 //purpose : In each group, the cdr of nodes are substituted by the first one
6804 //=======================================================================
6806 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
6807 const bool theAvoidMakingHoles)
6811 SMESHDS_Mesh* mesh = GetMeshDS();
6813 TNodeNodeMap nodeNodeMap; // node to replace - new node
6814 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6815 list< int > rmElemIds, rmNodeIds;
6816 vector< ElemFeatures > newElemDefs;
6818 // Fill nodeNodeMap and elems
6820 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6821 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
6823 list<const SMDS_MeshNode*>& nodes = *grIt;
6824 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6825 const SMDS_MeshNode* nToKeep = *nIt;
6826 for ( ++nIt; nIt != nodes.end(); nIt++ )
6828 const SMDS_MeshNode* nToRemove = *nIt;
6829 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
6830 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6831 while ( invElemIt->more() ) {
6832 const SMDS_MeshElement* elem = invElemIt->next();
6838 // Apply recursive replacements (BUG 0020185)
6839 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
6840 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
6842 const SMDS_MeshNode* nToKeep = nnIt->second;
6843 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
6844 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
6846 nToKeep = nnIt_i->second;
6847 nnIt->second = nToKeep;
6848 nnIt_i = nodeNodeMap.find( nToKeep );
6852 if ( theAvoidMakingHoles )
6854 // find elements whose topology changes
6856 vector<const SMDS_MeshElement*> pbElems;
6857 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6858 for ( ; eIt != elems.end(); ++eIt )
6860 const SMDS_MeshElement* elem = *eIt;
6861 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6862 while ( itN->more() )
6864 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6865 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6866 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
6868 // several nodes of elem stick
6869 pbElems.push_back( elem );
6874 // exclude from merge nodes causing spoiling element
6875 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
6877 bool nodesExcluded = false;
6878 for ( size_t i = 0; i < pbElems.size(); ++i )
6880 size_t prevNbMergeNodes = nodeNodeMap.size();
6881 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
6882 prevNbMergeNodes < nodeNodeMap.size() )
6883 nodesExcluded = true;
6885 if ( !nodesExcluded )
6890 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
6892 const SMDS_MeshNode* nToRemove = nnIt->first;
6893 const SMDS_MeshNode* nToKeep = nnIt->second;
6894 if ( nToRemove != nToKeep )
6896 rmNodeIds.push_back( nToRemove->GetID() );
6897 AddToSameGroups( nToKeep, nToRemove, mesh );
6898 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
6899 // w/o creating node in place of merged ones.
6900 SMDS_PositionPtr pos = nToRemove->GetPosition();
6901 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6902 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6903 sm->SetIsAlwaysComputed( true );
6907 // Change element nodes or remove an element
6909 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6910 for ( ; eIt != elems.end(); eIt++ )
6912 const SMDS_MeshElement* elem = *eIt;
6913 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
6915 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
6917 rmElemIds.push_back( elem->GetID() );
6919 for ( size_t i = 0; i < newElemDefs.size(); ++i )
6921 if ( i > 0 || !mesh->ChangeElementNodes( elem,
6922 & newElemDefs[i].myNodes[0],
6923 newElemDefs[i].myNodes.size() ))
6927 newElemDefs[i].SetID( elem->GetID() );
6928 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6929 if ( !keepElem ) rmElemIds.pop_back();
6933 newElemDefs[i].SetID( -1 );
6935 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
6936 if ( sm && newElem )
6937 sm->AddElement( newElem );
6938 if ( elem != newElem )
6939 ReplaceElemInGroups( elem, newElem, mesh );
6944 // Remove bad elements, then equal nodes (order important)
6945 Remove( rmElemIds, /*isNodes=*/false );
6946 Remove( rmNodeIds, /*isNodes=*/true );
6951 //=======================================================================
6952 //function : applyMerge
6953 //purpose : Compute new connectivity of an element after merging nodes
6954 // \param [in] elems - the element
6955 // \param [out] newElemDefs - definition(s) of result element(s)
6956 // \param [inout] nodeNodeMap - nodes to merge
6957 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
6958 // after merging (but not degenerated), removes nodes causing
6959 // the invalidity from \a nodeNodeMap.
6960 // \return bool - true if the element should be removed
6961 //=======================================================================
6963 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
6964 vector< ElemFeatures >& newElemDefs,
6965 TNodeNodeMap& nodeNodeMap,
6966 const bool avoidMakingHoles )
6968 bool toRemove = false; // to remove elem
6969 int nbResElems = 1; // nb new elements
6971 newElemDefs.resize(nbResElems);
6972 newElemDefs[0].Init( elem );
6973 newElemDefs[0].myNodes.clear();
6975 set<const SMDS_MeshNode*> nodeSet;
6976 vector< const SMDS_MeshNode*> curNodes;
6977 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
6980 const int nbNodes = elem->NbNodes();
6981 SMDSAbs_EntityType entity = elem->GetEntityType();
6983 curNodes.resize( nbNodes );
6984 uniqueNodes.resize( nbNodes );
6985 iRepl.resize( nbNodes );
6986 int iUnique = 0, iCur = 0, nbRepl = 0;
6988 // Get new seq of nodes
6990 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6991 while ( itN->more() )
6993 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6995 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6996 if ( nnIt != nodeNodeMap.end() ) {
6999 curNodes[ iCur ] = n;
7000 bool isUnique = nodeSet.insert( n ).second;
7002 uniqueNodes[ iUnique++ ] = n;
7004 iRepl[ nbRepl++ ] = iCur;
7008 // Analyse element topology after replacement
7010 int nbUniqueNodes = nodeSet.size();
7011 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7016 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7018 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7019 int nbCorners = nbNodes / 2;
7020 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7022 int iNext = ( iCur + 1 ) % nbCorners;
7023 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7025 int iMedium = iCur + nbCorners;
7026 vector< const SMDS_MeshNode* >::iterator i =
7027 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7029 curNodes[ iMedium ]);
7030 if ( i != uniqueNodes.end() )
7033 for ( ; i+1 != uniqueNodes.end(); ++i )
7042 case SMDSEntity_Polygon:
7043 case SMDSEntity_Quad_Polygon: // Polygon
7045 ElemFeatures* elemType = & newElemDefs[0];
7046 const bool isQuad = elemType->myIsQuad;
7048 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7049 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7051 // a polygon can divide into several elements
7052 vector<const SMDS_MeshNode *> polygons_nodes;
7053 vector<int> quantities;
7054 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7055 newElemDefs.resize( nbResElems );
7056 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7058 ElemFeatures* elemType = & newElemDefs[iface];
7059 if ( iface ) elemType->Init( elem );
7061 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7062 int nbNewNodes = quantities[iface];
7063 face_nodes.assign( polygons_nodes.begin() + inode,
7064 polygons_nodes.begin() + inode + nbNewNodes );
7065 inode += nbNewNodes;
7066 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7068 bool isValid = ( nbNewNodes % 2 == 0 );
7069 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7070 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7071 elemType->SetQuad( isValid );
7072 if ( isValid ) // put medium nodes after corners
7073 SMDS_MeshCell::applyInterlaceRev
7074 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7075 nbNewNodes ), face_nodes );
7077 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7079 nbUniqueNodes = newElemDefs[0].myNodes.size();
7083 case SMDSEntity_Polyhedra: // Polyhedral volume
7085 if ( nbUniqueNodes >= 4 )
7087 // each face has to be analyzed in order to check volume validity
7088 if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7090 int nbFaces = aPolyedre->NbFaces();
7092 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7093 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7094 vector<const SMDS_MeshNode *> faceNodes;
7098 for (int iface = 1; iface <= nbFaces; iface++)
7100 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7101 faceNodes.resize( nbFaceNodes );
7102 for (int inode = 1; inode <= nbFaceNodes; inode++)
7104 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7105 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7106 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7107 faceNode = (*nnIt).second;
7108 faceNodes[inode - 1] = faceNode;
7110 SimplifyFace(faceNodes, poly_nodes, quantities);
7113 if ( quantities.size() > 3 )
7115 // TODO: remove coincident faces
7117 nbUniqueNodes = newElemDefs[0].myNodes.size();
7125 // TODO not all the possible cases are solved. Find something more generic?
7126 case SMDSEntity_Edge: //////// EDGE
7127 case SMDSEntity_Triangle: //// TRIANGLE
7128 case SMDSEntity_Quad_Triangle:
7129 case SMDSEntity_Tetra:
7130 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7134 case SMDSEntity_Quad_Edge:
7138 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7140 if ( nbUniqueNodes < 3 )
7142 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7143 toRemove = true; // opposite nodes stick
7148 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7157 if ( nbUniqueNodes == 6 &&
7159 ( nbRepl == 1 || iRepl[1] >= 4 ))
7165 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7174 if ( nbUniqueNodes == 7 &&
7176 ( nbRepl == 1 || iRepl[1] != 8 ))
7182 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7184 if ( nbUniqueNodes == 4 ) {
7185 // ---------------------------------> tetrahedron
7186 if ( curNodes[3] == curNodes[4] &&
7187 curNodes[3] == curNodes[5] ) {
7191 else if ( curNodes[0] == curNodes[1] &&
7192 curNodes[0] == curNodes[2] ) {
7193 // bottom nodes stick: set a top before
7194 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7195 uniqueNodes[ 0 ] = curNodes [ 5 ];
7196 uniqueNodes[ 1 ] = curNodes [ 4 ];
7197 uniqueNodes[ 2 ] = curNodes [ 3 ];
7200 else if (( curNodes[0] == curNodes[3] ) +
7201 ( curNodes[1] == curNodes[4] ) +
7202 ( curNodes[2] == curNodes[5] ) == 2 ) {
7203 // a lateral face turns into a line
7207 else if ( nbUniqueNodes == 5 ) {
7208 // PENTAHEDRON --------------------> pyramid
7209 if ( curNodes[0] == curNodes[3] )
7211 uniqueNodes[ 0 ] = curNodes[ 1 ];
7212 uniqueNodes[ 1 ] = curNodes[ 4 ];
7213 uniqueNodes[ 2 ] = curNodes[ 5 ];
7214 uniqueNodes[ 3 ] = curNodes[ 2 ];
7215 uniqueNodes[ 4 ] = curNodes[ 0 ];
7218 if ( curNodes[1] == curNodes[4] )
7220 uniqueNodes[ 0 ] = curNodes[ 0 ];
7221 uniqueNodes[ 1 ] = curNodes[ 2 ];
7222 uniqueNodes[ 2 ] = curNodes[ 5 ];
7223 uniqueNodes[ 3 ] = curNodes[ 3 ];
7224 uniqueNodes[ 4 ] = curNodes[ 1 ];
7227 if ( curNodes[2] == curNodes[5] )
7229 uniqueNodes[ 0 ] = curNodes[ 0 ];
7230 uniqueNodes[ 1 ] = curNodes[ 3 ];
7231 uniqueNodes[ 2 ] = curNodes[ 4 ];
7232 uniqueNodes[ 3 ] = curNodes[ 1 ];
7233 uniqueNodes[ 4 ] = curNodes[ 2 ];
7239 case SMDSEntity_Hexa:
7241 //////////////////////////////////// HEXAHEDRON
7242 SMDS_VolumeTool hexa (elem);
7243 hexa.SetExternalNormal();
7244 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7245 //////////////////////// HEX ---> tetrahedron
7246 for ( int iFace = 0; iFace < 6; iFace++ ) {
7247 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7248 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7249 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7250 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7251 // one face turns into a point ...
7252 int pickInd = ind[ 0 ];
7253 int iOppFace = hexa.GetOppFaceIndex( iFace );
7254 ind = hexa.GetFaceNodesIndices( iOppFace );
7256 uniqueNodes.clear();
7257 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7258 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7261 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7263 if ( nbStick == 1 ) {
7264 // ... and the opposite one - into a triangle.
7266 uniqueNodes.push_back( curNodes[ pickInd ]);
7273 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7274 //////////////////////// HEX ---> prism
7275 int nbTria = 0, iTria[3];
7276 const int *ind; // indices of face nodes
7277 // look for triangular faces
7278 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7279 ind = hexa.GetFaceNodesIndices( iFace );
7280 TIDSortedNodeSet faceNodes;
7281 for ( iCur = 0; iCur < 4; iCur++ )
7282 faceNodes.insert( curNodes[ind[iCur]] );
7283 if ( faceNodes.size() == 3 )
7284 iTria[ nbTria++ ] = iFace;
7286 // check if triangles are opposite
7287 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7289 // set nodes of the bottom triangle
7290 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7292 for ( iCur = 0; iCur < 4; iCur++ )
7293 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7294 indB.push_back( ind[iCur] );
7295 if ( !hexa.IsForward() )
7296 std::swap( indB[0], indB[2] );
7297 for ( iCur = 0; iCur < 3; iCur++ )
7298 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7299 // set nodes of the top triangle
7300 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7301 for ( iCur = 0; iCur < 3; ++iCur )
7302 for ( int j = 0; j < 4; ++j )
7303 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7305 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7312 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7313 //////////////////// HEXAHEDRON ---> pyramid
7314 for ( int iFace = 0; iFace < 6; iFace++ ) {
7315 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7316 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7317 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7318 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7319 // one face turns into a point ...
7320 int iOppFace = hexa.GetOppFaceIndex( iFace );
7321 ind = hexa.GetFaceNodesIndices( iOppFace );
7322 uniqueNodes.clear();
7323 for ( iCur = 0; iCur < 4; iCur++ ) {
7324 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7327 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7329 if ( uniqueNodes.size() == 4 ) {
7330 // ... and the opposite one is a quadrangle
7332 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7333 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7341 if ( toRemove && nbUniqueNodes > 4 ) {
7342 ////////////////// HEXAHEDRON ---> polyhedron
7343 hexa.SetExternalNormal();
7344 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7345 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7346 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7347 quantities.reserve( 6 ); quantities.clear();
7348 for ( int iFace = 0; iFace < 6; iFace++ )
7350 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7351 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7352 curNodes[ind[1]] == curNodes[ind[3]] )
7355 break; // opposite nodes stick
7358 for ( iCur = 0; iCur < 4; iCur++ )
7360 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7361 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7363 if ( nodeSet.size() < 3 )
7364 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7366 quantities.push_back( nodeSet.size() );
7368 if ( quantities.size() >= 4 )
7371 nbUniqueNodes = poly_nodes.size();
7372 newElemDefs[0].SetPoly(true);
7376 } // case HEXAHEDRON
7381 } // switch ( entity )
7383 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7385 // erase from nodeNodeMap nodes whose merge spoils elem
7386 vector< const SMDS_MeshNode* > noMergeNodes;
7387 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7388 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7389 nodeNodeMap.erase( noMergeNodes[i] );
7392 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7394 uniqueNodes.resize( nbUniqueNodes );
7396 if ( !toRemove && nbResElems == 0 )
7399 newElemDefs.resize( nbResElems );
7405 // ========================================================
7406 // class : ComparableElement
7407 // purpose : allow comparing elements basing on their nodes
7408 // ========================================================
7410 class ComparableElement : public boost::container::flat_set< int >
7412 typedef boost::container::flat_set< int > int_set;
7414 const SMDS_MeshElement* myElem;
7416 mutable int myGroupID;
7420 ComparableElement( const SMDS_MeshElement* theElem ):
7421 myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7423 this->reserve( theElem->NbNodes() );
7424 for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7426 int id = nodeIt->next()->GetID();
7432 const SMDS_MeshElement* GetElem() const { return myElem; }
7434 int& GroupID() const { return myGroupID; }
7435 //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7437 ComparableElement( const ComparableElement& theSource ) // move copy
7439 ComparableElement& src = const_cast< ComparableElement& >( theSource );
7440 (int_set&) (*this ) = boost::move( src );
7441 myElem = src.myElem;
7442 mySumID = src.mySumID;
7443 myGroupID = src.myGroupID;
7446 static int HashCode(const ComparableElement& se, int limit )
7448 return ::HashCode( se.mySumID, limit );
7450 static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7452 return ( se1 == se2 );
7457 //=======================================================================
7458 //function : FindEqualElements
7459 //purpose : Return list of group of elements built on the same nodes.
7460 // Search among theElements or in the whole mesh if theElements is empty
7461 //=======================================================================
7463 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet & theElements,
7464 TListOfListOfElementsID & theGroupsOfElementsID )
7468 SMDS_ElemIteratorPtr elemIt;
7469 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7470 else elemIt = SMESHUtils::elemSetIterator( theElements );
7472 typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7473 typedef std::list<int> TGroupOfElems;
7474 TMapOfElements mapOfElements;
7475 std::vector< TGroupOfElems > arrayOfGroups;
7476 TGroupOfElems groupOfElems;
7478 while ( elemIt->more() )
7480 const SMDS_MeshElement* curElem = elemIt->next();
7481 ComparableElement compElem = curElem;
7483 const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7484 if ( elemInSet.GetElem() != curElem ) // coincident elem
7486 int& iG = elemInSet.GroupID();
7489 iG = arrayOfGroups.size();
7490 arrayOfGroups.push_back( groupOfElems );
7491 arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7493 arrayOfGroups[ iG ].push_back( curElem->GetID() );
7497 groupOfElems.clear();
7498 std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7499 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7501 if ( groupIt->size() > 1 ) {
7502 //groupOfElems.sort(); -- theElements are sorted already
7503 theGroupsOfElementsID.emplace_back( *groupIt );
7508 //=======================================================================
7509 //function : MergeElements
7510 //purpose : In each given group, substitute all elements by the first one.
7511 //=======================================================================
7513 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7517 typedef list<int> TListOfIDs;
7518 TListOfIDs rmElemIds; // IDs of elems to remove
7520 SMESHDS_Mesh* aMesh = GetMeshDS();
7522 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7523 while ( groupsIt != theGroupsOfElementsID.end() ) {
7524 TListOfIDs& aGroupOfElemID = *groupsIt;
7525 aGroupOfElemID.sort();
7526 int elemIDToKeep = aGroupOfElemID.front();
7527 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7528 aGroupOfElemID.pop_front();
7529 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7530 while ( idIt != aGroupOfElemID.end() ) {
7531 int elemIDToRemove = *idIt;
7532 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7533 // add the kept element in groups of removed one (PAL15188)
7534 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7535 rmElemIds.push_back( elemIDToRemove );
7541 Remove( rmElemIds, false );
7544 //=======================================================================
7545 //function : MergeEqualElements
7546 //purpose : Remove all but one of elements built on the same nodes.
7547 //=======================================================================
7549 void SMESH_MeshEditor::MergeEqualElements()
7551 TIDSortedElemSet aMeshElements; /* empty input ==
7552 to merge equal elements in the whole mesh */
7553 TListOfListOfElementsID aGroupsOfElementsID;
7554 FindEqualElements( aMeshElements, aGroupsOfElementsID );
7555 MergeElements( aGroupsOfElementsID );
7558 //=======================================================================
7559 //function : findAdjacentFace
7561 //=======================================================================
7563 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7564 const SMDS_MeshNode* n2,
7565 const SMDS_MeshElement* elem)
7567 TIDSortedElemSet elemSet, avoidSet;
7569 avoidSet.insert ( elem );
7570 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7573 //=======================================================================
7574 //function : findSegment
7575 //purpose : Return a mesh segment by two nodes one of which can be medium
7576 //=======================================================================
7578 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7579 const SMDS_MeshNode* n2)
7581 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7582 while ( it->more() )
7584 const SMDS_MeshElement* seg = it->next();
7585 if ( seg->GetNodeIndex( n2 ) >= 0 )
7591 //=======================================================================
7592 //function : FindFreeBorder
7594 //=======================================================================
7596 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7598 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7599 const SMDS_MeshNode* theSecondNode,
7600 const SMDS_MeshNode* theLastNode,
7601 list< const SMDS_MeshNode* > & theNodes,
7602 list< const SMDS_MeshElement* >& theFaces)
7604 if ( !theFirstNode || !theSecondNode )
7606 // find border face between theFirstNode and theSecondNode
7607 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7611 theFaces.push_back( curElem );
7612 theNodes.push_back( theFirstNode );
7613 theNodes.push_back( theSecondNode );
7615 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7616 //TIDSortedElemSet foundElems;
7617 bool needTheLast = ( theLastNode != 0 );
7619 vector<const SMDS_MeshNode*> nodes;
7621 while ( nStart != theLastNode ) {
7622 if ( nStart == theFirstNode )
7623 return !needTheLast;
7625 // find all free border faces sharing nStart
7627 list< const SMDS_MeshElement* > curElemList;
7628 list< const SMDS_MeshNode* > nStartList;
7629 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7630 while ( invElemIt->more() ) {
7631 const SMDS_MeshElement* e = invElemIt->next();
7632 //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
7635 nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7636 SMDS_MeshElement::iterator() );
7637 nodes.push_back( nodes[ 0 ]);
7640 int iNode = 0, nbNodes = nodes.size() - 1;
7641 for ( iNode = 0; iNode < nbNodes; iNode++ )
7642 if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7643 ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7644 ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
7646 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
7647 curElemList.push_back( e );
7651 // analyse the found
7653 int nbNewBorders = curElemList.size();
7654 if ( nbNewBorders == 0 ) {
7655 // no free border furthermore
7656 return !needTheLast;
7658 else if ( nbNewBorders == 1 ) {
7659 // one more element found
7661 nStart = nStartList.front();
7662 curElem = curElemList.front();
7663 theFaces.push_back( curElem );
7664 theNodes.push_back( nStart );
7667 // several continuations found
7668 list< const SMDS_MeshElement* >::iterator curElemIt;
7669 list< const SMDS_MeshNode* >::iterator nStartIt;
7670 // check if one of them reached the last node
7671 if ( needTheLast ) {
7672 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7673 curElemIt!= curElemList.end();
7674 curElemIt++, nStartIt++ )
7675 if ( *nStartIt == theLastNode ) {
7676 theFaces.push_back( *curElemIt );
7677 theNodes.push_back( *nStartIt );
7681 // find the best free border by the continuations
7682 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7683 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7684 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7685 curElemIt!= curElemList.end();
7686 curElemIt++, nStartIt++ )
7688 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7689 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7690 // find one more free border
7691 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7695 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7696 // choice: clear a worse one
7697 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7698 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7699 contNodes[ iWorse ].clear();
7700 contFaces[ iWorse ].clear();
7703 if ( contNodes[0].empty() && contNodes[1].empty() )
7706 // push_back the best free border
7707 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7708 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7709 //theNodes.pop_back(); // remove nIgnore
7710 theNodes.pop_back(); // remove nStart
7711 //theFaces.pop_back(); // remove curElem
7712 theNodes.splice( theNodes.end(), *cNL );
7713 theFaces.splice( theFaces.end(), *cFL );
7716 } // several continuations found
7717 } // while ( nStart != theLastNode )
7722 //=======================================================================
7723 //function : CheckFreeBorderNodes
7724 //purpose : Return true if the tree nodes are on a free border
7725 //=======================================================================
7727 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7728 const SMDS_MeshNode* theNode2,
7729 const SMDS_MeshNode* theNode3)
7731 list< const SMDS_MeshNode* > nodes;
7732 list< const SMDS_MeshElement* > faces;
7733 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7736 //=======================================================================
7737 //function : SewFreeBorder
7739 //warning : for border-to-side sewing theSideSecondNode is considered as
7740 // the last side node and theSideThirdNode is not used
7741 //=======================================================================
7743 SMESH_MeshEditor::Sew_Error
7744 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7745 const SMDS_MeshNode* theBordSecondNode,
7746 const SMDS_MeshNode* theBordLastNode,
7747 const SMDS_MeshNode* theSideFirstNode,
7748 const SMDS_MeshNode* theSideSecondNode,
7749 const SMDS_MeshNode* theSideThirdNode,
7750 const bool theSideIsFreeBorder,
7751 const bool toCreatePolygons,
7752 const bool toCreatePolyedrs)
7756 Sew_Error aResult = SEW_OK;
7758 // ====================================
7759 // find side nodes and elements
7760 // ====================================
7762 list< const SMDS_MeshNode* > nSide[ 2 ];
7763 list< const SMDS_MeshElement* > eSide[ 2 ];
7764 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7765 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7769 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7770 nSide[0], eSide[0])) {
7771 MESSAGE(" Free Border 1 not found " );
7772 aResult = SEW_BORDER1_NOT_FOUND;
7774 if (theSideIsFreeBorder) {
7777 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7778 nSide[1], eSide[1])) {
7779 MESSAGE(" Free Border 2 not found " );
7780 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7783 if ( aResult != SEW_OK )
7786 if (!theSideIsFreeBorder) {
7790 // -------------------------------------------------------------------------
7792 // 1. If nodes to merge are not coincident, move nodes of the free border
7793 // from the coord sys defined by the direction from the first to last
7794 // nodes of the border to the correspondent sys of the side 2
7795 // 2. On the side 2, find the links most co-directed with the correspondent
7796 // links of the free border
7797 // -------------------------------------------------------------------------
7799 // 1. Since sewing may break if there are volumes to split on the side 2,
7800 // we won't move nodes but just compute new coordinates for them
7801 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7802 TNodeXYZMap nBordXYZ;
7803 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7804 list< const SMDS_MeshNode* >::iterator nBordIt;
7806 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7807 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7808 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7809 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7810 double tol2 = 1.e-8;
7811 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7812 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7813 // Need node movement.
7815 // find X and Z axes to create trsf
7816 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7818 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7820 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7823 gp_Ax3 toBordAx( Pb1, Zb, X );
7824 gp_Ax3 fromSideAx( Ps1, Zs, X );
7825 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7827 gp_Trsf toBordSys, fromSide2Sys;
7828 toBordSys.SetTransformation( toBordAx );
7829 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7830 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7833 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7834 const SMDS_MeshNode* n = *nBordIt;
7835 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7836 toBordSys.Transforms( xyz );
7837 fromSide2Sys.Transforms( xyz );
7838 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7842 // just insert nodes XYZ in the nBordXYZ map
7843 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7844 const SMDS_MeshNode* n = *nBordIt;
7845 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7849 // 2. On the side 2, find the links most co-directed with the correspondent
7850 // links of the free border
7852 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7853 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7854 sideNodes.push_back( theSideFirstNode );
7856 bool hasVolumes = false;
7857 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7858 set<long> foundSideLinkIDs, checkedLinkIDs;
7859 SMDS_VolumeTool volume;
7860 //const SMDS_MeshNode* faceNodes[ 4 ];
7862 const SMDS_MeshNode* sideNode;
7863 const SMDS_MeshElement* sideElem = 0;
7864 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7865 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7866 nBordIt = bordNodes.begin();
7868 // border node position and border link direction to compare with
7869 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7870 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7871 // choose next side node by link direction or by closeness to
7872 // the current border node:
7873 bool searchByDir = ( *nBordIt != theBordLastNode );
7875 // find the next node on the Side 2
7877 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7879 checkedLinkIDs.clear();
7880 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7882 // loop on inverse elements of current node (prevSideNode) on the Side 2
7883 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7884 while ( invElemIt->more() )
7886 const SMDS_MeshElement* elem = invElemIt->next();
7887 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7888 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7889 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7890 bool isVolume = volume.Set( elem );
7891 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7892 if ( isVolume ) // --volume
7894 else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7895 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7896 SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
7897 while ( nIt->more() ) {
7898 nodes[ iNode ] = cast2Node( nIt->next() );
7899 if ( nodes[ iNode++ ] == prevSideNode )
7900 iPrevNode = iNode - 1;
7902 // there are 2 links to check
7907 // loop on links, to be precise, on the second node of links
7908 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7909 const SMDS_MeshNode* n = nodes[ iNode ];
7911 if ( !volume.IsLinked( n, prevSideNode ))
7915 if ( iNode ) // a node before prevSideNode
7916 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7917 else // a node after prevSideNode
7918 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7920 // check if this link was already used
7921 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7922 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7923 if (!isJustChecked &&
7924 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7926 // test a link geometrically
7927 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7928 bool linkIsBetter = false;
7929 double dot = 0.0, dist = 0.0;
7930 if ( searchByDir ) { // choose most co-directed link
7931 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7932 linkIsBetter = ( dot > maxDot );
7934 else { // choose link with the node closest to bordPos
7935 dist = ( nextXYZ - bordPos ).SquareModulus();
7936 linkIsBetter = ( dist < minDist );
7938 if ( linkIsBetter ) {
7947 } // loop on inverse elements of prevSideNode
7950 MESSAGE(" Can't find path by links of the Side 2 ");
7951 return SEW_BAD_SIDE_NODES;
7953 sideNodes.push_back( sideNode );
7954 sideElems.push_back( sideElem );
7955 foundSideLinkIDs.insert ( linkID );
7956 prevSideNode = sideNode;
7958 if ( *nBordIt == theBordLastNode )
7959 searchByDir = false;
7961 // find the next border link to compare with
7962 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7963 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7964 // move to next border node if sideNode is before forward border node (bordPos)
7965 while ( *nBordIt != theBordLastNode && !searchByDir ) {
7966 prevBordNode = *nBordIt;
7968 bordPos = nBordXYZ[ *nBordIt ];
7969 bordDir = bordPos - nBordXYZ[ prevBordNode ];
7970 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7974 while ( sideNode != theSideSecondNode );
7976 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7977 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7978 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7980 } // end nodes search on the side 2
7982 // ============================
7983 // sew the border to the side 2
7984 // ============================
7986 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
7987 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
7989 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
7990 if ( toMergeConformal && toCreatePolygons )
7992 // do not merge quadrangles if polygons are OK (IPAL0052824)
7993 eIt[0] = eSide[0].begin();
7994 eIt[1] = eSide[1].begin();
7995 bool allQuads[2] = { true, true };
7996 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7997 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
7998 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8000 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8003 TListOfListOfNodes nodeGroupsToMerge;
8004 if (( toMergeConformal ) ||
8005 ( theSideIsFreeBorder && !theSideThirdNode )) {
8007 // all nodes are to be merged
8009 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8010 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8011 nIt[0]++, nIt[1]++ )
8013 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8014 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8015 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8020 // insert new nodes into the border and the side to get equal nb of segments
8022 // get normalized parameters of nodes on the borders
8023 vector< double > param[ 2 ];
8024 param[0].resize( maxNbNodes );
8025 param[1].resize( maxNbNodes );
8027 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8028 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8029 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8030 const SMDS_MeshNode* nPrev = *nIt;
8031 double bordLength = 0;
8032 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8033 const SMDS_MeshNode* nCur = *nIt;
8034 gp_XYZ segment (nCur->X() - nPrev->X(),
8035 nCur->Y() - nPrev->Y(),
8036 nCur->Z() - nPrev->Z());
8037 double segmentLen = segment.Modulus();
8038 bordLength += segmentLen;
8039 param[ iBord ][ iNode ] = bordLength;
8042 // normalize within [0,1]
8043 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8044 param[ iBord ][ iNode ] /= bordLength;
8048 // loop on border segments
8049 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8050 int i[ 2 ] = { 0, 0 };
8051 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8052 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8054 // element can be split while iterating on border if it has two edges in the border
8055 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8056 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8058 TElemOfNodeListMap insertMap;
8059 TElemOfNodeListMap::iterator insertMapIt;
8061 // key: elem to insert nodes into
8062 // value: 2 nodes to insert between + nodes to be inserted
8064 bool next[ 2 ] = { false, false };
8066 // find min adjacent segment length after sewing
8067 double nextParam = 10., prevParam = 0;
8068 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8069 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8070 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8071 if ( i[ iBord ] > 0 )
8072 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8074 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8075 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8076 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8078 // choose to insert or to merge nodes
8079 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8080 if ( Abs( du ) <= minSegLen * 0.2 ) {
8083 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8084 const SMDS_MeshNode* n0 = *nIt[0];
8085 const SMDS_MeshNode* n1 = *nIt[1];
8086 nodeGroupsToMerge.back().push_back( n1 );
8087 nodeGroupsToMerge.back().push_back( n0 );
8088 // position of node of the border changes due to merge
8089 param[ 0 ][ i[0] ] += du;
8090 // move n1 for the sake of elem shape evaluation during insertion.
8091 // n1 will be removed by MergeNodes() anyway
8092 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8093 next[0] = next[1] = true;
8098 int intoBord = ( du < 0 ) ? 0 : 1;
8099 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8100 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8101 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8102 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8103 if ( intoBord == 1 ) {
8104 // move node of the border to be on a link of elem of the side
8105 SMESH_NodeXYZ p1( n1 ), p2( n2 );
8106 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8107 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8108 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8110 elemReplaceMapIt = elemReplaceMap.find( elem );
8111 if ( elemReplaceMapIt != elemReplaceMap.end() )
8112 elem = elemReplaceMapIt->second;
8114 insertMapIt = insertMap.find( elem );
8115 bool notFound = ( insertMapIt == insertMap.end() );
8116 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8118 // insert into another link of the same element:
8119 // 1. perform insertion into the other link of the elem
8120 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8121 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8122 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8123 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8124 // 2. perform insertion into the link of adjacent faces
8125 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8126 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8128 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8129 InsertNodesIntoLink( seg, n12, n22, nodeList );
8131 if (toCreatePolyedrs) {
8132 // perform insertion into the links of adjacent volumes
8133 UpdateVolumes(n12, n22, nodeList);
8135 // 3. find an element appeared on n1 and n2 after the insertion
8136 insertMap.erase( insertMapIt );
8137 const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8138 elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8141 if ( notFound || otherLink ) {
8142 // add element and nodes of the side into the insertMap
8143 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8144 (*insertMapIt).second.push_back( n1 );
8145 (*insertMapIt).second.push_back( n2 );
8147 // add node to be inserted into elem
8148 (*insertMapIt).second.push_back( nIns );
8149 next[ 1 - intoBord ] = true;
8152 // go to the next segment
8153 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8154 if ( next[ iBord ] ) {
8155 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8157 nPrev[ iBord ] = *nIt[ iBord ];
8158 nIt[ iBord ]++; i[ iBord ]++;
8162 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8164 // perform insertion of nodes into elements
8166 for (insertMapIt = insertMap.begin();
8167 insertMapIt != insertMap.end();
8170 const SMDS_MeshElement* elem = (*insertMapIt).first;
8171 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8172 if ( nodeList.size() < 3 ) continue;
8173 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8174 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8176 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8178 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8179 InsertNodesIntoLink( seg, n1, n2, nodeList );
8182 if ( !theSideIsFreeBorder ) {
8183 // look for and insert nodes into the faces adjacent to elem
8184 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8185 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8188 if (toCreatePolyedrs) {
8189 // perform insertion into the links of adjacent volumes
8190 UpdateVolumes(n1, n2, nodeList);
8193 } // end: insert new nodes
8195 MergeNodes ( nodeGroupsToMerge );
8198 // Remove coincident segments
8201 TIDSortedElemSet segments;
8202 SMESH_SequenceOfElemPtr newFaces;
8203 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8205 if ( !myLastCreatedElems[i] ) continue;
8206 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8207 segments.insert( segments.end(), myLastCreatedElems[i] );
8209 newFaces.push_back( myLastCreatedElems[i] );
8211 // get segments adjacent to merged nodes
8212 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8213 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8215 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8216 if ( nodes.front()->IsNull() ) continue;
8217 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8218 while ( segIt->more() )
8219 segments.insert( segIt->next() );
8223 TListOfListOfElementsID equalGroups;
8224 if ( !segments.empty() )
8225 FindEqualElements( segments, equalGroups );
8226 if ( !equalGroups.empty() )
8228 // remove from segments those that will be removed
8229 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8230 for ( ; itGroups != equalGroups.end(); ++itGroups )
8232 list< int >& group = *itGroups;
8233 list< int >::iterator id = group.begin();
8234 for ( ++id; id != group.end(); ++id )
8235 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8236 segments.erase( seg );
8238 // remove equal segments
8239 MergeElements( equalGroups );
8241 // restore myLastCreatedElems
8242 myLastCreatedElems = newFaces;
8243 TIDSortedElemSet::iterator seg = segments.begin();
8244 for ( ; seg != segments.end(); ++seg )
8245 myLastCreatedElems.push_back( *seg );
8251 //=======================================================================
8252 //function : InsertNodesIntoLink
8253 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8254 // and theBetweenNode2 and split theElement
8255 //=======================================================================
8257 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8258 const SMDS_MeshNode* theBetweenNode1,
8259 const SMDS_MeshNode* theBetweenNode2,
8260 list<const SMDS_MeshNode*>& theNodesToInsert,
8261 const bool toCreatePoly)
8263 if ( !theElement ) return;
8265 SMESHDS_Mesh *aMesh = GetMeshDS();
8266 vector<const SMDS_MeshElement*> newElems;
8268 if ( theElement->GetType() == SMDSAbs_Edge )
8270 theNodesToInsert.push_front( theBetweenNode1 );
8271 theNodesToInsert.push_back ( theBetweenNode2 );
8272 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8273 const SMDS_MeshNode* n1 = *n;
8274 for ( ++n; n != theNodesToInsert.end(); ++n )
8276 const SMDS_MeshNode* n2 = *n;
8277 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8278 AddToSameGroups( seg, theElement, aMesh );
8280 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8283 theNodesToInsert.pop_front();
8284 theNodesToInsert.pop_back();
8286 if ( theElement->IsQuadratic() ) // add a not split part
8288 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8289 theElement->end_nodes() );
8290 int iOther = 0, nbN = nodes.size();
8291 for ( ; iOther < nbN; ++iOther )
8292 if ( nodes[iOther] != theBetweenNode1 &&
8293 nodes[iOther] != theBetweenNode2 )
8297 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8298 AddToSameGroups( seg, theElement, aMesh );
8300 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8302 else if ( iOther == 2 )
8304 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8305 AddToSameGroups( seg, theElement, aMesh );
8307 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8310 // treat new elements
8311 for ( size_t i = 0; i < newElems.size(); ++i )
8314 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8315 myLastCreatedElems.push_back( newElems[i] );
8317 ReplaceElemInGroups( theElement, newElems, aMesh );
8318 aMesh->RemoveElement( theElement );
8321 } // if ( theElement->GetType() == SMDSAbs_Edge )
8323 const SMDS_MeshElement* theFace = theElement;
8324 if ( theFace->GetType() != SMDSAbs_Face ) return;
8326 // find indices of 2 link nodes and of the rest nodes
8327 int iNode = 0, il1, il2, i3, i4;
8328 il1 = il2 = i3 = i4 = -1;
8329 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8331 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8332 while ( nodeIt->more() ) {
8333 const SMDS_MeshNode* n = nodeIt->next();
8334 if ( n == theBetweenNode1 )
8336 else if ( n == theBetweenNode2 )
8342 nodes[ iNode++ ] = n;
8344 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8347 // arrange link nodes to go one after another regarding the face orientation
8348 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8349 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8354 aNodesToInsert.reverse();
8356 // check that not link nodes of a quadrangles are in good order
8357 int nbFaceNodes = theFace->NbNodes();
8358 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8364 if (toCreatePoly || theFace->IsPoly()) {
8367 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8369 // add nodes of face up to first node of link
8371 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8372 while ( nodeIt->more() && !isFLN ) {
8373 const SMDS_MeshNode* n = nodeIt->next();
8374 poly_nodes[iNode++] = n;
8375 isFLN = ( n == nodes[il1] );
8377 // add nodes to insert
8378 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8379 for (; nIt != aNodesToInsert.end(); nIt++) {
8380 poly_nodes[iNode++] = *nIt;
8382 // add nodes of face starting from last node of link
8383 while ( nodeIt->more() ) {
8384 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8385 poly_nodes[iNode++] = n;
8389 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8392 else if ( !theFace->IsQuadratic() )
8394 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8395 int nbLinkNodes = 2 + aNodesToInsert.size();
8396 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8397 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8398 linkNodes[ 0 ] = nodes[ il1 ];
8399 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8400 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8401 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8402 linkNodes[ iNode++ ] = *nIt;
8404 // decide how to split a quadrangle: compare possible variants
8405 // and choose which of splits to be a quadrangle
8406 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8407 if ( nbFaceNodes == 3 ) {
8408 iBestQuad = nbSplits;
8411 else if ( nbFaceNodes == 4 ) {
8412 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8413 double aBestRate = DBL_MAX;
8414 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8416 double aBadRate = 0;
8417 // evaluate elements quality
8418 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8419 if ( iSplit == iQuad ) {
8420 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8424 aBadRate += getBadRate( &quad, aCrit );
8427 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8429 nodes[ iSplit < iQuad ? i4 : i3 ]);
8430 aBadRate += getBadRate( &tria, aCrit );
8434 if ( aBadRate < aBestRate ) {
8436 aBestRate = aBadRate;
8441 // create new elements
8443 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8445 if ( iSplit == iBestQuad )
8446 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8451 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8453 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8456 const SMDS_MeshNode* newNodes[ 4 ];
8457 newNodes[ 0 ] = linkNodes[ i1 ];
8458 newNodes[ 1 ] = linkNodes[ i2 ];
8459 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8460 newNodes[ 3 ] = nodes[ i4 ];
8461 if (iSplit == iBestQuad)
8462 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8464 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8466 } // end if(!theFace->IsQuadratic())
8468 else { // theFace is quadratic
8469 // we have to split theFace on simple triangles and one simple quadrangle
8471 int nbshift = tmp*2;
8472 // shift nodes in nodes[] by nbshift
8474 for(i=0; i<nbshift; i++) {
8475 const SMDS_MeshNode* n = nodes[0];
8476 for(j=0; j<nbFaceNodes-1; j++) {
8477 nodes[j] = nodes[j+1];
8479 nodes[nbFaceNodes-1] = n;
8481 il1 = il1 - nbshift;
8482 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8483 // n0 n1 n2 n0 n1 n2
8484 // +-----+-----+ +-----+-----+
8493 // create new elements
8495 if ( nbFaceNodes == 6 ) { // quadratic triangle
8496 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8497 if ( theFace->IsMediumNode(nodes[il1]) ) {
8498 // create quadrangle
8499 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8505 // create quadrangle
8506 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8512 else { // nbFaceNodes==8 - quadratic quadrangle
8513 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8514 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8515 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8516 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8517 // create quadrangle
8518 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8524 // create quadrangle
8525 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8531 // create needed triangles using n1,n2,n3 and inserted nodes
8532 int nbn = 2 + aNodesToInsert.size();
8533 vector<const SMDS_MeshNode*> aNodes(nbn);
8534 aNodes[0 ] = nodes[n1];
8535 aNodes[nbn-1] = nodes[n2];
8536 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8537 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8538 aNodes[iNode++] = *nIt;
8540 for ( i = 1; i < nbn; i++ )
8541 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8544 // remove the old face
8545 for ( size_t i = 0; i < newElems.size(); ++i )
8548 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8549 myLastCreatedElems.push_back( newElems[i] );
8551 ReplaceElemInGroups( theFace, newElems, aMesh );
8552 aMesh->RemoveElement(theFace);
8554 } // InsertNodesIntoLink()
8556 //=======================================================================
8557 //function : UpdateVolumes
8559 //=======================================================================
8561 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8562 const SMDS_MeshNode* theBetweenNode2,
8563 list<const SMDS_MeshNode*>& theNodesToInsert)
8567 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8568 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8569 const SMDS_MeshElement* elem = invElemIt->next();
8571 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8572 SMDS_VolumeTool aVolume (elem);
8573 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8576 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8577 int iface, nbFaces = aVolume.NbFaces();
8578 vector<const SMDS_MeshNode *> poly_nodes;
8579 vector<int> quantities (nbFaces);
8581 for (iface = 0; iface < nbFaces; iface++) {
8582 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8583 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8584 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8586 for (int inode = 0; inode < nbFaceNodes; inode++) {
8587 poly_nodes.push_back(faceNodes[inode]);
8589 if (nbInserted == 0) {
8590 if (faceNodes[inode] == theBetweenNode1) {
8591 if (faceNodes[inode + 1] == theBetweenNode2) {
8592 nbInserted = theNodesToInsert.size();
8594 // add nodes to insert
8595 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8596 for (; nIt != theNodesToInsert.end(); nIt++) {
8597 poly_nodes.push_back(*nIt);
8601 else if (faceNodes[inode] == theBetweenNode2) {
8602 if (faceNodes[inode + 1] == theBetweenNode1) {
8603 nbInserted = theNodesToInsert.size();
8605 // add nodes to insert in reversed order
8606 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8608 for (; nIt != theNodesToInsert.begin(); nIt--) {
8609 poly_nodes.push_back(*nIt);
8611 poly_nodes.push_back(*nIt);
8618 quantities[iface] = nbFaceNodes + nbInserted;
8621 // Replace the volume
8622 SMESHDS_Mesh *aMesh = GetMeshDS();
8624 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8626 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8627 myLastCreatedElems.push_back( newElem );
8628 ReplaceElemInGroups( elem, newElem, aMesh );
8630 aMesh->RemoveElement( elem );
8636 //================================================================================
8638 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8640 //================================================================================
8642 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8643 vector<const SMDS_MeshNode *> & nodes,
8644 vector<int> & nbNodeInFaces )
8647 nbNodeInFaces.clear();
8648 SMDS_VolumeTool vTool ( elem );
8649 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8651 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8652 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8653 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8658 //=======================================================================
8660 * \brief Convert elements contained in a sub-mesh to quadratic
8661 * \return int - nb of checked elements
8663 //=======================================================================
8665 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8666 SMESH_MesherHelper& theHelper,
8667 const bool theForce3d)
8669 //MESSAGE("convertElemToQuadratic");
8671 if( !theSm ) return nbElem;
8673 vector<int> nbNodeInFaces;
8674 vector<const SMDS_MeshNode *> nodes;
8675 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8676 while(ElemItr->more())
8679 const SMDS_MeshElement* elem = ElemItr->next();
8680 if( !elem ) continue;
8682 // analyse a necessity of conversion
8683 const SMDSAbs_ElementType aType = elem->GetType();
8684 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8686 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8687 bool hasCentralNodes = false;
8688 if ( elem->IsQuadratic() )
8691 switch ( aGeomType ) {
8692 case SMDSEntity_Quad_Triangle:
8693 case SMDSEntity_Quad_Quadrangle:
8694 case SMDSEntity_Quad_Hexa:
8695 case SMDSEntity_Quad_Penta:
8696 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8698 case SMDSEntity_BiQuad_Triangle:
8699 case SMDSEntity_BiQuad_Quadrangle:
8700 case SMDSEntity_TriQuad_Hexa:
8701 case SMDSEntity_BiQuad_Penta:
8702 alreadyOK = theHelper.GetIsBiQuadratic();
8703 hasCentralNodes = true;
8708 // take into account already present medium nodes
8710 case SMDSAbs_Volume:
8711 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8713 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8715 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8721 // get elem data needed to re-create it
8723 const int id = elem->GetID();
8724 const int nbNodes = elem->NbCornerNodes();
8725 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8726 if ( aGeomType == SMDSEntity_Polyhedra )
8727 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8728 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8729 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8731 // remove a linear element
8732 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8734 // remove central nodes of biquadratic elements (biquad->quad conversion)
8735 if ( hasCentralNodes )
8736 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8737 if ( nodes[i]->NbInverseElements() == 0 )
8738 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8740 const SMDS_MeshElement* NewElem = 0;
8746 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8754 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8757 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8760 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8764 case SMDSAbs_Volume :
8768 case SMDSEntity_Tetra:
8769 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8771 case SMDSEntity_Pyramid:
8772 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8774 case SMDSEntity_Penta:
8775 case SMDSEntity_Quad_Penta:
8776 case SMDSEntity_BiQuad_Penta:
8777 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8779 case SMDSEntity_Hexa:
8780 case SMDSEntity_Quad_Hexa:
8781 case SMDSEntity_TriQuad_Hexa:
8782 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8783 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8785 case SMDSEntity_Hexagonal_Prism:
8787 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8794 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8795 if( NewElem && NewElem->getshapeId() < 1 )
8796 theSm->AddElement( NewElem );
8800 //=======================================================================
8801 //function : ConvertToQuadratic
8803 //=======================================================================
8805 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8807 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8808 SMESHDS_Mesh* meshDS = GetMeshDS();
8810 SMESH_MesherHelper aHelper(*myMesh);
8812 aHelper.SetIsQuadratic( true );
8813 aHelper.SetIsBiQuadratic( theToBiQuad );
8814 aHelper.SetElementsOnShape(true);
8815 aHelper.ToFixNodeParameters( true );
8817 // convert elements assigned to sub-meshes
8818 int nbCheckedElems = 0;
8819 if ( myMesh->HasShapeToMesh() )
8821 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8823 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8824 while ( smIt->more() ) {
8825 SMESH_subMesh* sm = smIt->next();
8826 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8827 aHelper.SetSubShape( sm->GetSubShape() );
8828 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8834 // convert elements NOT assigned to sub-meshes
8835 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8836 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8838 aHelper.SetElementsOnShape(false);
8839 SMESHDS_SubMesh *smDS = 0;
8842 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8843 while( aEdgeItr->more() )
8845 const SMDS_MeshEdge* edge = aEdgeItr->next();
8846 if ( !edge->IsQuadratic() )
8848 int id = edge->GetID();
8849 const SMDS_MeshNode* n1 = edge->GetNode(0);
8850 const SMDS_MeshNode* n2 = edge->GetNode(1);
8852 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8854 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8855 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8859 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8864 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8865 while( aFaceItr->more() )
8867 const SMDS_MeshFace* face = aFaceItr->next();
8868 if ( !face ) continue;
8870 const SMDSAbs_EntityType type = face->GetEntityType();
8874 case SMDSEntity_Quad_Triangle:
8875 case SMDSEntity_Quad_Quadrangle:
8876 alreadyOK = !theToBiQuad;
8877 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8879 case SMDSEntity_BiQuad_Triangle:
8880 case SMDSEntity_BiQuad_Quadrangle:
8881 alreadyOK = theToBiQuad;
8882 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8884 default: alreadyOK = false;
8889 const int id = face->GetID();
8890 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8892 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8894 SMDS_MeshFace * NewFace = 0;
8897 case SMDSEntity_Triangle:
8898 case SMDSEntity_Quad_Triangle:
8899 case SMDSEntity_BiQuad_Triangle:
8900 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8901 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8902 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8905 case SMDSEntity_Quadrangle:
8906 case SMDSEntity_Quad_Quadrangle:
8907 case SMDSEntity_BiQuad_Quadrangle:
8908 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8909 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8910 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8914 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8916 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8920 vector<int> nbNodeInFaces;
8921 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8922 while(aVolumeItr->more())
8924 const SMDS_MeshVolume* volume = aVolumeItr->next();
8925 if ( !volume ) continue;
8927 const SMDSAbs_EntityType type = volume->GetEntityType();
8928 if ( volume->IsQuadratic() )
8933 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8934 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8935 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
8936 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
8937 default: alreadyOK = true;
8941 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8945 const int id = volume->GetID();
8946 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8947 if ( type == SMDSEntity_Polyhedra )
8948 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
8949 else if ( type == SMDSEntity_Hexagonal_Prism )
8950 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8952 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8954 SMDS_MeshVolume * NewVolume = 0;
8957 case SMDSEntity_Tetra:
8958 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8960 case SMDSEntity_Hexa:
8961 case SMDSEntity_Quad_Hexa:
8962 case SMDSEntity_TriQuad_Hexa:
8963 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8964 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8965 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8966 if ( nodes[i]->NbInverseElements() == 0 )
8967 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8969 case SMDSEntity_Pyramid:
8970 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8971 nodes[3], nodes[4], id, theForce3d);
8973 case SMDSEntity_Penta:
8974 case SMDSEntity_Quad_Penta:
8975 case SMDSEntity_BiQuad_Penta:
8976 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8977 nodes[3], nodes[4], nodes[5], id, theForce3d);
8978 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
8979 if ( nodes[i]->NbInverseElements() == 0 )
8980 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8982 case SMDSEntity_Hexagonal_Prism:
8984 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8986 ReplaceElemInGroups(volume, NewVolume, meshDS);
8991 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8992 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8993 // aHelper.FixQuadraticElements(myError);
8994 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8998 //================================================================================
9000 * \brief Makes given elements quadratic
9001 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9002 * \param theElements - elements to make quadratic
9004 //================================================================================
9006 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9007 TIDSortedElemSet& theElements,
9008 const bool theToBiQuad)
9010 if ( theElements.empty() ) return;
9012 // we believe that all theElements are of the same type
9013 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9015 // get all nodes shared by theElements
9016 TIDSortedNodeSet allNodes;
9017 TIDSortedElemSet::iterator eIt = theElements.begin();
9018 for ( ; eIt != theElements.end(); ++eIt )
9019 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9021 // complete theElements with elements of lower dim whose all nodes are in allNodes
9023 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9024 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9025 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9026 for ( ; nIt != allNodes.end(); ++nIt )
9028 const SMDS_MeshNode* n = *nIt;
9029 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9030 while ( invIt->more() )
9032 const SMDS_MeshElement* e = invIt->next();
9033 const SMDSAbs_ElementType type = e->GetType();
9034 if ( e->IsQuadratic() )
9036 quadAdjacentElems[ type ].insert( e );
9039 switch ( e->GetEntityType() ) {
9040 case SMDSEntity_Quad_Triangle:
9041 case SMDSEntity_Quad_Quadrangle:
9042 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9043 case SMDSEntity_BiQuad_Triangle:
9044 case SMDSEntity_BiQuad_Quadrangle:
9045 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9046 default: alreadyOK = true;
9051 if ( type >= elemType )
9052 continue; // same type or more complex linear element
9054 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9055 continue; // e is already checked
9059 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9060 while ( nodeIt->more() && allIn )
9061 allIn = allNodes.count( nodeIt->next() );
9063 theElements.insert(e );
9067 SMESH_MesherHelper helper(*myMesh);
9068 helper.SetIsQuadratic( true );
9069 helper.SetIsBiQuadratic( theToBiQuad );
9071 // add links of quadratic adjacent elements to the helper
9073 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9074 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9075 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9077 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9079 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9080 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9081 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9083 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9085 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9086 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9087 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9089 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9092 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9094 SMESHDS_Mesh* meshDS = GetMeshDS();
9095 SMESHDS_SubMesh* smDS = 0;
9096 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9098 const SMDS_MeshElement* elem = *eIt;
9101 int nbCentralNodes = 0;
9102 switch ( elem->GetEntityType() ) {
9103 // linear convertible
9104 case SMDSEntity_Edge:
9105 case SMDSEntity_Triangle:
9106 case SMDSEntity_Quadrangle:
9107 case SMDSEntity_Tetra:
9108 case SMDSEntity_Pyramid:
9109 case SMDSEntity_Hexa:
9110 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9111 // quadratic that can become bi-quadratic
9112 case SMDSEntity_Quad_Triangle:
9113 case SMDSEntity_Quad_Quadrangle:
9114 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9116 case SMDSEntity_BiQuad_Triangle:
9117 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9118 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9120 default: alreadyOK = true;
9122 if ( alreadyOK ) continue;
9124 const SMDSAbs_ElementType type = elem->GetType();
9125 const int id = elem->GetID();
9126 const int nbNodes = elem->NbCornerNodes();
9127 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9129 helper.SetSubShape( elem->getshapeId() );
9131 if ( !smDS || !smDS->Contains( elem ))
9132 smDS = meshDS->MeshElements( elem->getshapeId() );
9133 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9135 SMDS_MeshElement * newElem = 0;
9138 case 4: // cases for most frequently used element types go first (for optimization)
9139 if ( type == SMDSAbs_Volume )
9140 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9142 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9145 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9146 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9149 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9152 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9155 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9156 nodes[4], id, theForce3d);
9159 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9160 nodes[4], nodes[5], id, theForce3d);
9164 ReplaceElemInGroups( elem, newElem, meshDS);
9165 if( newElem && smDS )
9166 smDS->AddElement( newElem );
9168 // remove central nodes
9169 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9170 if ( nodes[i]->NbInverseElements() == 0 )
9171 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9173 } // loop on theElements
9176 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9177 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9178 // helper.FixQuadraticElements( myError );
9179 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9183 //=======================================================================
9185 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9186 * \return int - nb of checked elements
9188 //=======================================================================
9190 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9191 SMDS_ElemIteratorPtr theItr,
9192 const int theShapeID)
9195 SMESHDS_Mesh* meshDS = GetMeshDS();
9196 ElemFeatures elemType;
9197 vector<const SMDS_MeshNode *> nodes;
9199 while( theItr->more() )
9201 const SMDS_MeshElement* elem = theItr->next();
9203 if( elem && elem->IsQuadratic())
9206 int nbCornerNodes = elem->NbCornerNodes();
9207 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9209 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9211 //remove a quadratic element
9212 if ( !theSm || !theSm->Contains( elem ))
9213 theSm = meshDS->MeshElements( elem->getshapeId() );
9214 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9216 // remove medium nodes
9217 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9218 if ( nodes[i]->NbInverseElements() == 0 )
9219 meshDS->RemoveFreeNode( nodes[i], theSm );
9221 // add a linear element
9222 nodes.resize( nbCornerNodes );
9223 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9224 ReplaceElemInGroups(elem, newElem, meshDS);
9225 if( theSm && newElem )
9226 theSm->AddElement( newElem );
9232 //=======================================================================
9233 //function : ConvertFromQuadratic
9235 //=======================================================================
9237 bool SMESH_MeshEditor::ConvertFromQuadratic()
9239 int nbCheckedElems = 0;
9240 if ( myMesh->HasShapeToMesh() )
9242 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9244 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9245 while ( smIt->more() ) {
9246 SMESH_subMesh* sm = smIt->next();
9247 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9248 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9254 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9255 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9257 SMESHDS_SubMesh *aSM = 0;
9258 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9266 //================================================================================
9268 * \brief Return true if all medium nodes of the element are in the node set
9270 //================================================================================
9272 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9274 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9275 if ( !nodeSet.count( elem->GetNode(i) ))
9281 //================================================================================
9283 * \brief Makes given elements linear
9285 //================================================================================
9287 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9289 if ( theElements.empty() ) return;
9291 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9292 set<int> mediumNodeIDs;
9293 TIDSortedElemSet::iterator eIt = theElements.begin();
9294 for ( ; eIt != theElements.end(); ++eIt )
9296 const SMDS_MeshElement* e = *eIt;
9297 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9298 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9301 // replace given elements by linear ones
9302 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9303 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9305 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9306 // except those elements sharing medium nodes of quadratic element whose medium nodes
9307 // are not all in mediumNodeIDs
9309 // get remaining medium nodes
9310 TIDSortedNodeSet mediumNodes;
9311 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9312 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9313 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9314 mediumNodes.insert( mediumNodes.end(), n );
9316 // find more quadratic elements to convert
9317 TIDSortedElemSet moreElemsToConvert;
9318 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9319 for ( ; nIt != mediumNodes.end(); ++nIt )
9321 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9322 while ( invIt->more() )
9324 const SMDS_MeshElement* e = invIt->next();
9325 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9327 // find a more complex element including e and
9328 // whose medium nodes are not in mediumNodes
9329 bool complexFound = false;
9330 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9332 SMDS_ElemIteratorPtr invIt2 =
9333 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9334 while ( invIt2->more() )
9336 const SMDS_MeshElement* eComplex = invIt2->next();
9337 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9339 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9340 if ( nbCommonNodes == e->NbNodes())
9342 complexFound = true;
9343 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9349 if ( !complexFound )
9350 moreElemsToConvert.insert( e );
9354 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9355 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9358 //=======================================================================
9359 //function : SewSideElements
9361 //=======================================================================
9363 SMESH_MeshEditor::Sew_Error
9364 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9365 TIDSortedElemSet& theSide2,
9366 const SMDS_MeshNode* theFirstNode1,
9367 const SMDS_MeshNode* theFirstNode2,
9368 const SMDS_MeshNode* theSecondNode1,
9369 const SMDS_MeshNode* theSecondNode2)
9373 if ( theSide1.size() != theSide2.size() )
9374 return SEW_DIFF_NB_OF_ELEMENTS;
9376 Sew_Error aResult = SEW_OK;
9378 // 1. Build set of faces representing each side
9379 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9380 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9382 // =======================================================================
9383 // 1. Build set of faces representing each side:
9384 // =======================================================================
9385 // a. build set of nodes belonging to faces
9386 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9387 // c. create temporary faces representing side of volumes if correspondent
9388 // face does not exist
9390 SMESHDS_Mesh* aMesh = GetMeshDS();
9391 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9392 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9393 TIDSortedElemSet faceSet1, faceSet2;
9394 set<const SMDS_MeshElement*> volSet1, volSet2;
9395 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9396 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9397 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9398 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9399 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9400 int iSide, iFace, iNode;
9402 list<const SMDS_MeshElement* > tempFaceList;
9403 for ( iSide = 0; iSide < 2; iSide++ ) {
9404 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9405 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9406 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9407 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9408 set<const SMDS_MeshElement*>::iterator vIt;
9409 TIDSortedElemSet::iterator eIt;
9410 set<const SMDS_MeshNode*>::iterator nIt;
9412 // check that given nodes belong to given elements
9413 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9414 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9415 int firstIndex = -1, secondIndex = -1;
9416 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9417 const SMDS_MeshElement* elem = *eIt;
9418 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9419 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9420 if ( firstIndex > -1 && secondIndex > -1 ) break;
9422 if ( firstIndex < 0 || secondIndex < 0 ) {
9423 // we can simply return until temporary faces created
9424 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9427 // -----------------------------------------------------------
9428 // 1a. Collect nodes of existing faces
9429 // and build set of face nodes in order to detect missing
9430 // faces corresponding to sides of volumes
9431 // -----------------------------------------------------------
9433 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9435 // loop on the given element of a side
9436 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9437 //const SMDS_MeshElement* elem = *eIt;
9438 const SMDS_MeshElement* elem = *eIt;
9439 if ( elem->GetType() == SMDSAbs_Face ) {
9440 faceSet->insert( elem );
9441 set <const SMDS_MeshNode*> faceNodeSet;
9442 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9443 while ( nodeIt->more() ) {
9444 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9445 nodeSet->insert( n );
9446 faceNodeSet.insert( n );
9448 setOfFaceNodeSet.insert( faceNodeSet );
9450 else if ( elem->GetType() == SMDSAbs_Volume )
9451 volSet->insert( elem );
9453 // ------------------------------------------------------------------------------
9454 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9455 // ------------------------------------------------------------------------------
9457 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9458 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9459 while ( fIt->more() ) { // loop on faces sharing a node
9460 const SMDS_MeshElement* f = fIt->next();
9461 if ( faceSet->find( f ) == faceSet->end() ) {
9462 // check if all nodes are in nodeSet and
9463 // complete setOfFaceNodeSet if they are
9464 set <const SMDS_MeshNode*> faceNodeSet;
9465 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9466 bool allInSet = true;
9467 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9468 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9469 if ( nodeSet->find( n ) == nodeSet->end() )
9472 faceNodeSet.insert( n );
9475 faceSet->insert( f );
9476 setOfFaceNodeSet.insert( faceNodeSet );
9482 // -------------------------------------------------------------------------
9483 // 1c. Create temporary faces representing sides of volumes if correspondent
9484 // face does not exist
9485 // -------------------------------------------------------------------------
9487 if ( !volSet->empty() ) {
9488 //int nodeSetSize = nodeSet->size();
9490 // loop on given volumes
9491 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9492 SMDS_VolumeTool vol (*vIt);
9493 // loop on volume faces: find free faces
9494 // --------------------------------------
9495 list<const SMDS_MeshElement* > freeFaceList;
9496 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9497 if ( !vol.IsFreeFace( iFace ))
9499 // check if there is already a face with same nodes in a face set
9500 const SMDS_MeshElement* aFreeFace = 0;
9501 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9502 int nbNodes = vol.NbFaceNodes( iFace );
9503 set <const SMDS_MeshNode*> faceNodeSet;
9504 vol.GetFaceNodes( iFace, faceNodeSet );
9505 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9507 // no such a face is given but it still can exist, check it
9508 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9509 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9512 // create a temporary face
9513 if ( nbNodes == 3 ) {
9514 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9515 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9517 else if ( nbNodes == 4 ) {
9518 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9519 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9522 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9523 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9524 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9527 tempFaceList.push_back( aFreeFace );
9531 freeFaceList.push_back( aFreeFace );
9533 } // loop on faces of a volume
9535 // choose one of several free faces of a volume
9536 // --------------------------------------------
9537 if ( freeFaceList.size() > 1 ) {
9538 // choose a face having max nb of nodes shared by other elems of a side
9539 int maxNbNodes = -1;
9540 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9541 while ( fIt != freeFaceList.end() ) { // loop on free faces
9542 int nbSharedNodes = 0;
9543 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9544 while ( nodeIt->more() ) { // loop on free face nodes
9545 const SMDS_MeshNode* n =
9546 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9547 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9548 while ( invElemIt->more() ) {
9549 const SMDS_MeshElement* e = invElemIt->next();
9550 nbSharedNodes += faceSet->count( e );
9551 nbSharedNodes += elemSet->count( e );
9554 if ( nbSharedNodes > maxNbNodes ) {
9555 maxNbNodes = nbSharedNodes;
9556 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9558 else if ( nbSharedNodes == maxNbNodes ) {
9562 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9565 if ( freeFaceList.size() > 1 )
9567 // could not choose one face, use another way
9568 // choose a face most close to the bary center of the opposite side
9569 gp_XYZ aBC( 0., 0., 0. );
9570 set <const SMDS_MeshNode*> addedNodes;
9571 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9572 eIt = elemSet2->begin();
9573 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9574 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9575 while ( nodeIt->more() ) { // loop on free face nodes
9576 const SMDS_MeshNode* n =
9577 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9578 if ( addedNodes.insert( n ).second )
9579 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9582 aBC /= addedNodes.size();
9583 double minDist = DBL_MAX;
9584 fIt = freeFaceList.begin();
9585 while ( fIt != freeFaceList.end() ) { // loop on free faces
9587 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9588 while ( nodeIt->more() ) { // loop on free face nodes
9589 const SMDS_MeshNode* n =
9590 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9591 gp_XYZ p( n->X(),n->Y(),n->Z() );
9592 dist += ( aBC - p ).SquareModulus();
9594 if ( dist < minDist ) {
9596 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9599 fIt = freeFaceList.erase( fIt++ );
9602 } // choose one of several free faces of a volume
9604 if ( freeFaceList.size() == 1 ) {
9605 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9606 faceSet->insert( aFreeFace );
9607 // complete a node set with nodes of a found free face
9608 // for ( iNode = 0; iNode < ; iNode++ )
9609 // nodeSet->insert( fNodes[ iNode ] );
9612 } // loop on volumes of a side
9614 // // complete a set of faces if new nodes in a nodeSet appeared
9615 // // ----------------------------------------------------------
9616 // if ( nodeSetSize != nodeSet->size() ) {
9617 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9618 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9619 // while ( fIt->more() ) { // loop on faces sharing a node
9620 // const SMDS_MeshElement* f = fIt->next();
9621 // if ( faceSet->find( f ) == faceSet->end() ) {
9622 // // check if all nodes are in nodeSet and
9623 // // complete setOfFaceNodeSet if they are
9624 // set <const SMDS_MeshNode*> faceNodeSet;
9625 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9626 // bool allInSet = true;
9627 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9628 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9629 // if ( nodeSet->find( n ) == nodeSet->end() )
9630 // allInSet = false;
9632 // faceNodeSet.insert( n );
9634 // if ( allInSet ) {
9635 // faceSet->insert( f );
9636 // setOfFaceNodeSet.insert( faceNodeSet );
9642 } // Create temporary faces, if there are volumes given
9645 if ( faceSet1.size() != faceSet2.size() ) {
9646 // delete temporary faces: they are in reverseElements of actual nodes
9647 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9648 // while ( tmpFaceIt->more() )
9649 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9650 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9651 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9652 // aMesh->RemoveElement(*tmpFaceIt);
9653 MESSAGE("Diff nb of faces");
9654 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9657 // ============================================================
9658 // 2. Find nodes to merge:
9659 // bind a node to remove to a node to put instead
9660 // ============================================================
9662 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9663 if ( theFirstNode1 != theFirstNode2 )
9664 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9665 if ( theSecondNode1 != theSecondNode2 )
9666 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9668 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9669 set< long > linkIdSet; // links to process
9670 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9672 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9673 list< NLink > linkList[2];
9674 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9675 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9676 // loop on links in linkList; find faces by links and append links
9677 // of the found faces to linkList
9678 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9679 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9681 NLink link[] = { *linkIt[0], *linkIt[1] };
9682 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9683 if ( !linkIdSet.count( linkID ) )
9686 // by links, find faces in the face sets,
9687 // and find indices of link nodes in the found faces;
9688 // in a face set, there is only one or no face sharing a link
9689 // ---------------------------------------------------------------
9691 const SMDS_MeshElement* face[] = { 0, 0 };
9692 vector<const SMDS_MeshNode*> fnodes[2];
9693 int iLinkNode[2][2];
9694 TIDSortedElemSet avoidSet;
9695 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9696 const SMDS_MeshNode* n1 = link[iSide].first;
9697 const SMDS_MeshNode* n2 = link[iSide].second;
9698 //cout << "Side " << iSide << " ";
9699 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9700 // find a face by two link nodes
9701 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9702 *faceSetPtr[ iSide ], avoidSet,
9703 &iLinkNode[iSide][0],
9704 &iLinkNode[iSide][1] );
9707 //cout << " F " << face[ iSide]->GetID() <<endl;
9708 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9709 // put face nodes to fnodes
9710 SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9711 fnodes[ iSide ].assign( nIt, nEnd );
9712 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9716 // check similarity of elements of the sides
9717 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9718 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9719 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9720 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9723 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9725 break; // do not return because it's necessary to remove tmp faces
9728 // set nodes to merge
9729 // -------------------
9731 if ( face[0] && face[1] ) {
9732 const int nbNodes = face[0]->NbNodes();
9733 if ( nbNodes != face[1]->NbNodes() ) {
9734 MESSAGE("Diff nb of face nodes");
9735 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9736 break; // do not return because it s necessary to remove tmp faces
9738 bool reverse[] = { false, false }; // order of nodes in the link
9739 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9740 // analyse link orientation in faces
9741 int i1 = iLinkNode[ iSide ][ 0 ];
9742 int i2 = iLinkNode[ iSide ][ 1 ];
9743 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9745 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9746 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9747 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9749 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9750 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9753 // add other links of the faces to linkList
9754 // -----------------------------------------
9756 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9757 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9758 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9759 if ( !iter_isnew.second ) { // already in a set: no need to process
9760 linkIdSet.erase( iter_isnew.first );
9762 else // new in set == encountered for the first time: add
9764 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9765 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9766 linkList[0].push_back ( NLink( n1, n2 ));
9767 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9772 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9775 } // loop on link lists
9777 if ( aResult == SEW_OK &&
9778 ( //linkIt[0] != linkList[0].end() ||
9779 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9780 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9781 " " << (faceSetPtr[1]->empty()));
9782 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9785 // ====================================================================
9786 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9787 // ====================================================================
9789 // delete temporary faces
9790 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9791 // while ( tmpFaceIt->more() )
9792 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9793 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9794 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9795 aMesh->RemoveElement(*tmpFaceIt);
9797 if ( aResult != SEW_OK)
9800 list< int > nodeIDsToRemove;
9801 vector< const SMDS_MeshNode*> nodes;
9802 ElemFeatures elemType;
9804 // loop on nodes replacement map
9805 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9806 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9807 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9809 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9810 nodeIDsToRemove.push_back( nToRemove->GetID() );
9811 // loop on elements sharing nToRemove
9812 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9813 while ( invElemIt->more() ) {
9814 const SMDS_MeshElement* e = invElemIt->next();
9815 // get a new suite of nodes: make replacement
9816 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9817 nodes.resize( nbNodes );
9818 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9819 while ( nIt->more() ) {
9820 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9821 nnIt = nReplaceMap.find( n );
9822 if ( nnIt != nReplaceMap.end() ) {
9828 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9829 // elemIDsToRemove.push_back( e->GetID() );
9833 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9834 aMesh->RemoveElement( e );
9836 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9838 AddToSameGroups( newElem, e, aMesh );
9839 if ( int aShapeId = e->getshapeId() )
9840 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9846 Remove( nodeIDsToRemove, true );
9851 //================================================================================
9853 * \brief Find corresponding nodes in two sets of faces
9854 * \param theSide1 - first face set
9855 * \param theSide2 - second first face
9856 * \param theFirstNode1 - a boundary node of set 1
9857 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9858 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9859 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9860 * \param nReplaceMap - output map of corresponding nodes
9861 * \return bool - is a success or not
9863 //================================================================================
9866 //#define DEBUG_MATCHING_NODES
9869 SMESH_MeshEditor::Sew_Error
9870 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9871 set<const SMDS_MeshElement*>& theSide2,
9872 const SMDS_MeshNode* theFirstNode1,
9873 const SMDS_MeshNode* theFirstNode2,
9874 const SMDS_MeshNode* theSecondNode1,
9875 const SMDS_MeshNode* theSecondNode2,
9876 TNodeNodeMap & nReplaceMap)
9878 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9880 nReplaceMap.clear();
9881 if ( theFirstNode1 != theFirstNode2 )
9882 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9883 if ( theSecondNode1 != theSecondNode2 )
9884 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9886 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9887 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9889 list< NLink > linkList[2];
9890 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9891 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9893 // loop on links in linkList; find faces by links and append links
9894 // of the found faces to linkList
9895 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9896 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9897 NLink link[] = { *linkIt[0], *linkIt[1] };
9898 if ( linkSet.find( link[0] ) == linkSet.end() )
9901 // by links, find faces in the face sets,
9902 // and find indices of link nodes in the found faces;
9903 // in a face set, there is only one or no face sharing a link
9904 // ---------------------------------------------------------------
9906 const SMDS_MeshElement* face[] = { 0, 0 };
9907 list<const SMDS_MeshNode*> notLinkNodes[2];
9908 //bool reverse[] = { false, false }; // order of notLinkNodes
9910 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9912 const SMDS_MeshNode* n1 = link[iSide].first;
9913 const SMDS_MeshNode* n2 = link[iSide].second;
9914 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9915 set< const SMDS_MeshElement* > facesOfNode1;
9916 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9918 // during a loop of the first node, we find all faces around n1,
9919 // during a loop of the second node, we find one face sharing both n1 and n2
9920 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9921 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9922 while ( fIt->more() ) { // loop on faces sharing a node
9923 const SMDS_MeshElement* f = fIt->next();
9924 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9925 ! facesOfNode1.insert( f ).second ) // f encounters twice
9927 if ( face[ iSide ] ) {
9928 MESSAGE( "2 faces per link " );
9929 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9932 faceSet->erase( f );
9934 // get not link nodes
9935 int nbN = f->NbNodes();
9936 if ( f->IsQuadratic() )
9938 nbNodes[ iSide ] = nbN;
9939 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9940 int i1 = f->GetNodeIndex( n1 );
9941 int i2 = f->GetNodeIndex( n2 );
9942 int iEnd = nbN, iBeg = -1, iDelta = 1;
9943 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9945 std::swap( iEnd, iBeg ); iDelta = -1;
9950 if ( i == iEnd ) i = iBeg + iDelta;
9951 if ( i == i1 ) break;
9952 nodes.push_back ( f->GetNode( i ) );
9958 // check similarity of elements of the sides
9959 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9960 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9961 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9962 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9965 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9969 // set nodes to merge
9970 // -------------------
9972 if ( face[0] && face[1] ) {
9973 if ( nbNodes[0] != nbNodes[1] ) {
9974 MESSAGE("Diff nb of face nodes");
9975 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9977 #ifdef DEBUG_MATCHING_NODES
9978 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9979 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9980 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9982 int nbN = nbNodes[0];
9984 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9985 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9986 for ( int i = 0 ; i < nbN - 2; ++i ) {
9987 #ifdef DEBUG_MATCHING_NODES
9988 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9990 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9994 // add other links of the face 1 to linkList
9995 // -----------------------------------------
9997 const SMDS_MeshElement* f0 = face[0];
9998 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
9999 for ( int i = 0; i < nbN; i++ )
10001 const SMDS_MeshNode* n2 = f0->GetNode( i );
10002 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10003 linkSet.insert( SMESH_TLink( n1, n2 ));
10004 if ( !iter_isnew.second ) { // already in a set: no need to process
10005 linkSet.erase( iter_isnew.first );
10007 else // new in set == encountered for the first time: add
10009 #ifdef DEBUG_MATCHING_NODES
10010 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10011 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10013 linkList[0].push_back ( NLink( n1, n2 ));
10014 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10019 } // loop on link lists
10024 namespace // automatically find theAffectedElems for DoubleNodes()
10026 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10028 //--------------------------------------------------------------------------------
10029 // Nodes shared by adjacent FissureBorder's.
10030 // 1 node if FissureBorder separates faces
10031 // 2 nodes if FissureBorder separates volumes
10034 const SMDS_MeshNode* _nodes[2];
10037 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10041 _nbNodes = bool( n1 ) + bool( n2 );
10042 if ( _nbNodes == 2 && n1 > n2 )
10043 std::swap( _nodes[0], _nodes[1] );
10045 bool operator<( const SubBorder& other ) const
10047 for ( int i = 0; i < _nbNodes; ++i )
10049 if ( _nodes[i] < other._nodes[i] ) return true;
10050 if ( _nodes[i] > other._nodes[i] ) return false;
10056 //--------------------------------------------------------------------------------
10057 // Map a SubBorder to all FissureBorder it bounds
10058 struct FissureBorder;
10059 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10060 typedef TBorderLinks::iterator TMappedSub;
10062 //--------------------------------------------------------------------------------
10064 * \brief Element border (volume facet or face edge) at a fissure
10066 struct FissureBorder
10068 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10069 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10071 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10072 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10074 FissureBorder( FissureBorder && from ) // move constructor
10076 std::swap( _nodes, from._nodes );
10077 std::swap( _sortedNodes, from._sortedNodes );
10078 _elems[0] = from._elems[0];
10079 _elems[1] = from._elems[1];
10082 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10083 std::vector< const SMDS_MeshElement* > & adjElems)
10084 : _nodes( elemToDuplicate->NbCornerNodes() )
10086 for ( size_t i = 0; i < _nodes.size(); ++i )
10087 _nodes[i] = elemToDuplicate->GetNode( i );
10089 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10090 findAdjacent( type, adjElems );
10093 FissureBorder( const SMDS_MeshNode** nodes,
10094 const size_t nbNodes,
10095 const SMDSAbs_ElementType adjElemsType,
10096 std::vector< const SMDS_MeshElement* > & adjElems)
10097 : _nodes( nodes, nodes + nbNodes )
10099 findAdjacent( adjElemsType, adjElems );
10102 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10103 std::vector< const SMDS_MeshElement* > & adjElems)
10105 _elems[0] = _elems[1] = 0;
10107 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10108 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10109 _elems[i] = adjElems[i];
10112 bool operator<( const FissureBorder& other ) const
10114 return GetSortedNodes() < other.GetSortedNodes();
10117 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10119 if ( _sortedNodes.empty() && !_nodes.empty() )
10121 FissureBorder* me = const_cast<FissureBorder*>( this );
10122 me->_sortedNodes = me->_nodes;
10123 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10125 return _sortedNodes;
10128 size_t NbSub() const
10130 return _nodes.size();
10133 SubBorder Sub(size_t i) const
10135 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10138 void AddSelfTo( TBorderLinks& borderLinks )
10140 _mappedSubs.resize( NbSub() );
10141 for ( size_t i = 0; i < NbSub(); ++i )
10143 TBorderLinks::iterator s2b =
10144 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10145 s2b->second.push_back( this );
10146 _mappedSubs[ i ] = s2b;
10155 const SMDS_MeshElement* GetMarkedElem() const
10157 if ( _nodes.empty() ) return 0; // cleared
10158 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10159 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10163 gp_XYZ GetNorm() const // normal to the border
10166 if ( _nodes.size() == 2 )
10168 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10169 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10171 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10174 gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10175 norm = bordDir ^ avgNorm;
10179 SMESH_NodeXYZ p0( _nodes[0] );
10180 SMESH_NodeXYZ p1( _nodes[1] );
10181 SMESH_NodeXYZ p2( _nodes[2] );
10182 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10184 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10190 void ChooseSide() // mark an _elem located at positive side of fissure
10192 _elems[0]->setIsMarked( true );
10193 gp_XYZ norm = GetNorm();
10194 double maxX = norm.Coord(1);
10195 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10196 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10199 _elems[0]->setIsMarked( false );
10200 _elems[1]->setIsMarked( true );
10204 }; // struct FissureBorder
10206 //--------------------------------------------------------------------------------
10208 * \brief Classifier of elements at fissure edge
10210 class FissureNormal
10212 std::vector< gp_XYZ > _normals;
10216 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10219 _normals.reserve(2);
10220 _normals.push_back( bord.GetNorm() );
10221 if ( _normals.size() == 2 )
10222 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10225 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10228 switch ( _normals.size() ) {
10231 isIn = !isOut( n, _normals[0], elem );
10236 bool in1 = !isOut( n, _normals[0], elem );
10237 bool in2 = !isOut( n, _normals[1], elem );
10238 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10245 //================================================================================
10247 * \brief Classify an element by a plane passing through a node
10249 //================================================================================
10251 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10253 SMESH_NodeXYZ p = n;
10255 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10257 SMESH_NodeXYZ pi = elem->GetNode( i );
10258 sumDot += norm * ( pi - p );
10260 return sumDot < -1e-100;
10263 //================================================================================
10265 * \brief Find FissureBorder's by nodes to duplicate
10267 //================================================================================
10269 void findFissureBorders( const TIDSortedElemSet& theNodes,
10270 std::vector< FissureBorder > & theFissureBorders )
10272 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10273 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10275 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10276 if ( n->NbInverseElements( elemType ) == 0 )
10278 elemType = SMDSAbs_Face;
10279 if ( n->NbInverseElements( elemType ) == 0 )
10282 // unmark elements touching the fissure
10283 for ( ; nIt != theNodes.end(); ++nIt )
10284 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10286 // loop on elements touching the fissure to get their borders belonging to the fissure
10287 std::set< FissureBorder > fissureBorders;
10288 std::vector< const SMDS_MeshElement* > adjElems;
10289 std::vector< const SMDS_MeshNode* > nodes;
10290 SMDS_VolumeTool volTool;
10291 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10293 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10294 while ( invIt->more() )
10296 const SMDS_MeshElement* eInv = invIt->next();
10297 if ( eInv->isMarked() ) continue;
10298 eInv->setIsMarked( true );
10300 if ( elemType == SMDSAbs_Volume )
10302 volTool.Set( eInv );
10303 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10304 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10306 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10307 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10309 bool allOnFissure = true;
10310 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10311 if (( allOnFissure = theNodes.count( nn[ iN ])))
10312 nodes.push_back( nn[ iN ]);
10313 if ( allOnFissure )
10314 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10315 elemType, adjElems )));
10318 else // elemType == SMDSAbs_Face
10320 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10321 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10322 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10324 nn[1] = eInv->GetNode( iN );
10325 onFissure1 = theNodes.count( nn[1] );
10326 if ( onFissure0 && onFissure1 )
10327 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10329 onFissure0 = onFissure1;
10335 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10336 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10337 for ( ; bord != fissureBorders.end(); ++bord )
10339 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10342 } // findFissureBorders()
10344 //================================================================================
10346 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10347 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10348 * \param [in] theNodesNot - nodes not to duplicate
10349 * \param [out] theAffectedElems - the found elements
10351 //================================================================================
10353 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10354 TIDSortedElemSet& theAffectedElems)
10356 if ( theElemsOrNodes.empty() ) return;
10358 // find FissureBorder's
10360 std::vector< FissureBorder > fissure;
10361 std::vector< const SMDS_MeshElement* > elemsByFacet;
10363 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10364 if ( (*elIt)->GetType() == SMDSAbs_Node )
10366 findFissureBorders( theElemsOrNodes, fissure );
10370 fissure.reserve( theElemsOrNodes.size() );
10371 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10372 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10374 if ( fissure.empty() )
10377 // fill borderLinks
10379 TBorderLinks borderLinks;
10381 for ( size_t i = 0; i < fissure.size(); ++i )
10383 fissure[i].AddSelfTo( borderLinks );
10386 // get theAffectedElems
10388 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10389 for ( size_t i = 0; i < fissure.size(); ++i )
10390 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10392 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10393 false, /*markElem=*/true );
10396 std::vector<const SMDS_MeshNode *> facetNodes;
10397 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10398 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10400 // choose a side of fissure
10401 fissure[0].ChooseSide();
10402 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10404 size_t nbCheckedBorders = 0;
10405 while ( nbCheckedBorders < fissure.size() )
10407 // find a FissureBorder to treat
10408 FissureBorder* bord = 0;
10409 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10410 if ( fissure[i].GetMarkedElem() )
10411 bord = & fissure[i];
10412 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10413 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10415 bord = & fissure[i];
10416 bord->ChooseSide();
10417 theAffectedElems.insert( bord->GetMarkedElem() );
10419 if ( !bord ) return;
10420 ++nbCheckedBorders;
10422 // treat FissureBorder's linked to bord
10423 fissureNodes.clear();
10424 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10425 for ( size_t i = 0; i < bord->NbSub(); ++i )
10427 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10428 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10429 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10430 const SubBorder& sb = l2b->first;
10431 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10433 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10435 for ( int j = 0; j < sb._nbNodes; ++j )
10436 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10440 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10441 // until an elem adjacent to a neighbour FissureBorder is found
10442 facetNodes.clear();
10443 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10444 facetNodes.resize( sb._nbNodes + 1 );
10448 // check if bordElem is adjacent to a neighbour FissureBorder
10449 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10451 FissureBorder* bord2 = linkedBorders[j];
10452 if ( bord2 == bord ) continue;
10453 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10456 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10461 // find the next bordElem
10462 const SMDS_MeshElement* nextBordElem = 0;
10463 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10465 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10466 if ( fissureNodes.count( n )) continue;
10468 facetNodes[ sb._nbNodes ] = n;
10469 elemsByFacet.clear();
10470 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10472 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10473 if ( elemsByFacet[ iE ] != bordElem &&
10474 !elemsByFacet[ iE ]->isMarked() )
10476 theAffectedElems.insert( elemsByFacet[ iE ]);
10477 elemsByFacet[ iE ]->setIsMarked( true );
10478 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10479 nextBordElem = elemsByFacet[ iE ];
10483 bordElem = nextBordElem;
10485 } // while ( bordElem )
10487 linkedBorders.clear(); // not to treat this link any more
10489 } // loop on SubBorder's of a FissureBorder
10493 } // loop on FissureBorder's
10496 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10498 // mark nodes of theAffectedElems
10499 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10501 // unmark nodes of the fissure
10502 elIt = theElemsOrNodes.begin();
10503 if ( (*elIt)->GetType() == SMDSAbs_Node )
10504 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10506 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10508 std::vector< gp_XYZ > normVec;
10510 // loop on nodes of the fissure, add elements having marked nodes
10511 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10513 const SMDS_MeshElement* e = (*elIt);
10514 if ( e->GetType() != SMDSAbs_Node )
10515 e->setIsMarked( true ); // avoid adding a fissure element
10517 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10519 const SMDS_MeshNode* n = e->GetNode( iN );
10520 if ( fissEdgeNodes2Norm.count( n ))
10523 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10524 while ( invIt->more() )
10526 const SMDS_MeshElement* eInv = invIt->next();
10527 if ( eInv->isMarked() ) continue;
10528 eInv->setIsMarked( true );
10530 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10531 while( nIt->more() )
10532 if ( nIt->next()->isMarked())
10534 theAffectedElems.insert( eInv );
10535 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10536 n->setIsMarked( false );
10543 // add elements on the fissure edge
10544 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10545 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10547 const SMDS_MeshNode* edgeNode = n2N->first;
10548 const FissureNormal & normals = n2N->second;
10550 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10551 while ( invIt->more() )
10553 const SMDS_MeshElement* eInv = invIt->next();
10554 if ( eInv->isMarked() ) continue;
10555 eInv->setIsMarked( true );
10557 // classify eInv using normals
10558 bool toAdd = normals.IsIn( edgeNode, eInv );
10559 if ( toAdd ) // check if all nodes lie on the fissure edge
10561 bool notOnEdge = false;
10562 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
10563 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10568 theAffectedElems.insert( eInv );
10574 } // findAffectedElems()
10577 //================================================================================
10579 * \brief Create elements equal (on same nodes) to given ones
10580 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10581 * elements of the uppest dimension are duplicated.
10583 //================================================================================
10585 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10587 ClearLastCreated();
10588 SMESHDS_Mesh* mesh = GetMeshDS();
10590 // get an element type and an iterator over elements
10592 SMDSAbs_ElementType type = SMDSAbs_All;
10593 SMDS_ElemIteratorPtr elemIt;
10594 if ( theElements.empty() )
10596 if ( mesh->NbNodes() == 0 )
10598 // get most complex type
10599 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10600 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10601 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10603 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10604 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10607 elemIt = mesh->elementsIterator( type );
10613 //type = (*theElements.begin())->GetType();
10614 elemIt = SMESHUtils::elemSetIterator( theElements );
10617 // un-mark all elements to avoid duplicating just created elements
10618 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10620 // duplicate elements
10622 ElemFeatures elemType;
10624 vector< const SMDS_MeshNode* > nodes;
10625 while ( elemIt->more() )
10627 const SMDS_MeshElement* elem = elemIt->next();
10628 if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10629 ( elem->isMarked() ))
10632 elemType.Init( elem, /*basicOnly=*/false );
10633 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10635 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10636 newElem->setIsMarked( true );
10640 //================================================================================
10642 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10643 \param theElems - the list of elements (edges or faces) to be replicated
10644 The nodes for duplication could be found from these elements
10645 \param theNodesNot - list of nodes to NOT replicate
10646 \param theAffectedElems - the list of elements (cells and edges) to which the
10647 replicated nodes should be associated to.
10648 \return TRUE if operation has been completed successfully, FALSE otherwise
10650 //================================================================================
10652 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10653 const TIDSortedElemSet& theNodesNot,
10654 const TIDSortedElemSet& theAffectedElems )
10656 ClearLastCreated();
10658 if ( theElems.size() == 0 )
10661 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10666 TNodeNodeMap anOldNodeToNewNode;
10667 // duplicate elements and nodes
10668 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10669 // replce nodes by duplications
10670 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10674 //================================================================================
10676 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10677 \param theMeshDS - mesh instance
10678 \param theElems - the elements replicated or modified (nodes should be changed)
10679 \param theNodesNot - nodes to NOT replicate
10680 \param theNodeNodeMap - relation of old node to new created node
10681 \param theIsDoubleElem - flag os to replicate element or modify
10682 \return TRUE if operation has been completed successfully, FALSE otherwise
10684 //================================================================================
10686 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10687 const TIDSortedElemSet& theElems,
10688 const TIDSortedElemSet& theNodesNot,
10689 TNodeNodeMap& theNodeNodeMap,
10690 const bool theIsDoubleElem )
10692 // iterate through element and duplicate them (by nodes duplication)
10694 std::vector<const SMDS_MeshNode*> newNodes;
10695 ElemFeatures elemType;
10697 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10698 for ( ; elemItr != theElems.end(); ++elemItr )
10700 const SMDS_MeshElement* anElem = *elemItr;
10704 // duplicate nodes to duplicate element
10705 bool isDuplicate = false;
10706 newNodes.resize( anElem->NbNodes() );
10707 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10709 while ( anIter->more() )
10711 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10712 const SMDS_MeshNode* aNewNode = aCurrNode;
10713 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10714 if ( n2n != theNodeNodeMap.end() )
10716 aNewNode = n2n->second;
10718 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10721 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10722 copyPosition( aCurrNode, aNewNode );
10723 theNodeNodeMap[ aCurrNode ] = aNewNode;
10724 myLastCreatedNodes.push_back( aNewNode );
10726 isDuplicate |= (aCurrNode != aNewNode);
10727 newNodes[ ind++ ] = aNewNode;
10729 if ( !isDuplicate )
10732 if ( theIsDoubleElem )
10733 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10735 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10742 //================================================================================
10744 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10745 \param theNodes - identifiers of nodes to be doubled
10746 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10747 nodes. If list of element identifiers is empty then nodes are doubled but
10748 they not assigned to elements
10749 \return TRUE if operation has been completed successfully, FALSE otherwise
10751 //================================================================================
10753 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10754 const std::list< int >& theListOfModifiedElems )
10756 ClearLastCreated();
10758 if ( theListOfNodes.size() == 0 )
10761 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10765 // iterate through nodes and duplicate them
10767 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10769 std::list< int >::const_iterator aNodeIter;
10770 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10772 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10778 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10781 copyPosition( aNode, aNewNode );
10782 anOldNodeToNewNode[ aNode ] = aNewNode;
10783 myLastCreatedNodes.push_back( aNewNode );
10787 // Change nodes of elements
10789 std::vector<const SMDS_MeshNode*> aNodeArr;
10791 std::list< int >::const_iterator anElemIter;
10792 for ( anElemIter = theListOfModifiedElems.begin();
10793 anElemIter != theListOfModifiedElems.end();
10796 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10800 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10801 for( size_t i = 0; i < aNodeArr.size(); ++i )
10803 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10804 anOldNodeToNewNode.find( aNodeArr[ i ]);
10805 if ( n2n != anOldNodeToNewNode.end() )
10806 aNodeArr[ i ] = n2n->second;
10808 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10816 //================================================================================
10818 \brief Check if element located inside shape
10819 \return TRUE if IN or ON shape, FALSE otherwise
10821 //================================================================================
10823 template<class Classifier>
10824 bool isInside(const SMDS_MeshElement* theElem,
10825 Classifier& theClassifier,
10826 const double theTol)
10828 gp_XYZ centerXYZ (0, 0, 0);
10829 for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10830 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10832 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10833 theClassifier.Perform(aPnt, theTol);
10834 TopAbs_State aState = theClassifier.State();
10835 return (aState == TopAbs_IN || aState == TopAbs_ON );
10838 //================================================================================
10840 * \brief Classifier of the 3D point on the TopoDS_Face
10841 * with interaface suitable for isInside()
10843 //================================================================================
10845 struct _FaceClassifier
10847 Extrema_ExtPS _extremum;
10848 BRepAdaptor_Surface _surface;
10849 TopAbs_State _state;
10851 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10853 _extremum.Initialize( _surface,
10854 _surface.FirstUParameter(), _surface.LastUParameter(),
10855 _surface.FirstVParameter(), _surface.LastVParameter(),
10856 _surface.Tolerance(), _surface.Tolerance() );
10858 void Perform(const gp_Pnt& aPnt, double theTol)
10861 _state = TopAbs_OUT;
10862 _extremum.Perform(aPnt);
10863 if ( _extremum.IsDone() )
10864 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10865 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10867 TopAbs_State State() const
10874 //================================================================================
10876 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10877 This method is the first step of DoubleNodeElemGroupsInRegion.
10878 \param theElems - list of groups of elements (edges or faces) to be replicated
10879 \param theNodesNot - list of groups of nodes not to replicated
10880 \param theShape - shape to detect affected elements (element which geometric center
10881 located on or inside shape). If the shape is null, detection is done on faces orientations
10882 (select elements with a gravity center on the side given by faces normals).
10883 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10884 The replicated nodes should be associated to affected elements.
10886 \sa DoubleNodeElemGroupsInRegion()
10888 //================================================================================
10890 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10891 const TIDSortedElemSet& theNodesNot,
10892 const TopoDS_Shape& theShape,
10893 TIDSortedElemSet& theAffectedElems)
10895 if ( theShape.IsNull() )
10897 findAffectedElems( theElems, theAffectedElems );
10901 const double aTol = Precision::Confusion();
10902 std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10903 std::unique_ptr<_FaceClassifier> aFaceClassifier;
10904 if ( theShape.ShapeType() == TopAbs_SOLID )
10906 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10907 bsc3d->PerformInfinitePoint(aTol);
10909 else if (theShape.ShapeType() == TopAbs_FACE )
10911 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10914 // iterates on indicated elements and get elements by back references from their nodes
10915 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10916 for ( ; elemItr != theElems.end(); ++elemItr )
10918 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10919 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10920 while ( nodeItr->more() )
10922 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10923 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10925 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10926 while ( backElemItr->more() )
10928 const SMDS_MeshElement* curElem = backElemItr->next();
10929 if ( curElem && theElems.find(curElem) == theElems.end() &&
10931 isInside( curElem, *bsc3d, aTol ) :
10932 isInside( curElem, *aFaceClassifier, aTol )))
10933 theAffectedElems.insert( curElem );
10941 //================================================================================
10943 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10944 \param theElems - group of of elements (edges or faces) to be replicated
10945 \param theNodesNot - group of nodes not to replicate
10946 \param theShape - shape to detect affected elements (element which geometric center
10947 located on or inside shape).
10948 The replicated nodes should be associated to affected elements.
10949 \return TRUE if operation has been completed successfully, FALSE otherwise
10951 //================================================================================
10953 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10954 const TIDSortedElemSet& theNodesNot,
10955 const TopoDS_Shape& theShape )
10957 if ( theShape.IsNull() )
10960 const double aTol = Precision::Confusion();
10961 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10962 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
10963 if ( theShape.ShapeType() == TopAbs_SOLID )
10965 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10966 bsc3d->PerformInfinitePoint(aTol);
10968 else if (theShape.ShapeType() == TopAbs_FACE )
10970 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
10973 // iterates on indicated elements and get elements by back references from their nodes
10974 TIDSortedElemSet anAffected;
10975 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10976 for ( ; elemItr != theElems.end(); ++elemItr )
10978 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10982 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10983 while ( nodeItr->more() )
10985 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10986 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10988 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10989 while ( backElemItr->more() )
10991 const SMDS_MeshElement* curElem = backElemItr->next();
10992 if ( curElem && theElems.find(curElem) == theElems.end() &&
10994 isInside( curElem, *bsc3d, aTol ) :
10995 isInside( curElem, *aFaceClassifier, aTol )))
10996 anAffected.insert( curElem );
11000 return DoubleNodes( theElems, theNodesNot, anAffected );
11004 * \brief compute an oriented angle between two planes defined by four points.
11005 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11006 * @param p0 base of the rotation axe
11007 * @param p1 extremity of the rotation axe
11008 * @param g1 belongs to the first plane
11009 * @param g2 belongs to the second plane
11011 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11013 gp_Vec vref(p0, p1);
11016 gp_Vec n1 = vref.Crossed(v1);
11017 gp_Vec n2 = vref.Crossed(v2);
11019 return n2.AngleWithRef(n1, vref);
11021 catch ( Standard_Failure ) {
11023 return Max( v1.Magnitude(), v2.Magnitude() );
11027 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11028 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11029 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11030 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11031 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11032 * 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.
11033 * 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.
11034 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11035 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11036 * \param theElems - list of groups of volumes, where a group of volume is a set of
11037 * SMDS_MeshElements sorted by Id.
11038 * \param createJointElems - if TRUE, create the elements
11039 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11040 * the boundary between \a theDomains and the rest mesh
11041 * \return TRUE if operation has been completed successfully, FALSE otherwise
11043 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11044 bool createJointElems,
11045 bool onAllBoundaries)
11047 // MESSAGE("----------------------------------------------");
11048 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11049 // MESSAGE("----------------------------------------------");
11051 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11052 meshDS->BuildDownWardConnectivity(true);
11054 SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11056 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11057 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11058 // build the list of nodes shared by 2 or more domains, with their domain indexes
11060 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11061 std::map<int,int>celldom; // cell vtkId --> domain
11062 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11063 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11064 faceDomains.clear();
11066 cellDomains.clear();
11067 nodeDomains.clear();
11068 std::map<int,int> emptyMap;
11069 std::set<int> emptySet;
11072 //MESSAGE(".. Number of domains :"<<theElems.size());
11074 TIDSortedElemSet theRestDomElems;
11075 const int iRestDom = -1;
11076 const int idom0 = onAllBoundaries ? iRestDom : 0;
11077 const int nbDomains = theElems.size();
11079 // Check if the domains do not share an element
11080 for (int idom = 0; idom < nbDomains-1; idom++)
11082 // MESSAGE("... Check of domain #" << idom);
11083 const TIDSortedElemSet& domain = theElems[idom];
11084 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11085 for (; elemItr != domain.end(); ++elemItr)
11087 const SMDS_MeshElement* anElem = *elemItr;
11088 int idombisdeb = idom + 1 ;
11089 // check if the element belongs to a domain further in the list
11090 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11092 const TIDSortedElemSet& domainbis = theElems[idombis];
11093 if ( domainbis.count( anElem ))
11095 MESSAGE(".... Domain #" << idom);
11096 MESSAGE(".... Domain #" << idombis);
11097 throw SALOME_Exception("The domains are not disjoint.");
11104 for (int idom = 0; idom < nbDomains; idom++)
11107 // --- build a map (face to duplicate --> volume to modify)
11108 // with all the faces shared by 2 domains (group of elements)
11109 // and corresponding volume of this domain, for each shared face.
11110 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11112 //MESSAGE("... Neighbors of domain #" << idom);
11113 const TIDSortedElemSet& domain = theElems[idom];
11114 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11115 for (; elemItr != domain.end(); ++elemItr)
11117 const SMDS_MeshElement* anElem = *elemItr;
11120 int vtkId = anElem->GetVtkID();
11121 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11122 int neighborsVtkIds[NBMAXNEIGHBORS];
11123 int downIds[NBMAXNEIGHBORS];
11124 unsigned char downTypes[NBMAXNEIGHBORS];
11125 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11126 for (int n = 0; n < nbNeighbors; n++)
11128 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11129 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11130 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11133 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11135 // MESSAGE("Domain " << idombis);
11136 const TIDSortedElemSet& domainbis = theElems[idombis];
11137 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11139 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11141 DownIdType face(downIds[n], downTypes[n]);
11142 if (!faceDomains[face].count(idom))
11144 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11145 celldom[vtkId] = idom;
11146 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11150 theRestDomElems.insert( elem );
11151 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11152 celldom[neighborsVtkIds[n]] = iRestDom;
11160 //MESSAGE("Number of shared faces " << faceDomains.size());
11161 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11163 // --- explore the shared faces domain by domain,
11164 // explore the nodes of the face and see if they belong to a cell in the domain,
11165 // which has only a node or an edge on the border (not a shared face)
11167 for (int idomain = idom0; idomain < nbDomains; idomain++)
11169 //MESSAGE("Domain " << idomain);
11170 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11171 itface = faceDomains.begin();
11172 for (; itface != faceDomains.end(); ++itface)
11174 const std::map<int, int>& domvol = itface->second;
11175 if (!domvol.count(idomain))
11177 DownIdType face = itface->first;
11178 //MESSAGE(" --- face " << face.cellId);
11179 std::set<int> oldNodes;
11181 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11182 std::set<int>::iterator itn = oldNodes.begin();
11183 for (; itn != oldNodes.end(); ++itn)
11186 //MESSAGE(" node " << oldId);
11187 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11188 for (int i=0; i<l.ncells; i++)
11190 int vtkId = l.cells[i];
11191 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11192 if (!domain.count(anElem))
11194 int vtkType = grid->GetCellType(vtkId);
11195 int downId = grid->CellIdToDownId(vtkId);
11198 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11199 continue; // not OK at this stage of the algorithm:
11200 //no cells created after BuildDownWardConnectivity
11202 DownIdType aCell(downId, vtkType);
11203 cellDomains[aCell][idomain] = vtkId;
11204 celldom[vtkId] = idomain;
11205 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11211 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11212 // for each shared face, get the nodes
11213 // for each node, for each domain of the face, create a clone of the node
11215 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11216 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11217 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11219 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11220 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11221 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11223 //MESSAGE(".. Duplication of the nodes");
11224 for (int idomain = idom0; idomain < nbDomains; idomain++)
11226 itface = faceDomains.begin();
11227 for (; itface != faceDomains.end(); ++itface)
11229 const std::map<int, int>& domvol = itface->second;
11230 if (!domvol.count(idomain))
11232 DownIdType face = itface->first;
11233 //MESSAGE(" --- face " << face.cellId);
11234 std::set<int> oldNodes;
11236 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11237 std::set<int>::iterator itn = oldNodes.begin();
11238 for (; itn != oldNodes.end(); ++itn)
11241 if (nodeDomains[oldId].empty())
11243 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11244 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11246 std::map<int, int>::const_iterator itdom = domvol.begin();
11247 for (; itdom != domvol.end(); ++itdom)
11249 int idom = itdom->first;
11250 //MESSAGE(" domain " << idom);
11251 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11253 if (nodeDomains[oldId].size() >= 2) // a multiple node
11255 vector<int> orderedDoms;
11256 //MESSAGE("multiple node " << oldId);
11257 if (mutipleNodes.count(oldId))
11258 orderedDoms = mutipleNodes[oldId];
11261 map<int,int>::iterator it = nodeDomains[oldId].begin();
11262 for (; it != nodeDomains[oldId].end(); ++it)
11263 orderedDoms.push_back(it->first);
11265 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11266 //stringstream txt;
11267 //for (int i=0; i<orderedDoms.size(); i++)
11268 // txt << orderedDoms[i] << " ";
11269 //MESSAGE("orderedDoms " << txt.str());
11270 mutipleNodes[oldId] = orderedDoms;
11272 double *coords = grid->GetPoint(oldId);
11273 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11274 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11275 int newId = newNode->GetVtkID();
11276 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11277 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11284 //MESSAGE(".. Creation of elements");
11285 for (int idomain = idom0; idomain < nbDomains; idomain++)
11287 itface = faceDomains.begin();
11288 for (; itface != faceDomains.end(); ++itface)
11290 std::map<int, int> domvol = itface->second;
11291 if (!domvol.count(idomain))
11293 DownIdType face = itface->first;
11294 //MESSAGE(" --- face " << face.cellId);
11295 std::set<int> oldNodes;
11297 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11298 int nbMultipleNodes = 0;
11299 std::set<int>::iterator itn = oldNodes.begin();
11300 for (; itn != oldNodes.end(); ++itn)
11303 if (mutipleNodes.count(oldId))
11306 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11308 //MESSAGE("multiple Nodes detected on a shared face");
11309 int downId = itface->first.cellId;
11310 unsigned char cellType = itface->first.cellType;
11311 // --- shared edge or shared face ?
11312 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11315 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11316 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11317 if (mutipleNodes.count(nodes[i]))
11318 if (!mutipleNodesToFace.count(nodes[i]))
11319 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11321 else // shared face (between two volumes)
11323 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11324 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11325 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11326 for (int ie =0; ie < nbEdges; ie++)
11329 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11330 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11332 vector<int> vn0 = mutipleNodes[nodes[0]];
11333 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11335 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11336 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11337 if ( vn0[i0] == vn1[i1] )
11338 doms.push_back( vn0[ i0 ]);
11339 if ( doms.size() > 2 )
11341 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11342 double *coords = grid->GetPoint(nodes[0]);
11343 gp_Pnt p0(coords[0], coords[1], coords[2]);
11344 coords = grid->GetPoint(nodes[nbNodes - 1]);
11345 gp_Pnt p1(coords[0], coords[1], coords[2]);
11347 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11348 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11349 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11350 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11351 for ( size_t id = 0; id < doms.size(); id++ )
11353 int idom = doms[id];
11354 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11355 for ( int ivol = 0; ivol < nbvol; ivol++ )
11357 int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11358 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11359 if (domain.count(elem))
11361 const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11362 domvol[idom] = (SMDS_MeshVolume*) svol;
11363 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11364 double values[3] = { 0,0,0 };
11365 vtkIdType npts = 0;
11366 vtkIdType* pts = 0;
11367 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11368 for ( vtkIdType i = 0; i < npts; ++i )
11370 double *coords = grid->GetPoint( pts[i] );
11371 for ( int j = 0; j < 3; ++j )
11372 values[j] += coords[j] / npts;
11376 gref.SetCoord( values[0], values[1], values[2] );
11377 angleDom[idom] = 0;
11381 gp_Pnt g( values[0], values[1], values[2] );
11382 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11383 //MESSAGE(" angle=" << angleDom[idom]);
11389 map<double, int> sortedDom; // sort domains by angle
11390 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11391 sortedDom[ia->second] = ia->first;
11392 vector<int> vnodes;
11394 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11396 vdom.push_back(ib->second);
11397 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11399 for (int ino = 0; ino < nbNodes; ino++)
11400 vnodes.push_back(nodes[ino]);
11401 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11410 // --- iterate on shared faces (volumes to modify, face to extrude)
11411 // get node id's of the face (id SMDS = id VTK)
11412 // create flat element with old and new nodes if requested
11414 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11415 // (domain1 X domain2) = domain1 + MAXINT*domain2
11417 std::map<int, std::map<long,int> > nodeQuadDomains;
11418 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11420 //MESSAGE(".. Creation of elements: simple junction");
11421 if (createJointElems)
11423 string joints2DName = "joints2D";
11424 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11425 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11426 string joints3DName = "joints3D";
11427 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11428 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11430 itface = faceDomains.begin();
11431 for (; itface != faceDomains.end(); ++itface)
11433 DownIdType face = itface->first;
11434 std::set<int> oldNodes;
11435 std::set<int>::iterator itn;
11437 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11439 std::map<int, int> domvol = itface->second;
11440 std::map<int, int>::iterator itdom = domvol.begin();
11441 int dom1 = itdom->first;
11442 int vtkVolId = itdom->second;
11444 int dom2 = itdom->first;
11445 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11447 stringstream grpname;
11450 grpname << dom1 << "_" << dom2;
11452 grpname << dom2 << "_" << dom1;
11453 string namegrp = grpname.str();
11454 if (!mapOfJunctionGroups.count(namegrp))
11455 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11456 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11458 sgrp->Add(vol->GetID());
11459 if (vol->GetType() == SMDSAbs_Volume)
11460 joints3DGrp->Add(vol->GetID());
11461 else if (vol->GetType() == SMDSAbs_Face)
11462 joints2DGrp->Add(vol->GetID());
11466 // --- create volumes on multiple domain intersection if requested
11467 // iterate on mutipleNodesToFace
11468 // iterate on edgesMultiDomains
11470 //MESSAGE(".. Creation of elements: multiple junction");
11471 if (createJointElems)
11473 // --- iterate on mutipleNodesToFace
11475 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11476 for (; itn != mutipleNodesToFace.end(); ++itn)
11478 int node = itn->first;
11479 vector<int> orderDom = itn->second;
11480 vector<vtkIdType> orderedNodes;
11481 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11482 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11483 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11485 stringstream grpname;
11487 grpname << 0 << "_" << 0;
11488 string namegrp = grpname.str();
11489 if (!mapOfJunctionGroups.count(namegrp))
11490 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11491 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11493 sgrp->Add(face->GetID());
11496 // --- iterate on edgesMultiDomains
11498 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11499 for (; ite != edgesMultiDomains.end(); ++ite)
11501 vector<int> nodes = ite->first;
11502 vector<int> orderDom = ite->second;
11503 vector<vtkIdType> orderedNodes;
11504 if (nodes.size() == 2)
11506 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11507 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11508 if ( orderDom.size() == 3 )
11509 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11510 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11512 for (int idom = orderDom.size()-1; idom >=0; idom--)
11513 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11514 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11516 string namegrp = "jointsMultiples";
11517 if (!mapOfJunctionGroups.count(namegrp))
11518 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11519 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11521 sgrp->Add(vol->GetID());
11525 //INFOS("Quadratic multiple joints not implemented");
11526 // TODO quadratic nodes
11531 // --- list the explicit faces and edges of the mesh that need to be modified,
11532 // i.e. faces and edges built with one or more duplicated nodes.
11533 // associate these faces or edges to their corresponding domain.
11534 // only the first domain found is kept when a face or edge is shared
11536 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11537 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11538 faceOrEdgeDom.clear();
11541 //MESSAGE(".. Modification of elements");
11542 for (int idomain = idom0; idomain < nbDomains; idomain++)
11544 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11545 for (; itnod != nodeDomains.end(); ++itnod)
11547 int oldId = itnod->first;
11548 //MESSAGE(" node " << oldId);
11549 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11550 for (int i = 0; i < l.ncells; i++)
11552 int vtkId = l.cells[i];
11553 int vtkType = grid->GetCellType(vtkId);
11554 int downId = grid->CellIdToDownId(vtkId);
11556 continue; // new cells: not to be modified
11557 DownIdType aCell(downId, vtkType);
11558 int volParents[1000];
11559 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11560 for (int j = 0; j < nbvol; j++)
11561 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11562 if (!feDom.count(vtkId))
11564 feDom[vtkId] = idomain;
11565 faceOrEdgeDom[aCell] = emptyMap;
11566 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11567 //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11568 // << " type " << vtkType << " downId " << downId);
11574 // --- iterate on shared faces (volumes to modify, face to extrude)
11575 // get node id's of the face
11576 // replace old nodes by new nodes in volumes, and update inverse connectivity
11578 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11579 for (int m=0; m<3; m++)
11581 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11582 itface = (*amap).begin();
11583 for (; itface != (*amap).end(); ++itface)
11585 DownIdType face = itface->first;
11586 std::set<int> oldNodes;
11587 std::set<int>::iterator itn;
11589 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11590 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11591 std::map<int, int> localClonedNodeIds;
11593 std::map<int, int> domvol = itface->second;
11594 std::map<int, int>::iterator itdom = domvol.begin();
11595 for (; itdom != domvol.end(); ++itdom)
11597 int idom = itdom->first;
11598 int vtkVolId = itdom->second;
11599 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11600 localClonedNodeIds.clear();
11601 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11604 if (nodeDomains[oldId].count(idom))
11606 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11607 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11610 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11615 // Remove empty groups (issue 0022812)
11616 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11617 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11619 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11620 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11623 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11624 grid->DeleteLinks();
11632 * \brief Double nodes on some external faces and create flat elements.
11633 * Flat elements are mainly used by some types of mechanic calculations.
11635 * Each group of the list must be constituted of faces.
11636 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11637 * @param theElems - list of groups of faces, where a group of faces is a set of
11638 * SMDS_MeshElements sorted by Id.
11639 * @return TRUE if operation has been completed successfully, FALSE otherwise
11641 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11643 // MESSAGE("-------------------------------------------------");
11644 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11645 // MESSAGE("-------------------------------------------------");
11647 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11649 // --- For each group of faces
11650 // duplicate the nodes, create a flat element based on the face
11651 // replace the nodes of the faces by their clones
11653 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11654 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11655 clonedNodes.clear();
11656 intermediateNodes.clear();
11657 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11658 mapOfJunctionGroups.clear();
11660 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11662 const TIDSortedElemSet& domain = theElems[idom];
11663 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11664 for ( ; elemItr != domain.end(); ++elemItr )
11666 const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11669 // MESSAGE("aFace=" << aFace->GetID());
11670 bool isQuad = aFace->IsQuadratic();
11671 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11673 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11675 SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11676 while (nodeIt->more())
11678 const SMDS_MeshNode* node = nodeIt->next();
11679 bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11681 ln2.push_back(node);
11683 ln0.push_back(node);
11685 const SMDS_MeshNode* clone = 0;
11686 if (!clonedNodes.count(node))
11688 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11689 copyPosition( node, clone );
11690 clonedNodes[node] = clone;
11693 clone = clonedNodes[node];
11696 ln3.push_back(clone);
11698 ln1.push_back(clone);
11700 const SMDS_MeshNode* inter = 0;
11701 if (isQuad && (!isMedium))
11703 if (!intermediateNodes.count(node))
11705 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11706 copyPosition( node, inter );
11707 intermediateNodes[node] = inter;
11710 inter = intermediateNodes[node];
11711 ln4.push_back(inter);
11715 // --- extrude the face
11717 vector<const SMDS_MeshNode*> ln;
11718 SMDS_MeshVolume* vol = 0;
11719 vtkIdType aType = aFace->GetVtkType();
11723 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11724 // MESSAGE("vol prism " << vol->GetID());
11725 ln.push_back(ln1[0]);
11726 ln.push_back(ln1[1]);
11727 ln.push_back(ln1[2]);
11730 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11731 // MESSAGE("vol hexa " << vol->GetID());
11732 ln.push_back(ln1[0]);
11733 ln.push_back(ln1[1]);
11734 ln.push_back(ln1[2]);
11735 ln.push_back(ln1[3]);
11737 case VTK_QUADRATIC_TRIANGLE:
11738 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11739 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11740 // MESSAGE("vol quad prism " << vol->GetID());
11741 ln.push_back(ln1[0]);
11742 ln.push_back(ln1[1]);
11743 ln.push_back(ln1[2]);
11744 ln.push_back(ln3[0]);
11745 ln.push_back(ln3[1]);
11746 ln.push_back(ln3[2]);
11748 case VTK_QUADRATIC_QUAD:
11749 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11750 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11751 // ln4[0], ln4[1], ln4[2], ln4[3]);
11752 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11753 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11754 ln4[0], ln4[1], ln4[2], ln4[3]);
11755 // MESSAGE("vol quad hexa " << vol->GetID());
11756 ln.push_back(ln1[0]);
11757 ln.push_back(ln1[1]);
11758 ln.push_back(ln1[2]);
11759 ln.push_back(ln1[3]);
11760 ln.push_back(ln3[0]);
11761 ln.push_back(ln3[1]);
11762 ln.push_back(ln3[2]);
11763 ln.push_back(ln3[3]);
11773 stringstream grpname;
11776 string namegrp = grpname.str();
11777 if (!mapOfJunctionGroups.count(namegrp))
11778 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11779 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11781 sgrp->Add(vol->GetID());
11784 // --- modify the face
11786 const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11793 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11794 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11795 * groups of faces to remove inside the object, (idem edges).
11796 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11798 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11799 const TopoDS_Shape& theShape,
11800 SMESH_NodeSearcher* theNodeSearcher,
11801 const char* groupName,
11802 std::vector<double>& nodesCoords,
11803 std::vector<std::vector<int> >& listOfListOfNodes)
11805 // MESSAGE("--------------------------------");
11806 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11807 // MESSAGE("--------------------------------");
11809 // --- zone of volumes to remove is given :
11810 // 1 either by a geom shape (one or more vertices) and a radius,
11811 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11812 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11813 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11814 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11815 // defined by it's name.
11817 SMESHDS_GroupBase* groupDS = 0;
11818 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11819 while ( groupIt->more() )
11822 SMESH_Group * group = groupIt->next();
11823 if ( !group ) continue;
11824 groupDS = group->GetGroupDS();
11825 if ( !groupDS || groupDS->IsEmpty() ) continue;
11826 std::string grpName = group->GetName();
11827 //MESSAGE("grpName=" << grpName);
11828 if (grpName == groupName)
11834 bool isNodeGroup = false;
11835 bool isNodeCoords = false;
11838 if (groupDS->GetType() != SMDSAbs_Node)
11840 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11843 if (nodesCoords.size() > 0)
11844 isNodeCoords = true; // a list o nodes given by their coordinates
11845 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11847 // --- define groups to build
11849 // --- group of SMDS volumes
11850 string grpvName = groupName;
11851 grpvName += "_vol";
11852 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11855 MESSAGE("group not created " << grpvName);
11858 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11860 // --- group of SMDS faces on the skin
11861 string grpsName = groupName;
11862 grpsName += "_skin";
11863 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11866 MESSAGE("group not created " << grpsName);
11869 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11871 // --- group of SMDS faces internal (several shapes)
11872 string grpiName = groupName;
11873 grpiName += "_internalFaces";
11874 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11877 MESSAGE("group not created " << grpiName);
11880 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11882 // --- group of SMDS faces internal (several shapes)
11883 string grpeiName = groupName;
11884 grpeiName += "_internalEdges";
11885 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11888 MESSAGE("group not created " << grpeiName);
11891 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11893 // --- build downward connectivity
11895 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11896 meshDS->BuildDownWardConnectivity(true);
11897 SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11899 // --- set of volumes detected inside
11901 std::set<int> setOfInsideVol;
11902 std::set<int> setOfVolToCheck;
11904 std::vector<gp_Pnt> gpnts;
11907 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11909 //MESSAGE("group of nodes provided");
11910 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11911 while ( elemIt->more() )
11913 const SMDS_MeshElement* elem = elemIt->next();
11916 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11919 SMDS_MeshElement* vol = 0;
11920 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11921 while (volItr->more())
11923 vol = (SMDS_MeshElement*)volItr->next();
11924 setOfInsideVol.insert(vol->GetVtkID());
11925 sgrp->Add(vol->GetID());
11929 else if (isNodeCoords)
11931 //MESSAGE("list of nodes coordinates provided");
11934 while ( i < nodesCoords.size()-2 )
11936 double x = nodesCoords[i++];
11937 double y = nodesCoords[i++];
11938 double z = nodesCoords[i++];
11939 gp_Pnt p = gp_Pnt(x, y ,z);
11940 gpnts.push_back(p);
11941 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11945 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11947 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11948 TopTools_IndexedMapOfShape vertexMap;
11949 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11950 gp_Pnt p = gp_Pnt(0,0,0);
11951 if (vertexMap.Extent() < 1)
11954 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11956 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11957 p = BRep_Tool::Pnt(vertex);
11958 gpnts.push_back(p);
11959 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11963 if (gpnts.size() > 0)
11965 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11966 //MESSAGE("startNode->nodeId " << nodeId);
11968 double radius2 = radius*radius;
11969 //MESSAGE("radius2 " << radius2);
11971 // --- volumes on start node
11973 setOfVolToCheck.clear();
11974 SMDS_MeshElement* startVol = 0;
11975 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11976 while (volItr->more())
11978 startVol = (SMDS_MeshElement*)volItr->next();
11979 setOfVolToCheck.insert(startVol->GetVtkID());
11981 if (setOfVolToCheck.empty())
11983 MESSAGE("No volumes found");
11987 // --- starting with central volumes then their neighbors, check if they are inside
11988 // or outside the domain, until no more new neighbor volume is inside.
11989 // Fill the group of inside volumes
11991 std::map<int, double> mapOfNodeDistance2;
11992 mapOfNodeDistance2.clear();
11993 std::set<int> setOfOutsideVol;
11994 while (!setOfVolToCheck.empty())
11996 std::set<int>::iterator it = setOfVolToCheck.begin();
11998 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
11999 bool volInside = false;
12000 vtkIdType npts = 0;
12001 vtkIdType* pts = 0;
12002 grid->GetCellPoints(vtkId, npts, pts);
12003 for (int i=0; i<npts; i++)
12005 double distance2 = 0;
12006 if (mapOfNodeDistance2.count(pts[i]))
12008 distance2 = mapOfNodeDistance2[pts[i]];
12009 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12013 double *coords = grid->GetPoint(pts[i]);
12014 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12016 for ( size_t j = 0; j < gpnts.size(); j++ )
12018 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12019 if (d2 < distance2)
12022 if (distance2 < radius2)
12026 mapOfNodeDistance2[pts[i]] = distance2;
12027 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12029 if (distance2 < radius2)
12031 volInside = true; // one or more nodes inside the domain
12032 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12038 setOfInsideVol.insert(vtkId);
12039 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12040 int neighborsVtkIds[NBMAXNEIGHBORS];
12041 int downIds[NBMAXNEIGHBORS];
12042 unsigned char downTypes[NBMAXNEIGHBORS];
12043 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12044 for (int n = 0; n < nbNeighbors; n++)
12045 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12046 setOfVolToCheck.insert(neighborsVtkIds[n]);
12050 setOfOutsideVol.insert(vtkId);
12051 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12053 setOfVolToCheck.erase(vtkId);
12057 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12058 // If yes, add the volume to the inside set
12060 bool addedInside = true;
12061 std::set<int> setOfVolToReCheck;
12062 while (addedInside)
12064 //MESSAGE(" --------------------------- re check");
12065 addedInside = false;
12066 std::set<int>::iterator itv = setOfInsideVol.begin();
12067 for (; itv != setOfInsideVol.end(); ++itv)
12070 int neighborsVtkIds[NBMAXNEIGHBORS];
12071 int downIds[NBMAXNEIGHBORS];
12072 unsigned char downTypes[NBMAXNEIGHBORS];
12073 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12074 for (int n = 0; n < nbNeighbors; n++)
12075 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12076 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12078 setOfVolToCheck = setOfVolToReCheck;
12079 setOfVolToReCheck.clear();
12080 while (!setOfVolToCheck.empty())
12082 std::set<int>::iterator it = setOfVolToCheck.begin();
12084 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12086 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12087 int countInside = 0;
12088 int neighborsVtkIds[NBMAXNEIGHBORS];
12089 int downIds[NBMAXNEIGHBORS];
12090 unsigned char downTypes[NBMAXNEIGHBORS];
12091 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12092 for (int n = 0; n < nbNeighbors; n++)
12093 if (setOfInsideVol.count(neighborsVtkIds[n]))
12095 //MESSAGE("countInside " << countInside);
12096 if (countInside > 1)
12098 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12099 setOfInsideVol.insert(vtkId);
12100 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12101 addedInside = true;
12104 setOfVolToReCheck.insert(vtkId);
12106 setOfVolToCheck.erase(vtkId);
12110 // --- map of Downward faces at the boundary, inside the global volume
12111 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12112 // fill group of SMDS faces inside the volume (when several volume shapes)
12113 // fill group of SMDS faces on the skin of the global volume (if skin)
12115 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12116 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12117 std::set<int>::iterator it = setOfInsideVol.begin();
12118 for (; it != setOfInsideVol.end(); ++it)
12121 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12122 int neighborsVtkIds[NBMAXNEIGHBORS];
12123 int downIds[NBMAXNEIGHBORS];
12124 unsigned char downTypes[NBMAXNEIGHBORS];
12125 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12126 for (int n = 0; n < nbNeighbors; n++)
12128 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12129 if (neighborDim == 3)
12131 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12133 DownIdType face(downIds[n], downTypes[n]);
12134 boundaryFaces[face] = vtkId;
12136 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12137 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12138 if (vtkFaceId >= 0)
12140 sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12141 // find also the smds edges on this face
12142 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12143 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12144 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12145 for (int i = 0; i < nbEdges; i++)
12147 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12148 if (vtkEdgeId >= 0)
12149 sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12153 else if (neighborDim == 2) // skin of the volume
12155 DownIdType face(downIds[n], downTypes[n]);
12156 skinFaces[face] = vtkId;
12157 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12158 if (vtkFaceId >= 0)
12159 sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12164 // --- identify the edges constituting the wire of each subshape on the skin
12165 // define polylines with the nodes of edges, equivalent to wires
12166 // project polylines on subshapes, and partition, to get geom faces
12168 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12169 std::set<int> emptySet;
12171 std::set<int> shapeIds;
12173 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12174 while (itelem->more())
12176 const SMDS_MeshElement *elem = itelem->next();
12177 int shapeId = elem->getshapeId();
12178 int vtkId = elem->GetVtkID();
12179 if (!shapeIdToVtkIdSet.count(shapeId))
12181 shapeIdToVtkIdSet[shapeId] = emptySet;
12182 shapeIds.insert(shapeId);
12184 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12187 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12188 std::set<DownIdType, DownIdCompare> emptyEdges;
12189 emptyEdges.clear();
12191 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12192 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12194 int shapeId = itShape->first;
12195 //MESSAGE(" --- Shape ID --- "<< shapeId);
12196 shapeIdToEdges[shapeId] = emptyEdges;
12198 std::vector<int> nodesEdges;
12200 std::set<int>::iterator its = itShape->second.begin();
12201 for (; its != itShape->second.end(); ++its)
12204 //MESSAGE(" " << vtkId);
12205 int neighborsVtkIds[NBMAXNEIGHBORS];
12206 int downIds[NBMAXNEIGHBORS];
12207 unsigned char downTypes[NBMAXNEIGHBORS];
12208 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12209 for (int n = 0; n < nbNeighbors; n++)
12211 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12213 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12214 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12215 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12217 DownIdType edge(downIds[n], downTypes[n]);
12218 if (!shapeIdToEdges[shapeId].count(edge))
12220 shapeIdToEdges[shapeId].insert(edge);
12222 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12223 nodesEdges.push_back(vtkNodeId[0]);
12224 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12225 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12231 std::list<int> order;
12233 if (nodesEdges.size() > 0)
12235 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12236 nodesEdges[0] = -1;
12237 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12238 nodesEdges[1] = -1; // do not reuse this edge
12242 int nodeTofind = order.back(); // try first to push back
12244 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12245 if (nodesEdges[i] == nodeTofind)
12247 if ( i == (int) nodesEdges.size() )
12248 found = false; // no follower found on back
12251 if (i%2) // odd ==> use the previous one
12252 if (nodesEdges[i-1] < 0)
12256 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12257 nodesEdges[i-1] = -1;
12259 else // even ==> use the next one
12260 if (nodesEdges[i+1] < 0)
12264 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12265 nodesEdges[i+1] = -1;
12270 // try to push front
12272 nodeTofind = order.front(); // try to push front
12273 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12274 if ( nodesEdges[i] == nodeTofind )
12276 if ( i == (int)nodesEdges.size() )
12278 found = false; // no predecessor found on front
12281 if (i%2) // odd ==> use the previous one
12282 if (nodesEdges[i-1] < 0)
12286 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12287 nodesEdges[i-1] = -1;
12289 else // even ==> use the next one
12290 if (nodesEdges[i+1] < 0)
12294 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12295 nodesEdges[i+1] = -1;
12301 std::vector<int> nodes;
12302 nodes.push_back(shapeId);
12303 std::list<int>::iterator itl = order.begin();
12304 for (; itl != order.end(); itl++)
12306 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12307 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12309 listOfListOfNodes.push_back(nodes);
12312 // partition geom faces with blocFissure
12313 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12314 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12320 //================================================================================
12322 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12323 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12324 * \return TRUE if operation has been completed successfully, FALSE otherwise
12326 //================================================================================
12328 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12330 // iterates on volume elements and detect all free faces on them
12331 SMESHDS_Mesh* aMesh = GetMeshDS();
12335 ElemFeatures faceType( SMDSAbs_Face );
12336 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12337 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12340 const SMDS_MeshVolume* volume = vIt->next();
12341 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12342 vTool.SetExternalNormal();
12343 const int iQuad = volume->IsQuadratic();
12344 faceType.SetQuad( iQuad );
12345 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12347 if (!vTool.IsFreeFace(iface))
12350 vector<const SMDS_MeshNode *> nodes;
12351 int nbFaceNodes = vTool.NbFaceNodes(iface);
12352 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12354 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12355 nodes.push_back(faceNodes[inode]);
12357 if (iQuad) // add medium nodes
12359 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12360 nodes.push_back(faceNodes[inode]);
12361 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12362 nodes.push_back(faceNodes[8]);
12364 // add new face based on volume nodes
12365 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12367 nbExisted++; // face already exists
12371 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12376 return ( nbFree == ( nbExisted + nbCreated ));
12381 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12383 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12385 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12388 //================================================================================
12390 * \brief Creates missing boundary elements
12391 * \param elements - elements whose boundary is to be checked
12392 * \param dimension - defines type of boundary elements to create
12393 * \param group - a group to store created boundary elements in
12394 * \param targetMesh - a mesh to store created boundary elements in
12395 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12396 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12397 * boundary elements will be copied into the targetMesh
12398 * \param toAddExistingBondary - if true, not only new but also pre-existing
12399 * boundary elements will be added into the new group
12400 * \param aroundElements - if true, elements will be created on boundary of given
12401 * elements else, on boundary of the whole mesh.
12402 * \return nb of added boundary elements
12404 //================================================================================
12406 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12407 Bnd_Dimension dimension,
12408 SMESH_Group* group/*=0*/,
12409 SMESH_Mesh* targetMesh/*=0*/,
12410 bool toCopyElements/*=false*/,
12411 bool toCopyExistingBoundary/*=false*/,
12412 bool toAddExistingBondary/*= false*/,
12413 bool aroundElements/*= false*/)
12415 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12416 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12417 // hope that all elements are of the same type, do not check them all
12418 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12419 throw SALOME_Exception(LOCALIZED("wrong element type"));
12422 toCopyElements = toCopyExistingBoundary = false;
12424 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12425 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12426 int nbAddedBnd = 0;
12428 // editor adding present bnd elements and optionally holding elements to add to the group
12429 SMESH_MeshEditor* presentEditor;
12430 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12431 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12433 SMESH_MesherHelper helper( *myMesh );
12434 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12435 SMDS_VolumeTool vTool;
12436 TIDSortedElemSet avoidSet;
12437 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12440 typedef vector<const SMDS_MeshNode*> TConnectivity;
12441 TConnectivity tgtNodes;
12442 ElemFeatures elemKind( missType ), elemToCopy;
12444 vector<const SMDS_MeshElement*> presentBndElems;
12445 vector<TConnectivity> missingBndElems;
12446 vector<int> freeFacets;
12447 TConnectivity nodes, elemNodes;
12449 SMDS_ElemIteratorPtr eIt;
12450 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12451 else eIt = SMESHUtils::elemSetIterator( elements );
12453 while ( eIt->more() )
12455 const SMDS_MeshElement* elem = eIt->next();
12456 const int iQuad = elem->IsQuadratic();
12457 elemKind.SetQuad( iQuad );
12459 // ------------------------------------------------------------------------------------
12460 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12461 // ------------------------------------------------------------------------------------
12462 presentBndElems.clear();
12463 missingBndElems.clear();
12464 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12465 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12467 const SMDS_MeshElement* otherVol = 0;
12468 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12470 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12471 ( !aroundElements || elements.count( otherVol )))
12473 freeFacets.push_back( iface );
12475 if ( missType == SMDSAbs_Face )
12476 vTool.SetExternalNormal();
12477 for ( size_t i = 0; i < freeFacets.size(); ++i )
12479 int iface = freeFacets[i];
12480 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12481 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12482 if ( missType == SMDSAbs_Edge ) // boundary edges
12484 nodes.resize( 2+iQuad );
12485 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12487 for ( size_t j = 0; j < nodes.size(); ++j )
12488 nodes[ j ] = nn[ i+j ];
12489 if ( const SMDS_MeshElement* edge =
12490 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12491 presentBndElems.push_back( edge );
12493 missingBndElems.push_back( nodes );
12496 else // boundary face
12499 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12500 nodes.push_back( nn[inode] ); // add corner nodes
12502 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12503 nodes.push_back( nn[inode] ); // add medium nodes
12504 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12506 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12508 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12509 SMDSAbs_Face, /*noMedium=*/false ))
12510 presentBndElems.push_back( f );
12512 missingBndElems.push_back( nodes );
12514 if ( targetMesh != myMesh )
12516 // add 1D elements on face boundary to be added to a new mesh
12517 const SMDS_MeshElement* edge;
12518 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12521 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12523 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12524 if ( edge && avoidSet.insert( edge ).second )
12525 presentBndElems.push_back( edge );
12531 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12533 avoidSet.clear(), avoidSet.insert( elem );
12534 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12535 SMDS_MeshElement::iterator() );
12536 elemNodes.push_back( elemNodes[0] );
12537 nodes.resize( 2 + iQuad );
12538 const int nbLinks = elem->NbCornerNodes();
12539 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12541 nodes[0] = elemNodes[iN];
12542 nodes[1] = elemNodes[iN+1+iQuad];
12543 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12544 continue; // not free link
12546 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12547 if ( const SMDS_MeshElement* edge =
12548 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12549 presentBndElems.push_back( edge );
12551 missingBndElems.push_back( nodes );
12555 // ---------------------------------
12556 // 2. Add missing boundary elements
12557 // ---------------------------------
12558 if ( targetMesh != myMesh )
12559 // instead of making a map of nodes in this mesh and targetMesh,
12560 // we create nodes with same IDs.
12561 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12563 TConnectivity& srcNodes = missingBndElems[i];
12564 tgtNodes.resize( srcNodes.size() );
12565 for ( inode = 0; inode < srcNodes.size(); ++inode )
12566 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12567 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12569 /*noMedium=*/false))
12571 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12575 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12577 TConnectivity& nodes = missingBndElems[ i ];
12578 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12580 /*noMedium=*/false))
12582 SMDS_MeshElement* newElem =
12583 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12584 nbAddedBnd += bool( newElem );
12586 // try to set a new element to a shape
12587 if ( myMesh->HasShapeToMesh() )
12590 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12591 const size_t nbN = nodes.size() / (iQuad+1 );
12592 for ( inode = 0; inode < nbN && ok; ++inode )
12594 pair<int, TopAbs_ShapeEnum> i_stype =
12595 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12596 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12597 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12599 if ( ok && mediumShapes.size() > 1 )
12601 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12602 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12603 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12605 if (( ok = ( stype_i->first != stype_i_0.first )))
12606 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12607 aMesh->IndexToShape( stype_i_0.second ));
12610 if ( ok && mediumShapes.begin()->first == missShapeType )
12611 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12615 // ----------------------------------
12616 // 3. Copy present boundary elements
12617 // ----------------------------------
12618 if ( toCopyExistingBoundary )
12619 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12621 const SMDS_MeshElement* e = presentBndElems[i];
12622 tgtNodes.resize( e->NbNodes() );
12623 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12624 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12625 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12627 else // store present elements to add them to a group
12628 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12630 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12633 } // loop on given elements
12635 // ---------------------------------------------
12636 // 4. Fill group with boundary elements
12637 // ---------------------------------------------
12640 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12641 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12642 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12644 tgtEditor.myLastCreatedElems.clear();
12645 tgtEditor2.myLastCreatedElems.clear();
12647 // -----------------------
12648 // 5. Copy given elements
12649 // -----------------------
12650 if ( toCopyElements && targetMesh != myMesh )
12652 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12653 else eIt = SMESHUtils::elemSetIterator( elements );
12654 while (eIt->more())
12656 const SMDS_MeshElement* elem = eIt->next();
12657 tgtNodes.resize( elem->NbNodes() );
12658 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12659 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12660 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12662 tgtEditor.myLastCreatedElems.clear();
12668 //================================================================================
12670 * \brief Copy node position and set \a to node on the same geometry
12672 //================================================================================
12674 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12675 const SMDS_MeshNode* to )
12677 if ( !from || !to ) return;
12679 SMDS_PositionPtr pos = from->GetPosition();
12680 if ( !pos || from->getshapeId() < 1 ) return;
12682 switch ( pos->GetTypeOfPosition() )
12684 case SMDS_TOP_3DSPACE: break;
12686 case SMDS_TOP_FACE:
12688 SMDS_FacePositionPtr fPos = pos;
12689 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12690 fPos->GetUParameter(), fPos->GetVParameter() );
12693 case SMDS_TOP_EDGE:
12695 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12696 SMDS_EdgePositionPtr ePos = pos;
12697 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12700 case SMDS_TOP_VERTEX:
12702 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12705 case SMDS_TOP_UNSPEC: