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 if ( curElem->IsNull() )
7483 ComparableElement compElem = curElem;
7485 const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7486 if ( elemInSet.GetElem() != curElem ) // coincident elem
7488 int& iG = elemInSet.GroupID();
7491 iG = arrayOfGroups.size();
7492 arrayOfGroups.push_back( groupOfElems );
7493 arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7495 arrayOfGroups[ iG ].push_back( curElem->GetID() );
7499 groupOfElems.clear();
7500 std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7501 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7503 if ( groupIt->size() > 1 ) {
7504 //groupOfElems.sort(); -- theElements are sorted already
7505 theGroupsOfElementsID.emplace_back( *groupIt );
7510 //=======================================================================
7511 //function : MergeElements
7512 //purpose : In each given group, substitute all elements by the first one.
7513 //=======================================================================
7515 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7519 typedef list<int> TListOfIDs;
7520 TListOfIDs rmElemIds; // IDs of elems to remove
7522 SMESHDS_Mesh* aMesh = GetMeshDS();
7524 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7525 while ( groupsIt != theGroupsOfElementsID.end() ) {
7526 TListOfIDs& aGroupOfElemID = *groupsIt;
7527 aGroupOfElemID.sort();
7528 int elemIDToKeep = aGroupOfElemID.front();
7529 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7530 aGroupOfElemID.pop_front();
7531 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7532 while ( idIt != aGroupOfElemID.end() ) {
7533 int elemIDToRemove = *idIt;
7534 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7535 // add the kept element in groups of removed one (PAL15188)
7536 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7537 rmElemIds.push_back( elemIDToRemove );
7543 Remove( rmElemIds, false );
7546 //=======================================================================
7547 //function : MergeEqualElements
7548 //purpose : Remove all but one of elements built on the same nodes.
7549 //=======================================================================
7551 void SMESH_MeshEditor::MergeEqualElements()
7553 TIDSortedElemSet aMeshElements; /* empty input ==
7554 to merge equal elements in the whole mesh */
7555 TListOfListOfElementsID aGroupsOfElementsID;
7556 FindEqualElements( aMeshElements, aGroupsOfElementsID );
7557 MergeElements( aGroupsOfElementsID );
7560 //=======================================================================
7561 //function : findAdjacentFace
7563 //=======================================================================
7565 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7566 const SMDS_MeshNode* n2,
7567 const SMDS_MeshElement* elem)
7569 TIDSortedElemSet elemSet, avoidSet;
7571 avoidSet.insert ( elem );
7572 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7575 //=======================================================================
7576 //function : findSegment
7577 //purpose : Return a mesh segment by two nodes one of which can be medium
7578 //=======================================================================
7580 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7581 const SMDS_MeshNode* n2)
7583 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7584 while ( it->more() )
7586 const SMDS_MeshElement* seg = it->next();
7587 if ( seg->GetNodeIndex( n2 ) >= 0 )
7593 //=======================================================================
7594 //function : FindFreeBorder
7596 //=======================================================================
7598 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7600 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7601 const SMDS_MeshNode* theSecondNode,
7602 const SMDS_MeshNode* theLastNode,
7603 list< const SMDS_MeshNode* > & theNodes,
7604 list< const SMDS_MeshElement* >& theFaces)
7606 if ( !theFirstNode || !theSecondNode )
7608 // find border face between theFirstNode and theSecondNode
7609 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7613 theFaces.push_back( curElem );
7614 theNodes.push_back( theFirstNode );
7615 theNodes.push_back( theSecondNode );
7617 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7618 //TIDSortedElemSet foundElems;
7619 bool needTheLast = ( theLastNode != 0 );
7621 vector<const SMDS_MeshNode*> nodes;
7623 while ( nStart != theLastNode ) {
7624 if ( nStart == theFirstNode )
7625 return !needTheLast;
7627 // find all free border faces sharing nStart
7629 list< const SMDS_MeshElement* > curElemList;
7630 list< const SMDS_MeshNode* > nStartList;
7631 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7632 while ( invElemIt->more() ) {
7633 const SMDS_MeshElement* e = invElemIt->next();
7634 //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
7637 nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7638 SMDS_MeshElement::iterator() );
7639 nodes.push_back( nodes[ 0 ]);
7642 int iNode = 0, nbNodes = nodes.size() - 1;
7643 for ( iNode = 0; iNode < nbNodes; iNode++ )
7644 if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7645 ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7646 ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
7648 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
7649 curElemList.push_back( e );
7653 // analyse the found
7655 int nbNewBorders = curElemList.size();
7656 if ( nbNewBorders == 0 ) {
7657 // no free border furthermore
7658 return !needTheLast;
7660 else if ( nbNewBorders == 1 ) {
7661 // one more element found
7663 nStart = nStartList.front();
7664 curElem = curElemList.front();
7665 theFaces.push_back( curElem );
7666 theNodes.push_back( nStart );
7669 // several continuations found
7670 list< const SMDS_MeshElement* >::iterator curElemIt;
7671 list< const SMDS_MeshNode* >::iterator nStartIt;
7672 // check if one of them reached the last node
7673 if ( needTheLast ) {
7674 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7675 curElemIt!= curElemList.end();
7676 curElemIt++, nStartIt++ )
7677 if ( *nStartIt == theLastNode ) {
7678 theFaces.push_back( *curElemIt );
7679 theNodes.push_back( *nStartIt );
7683 // find the best free border by the continuations
7684 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7685 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7686 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7687 curElemIt!= curElemList.end();
7688 curElemIt++, nStartIt++ )
7690 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7691 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7692 // find one more free border
7693 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7697 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7698 // choice: clear a worse one
7699 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7700 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7701 contNodes[ iWorse ].clear();
7702 contFaces[ iWorse ].clear();
7705 if ( contNodes[0].empty() && contNodes[1].empty() )
7708 // push_back the best free border
7709 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7710 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7711 //theNodes.pop_back(); // remove nIgnore
7712 theNodes.pop_back(); // remove nStart
7713 //theFaces.pop_back(); // remove curElem
7714 theNodes.splice( theNodes.end(), *cNL );
7715 theFaces.splice( theFaces.end(), *cFL );
7718 } // several continuations found
7719 } // while ( nStart != theLastNode )
7724 //=======================================================================
7725 //function : CheckFreeBorderNodes
7726 //purpose : Return true if the tree nodes are on a free border
7727 //=======================================================================
7729 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7730 const SMDS_MeshNode* theNode2,
7731 const SMDS_MeshNode* theNode3)
7733 list< const SMDS_MeshNode* > nodes;
7734 list< const SMDS_MeshElement* > faces;
7735 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7738 //=======================================================================
7739 //function : SewFreeBorder
7741 //warning : for border-to-side sewing theSideSecondNode is considered as
7742 // the last side node and theSideThirdNode is not used
7743 //=======================================================================
7745 SMESH_MeshEditor::Sew_Error
7746 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7747 const SMDS_MeshNode* theBordSecondNode,
7748 const SMDS_MeshNode* theBordLastNode,
7749 const SMDS_MeshNode* theSideFirstNode,
7750 const SMDS_MeshNode* theSideSecondNode,
7751 const SMDS_MeshNode* theSideThirdNode,
7752 const bool theSideIsFreeBorder,
7753 const bool toCreatePolygons,
7754 const bool toCreatePolyedrs)
7758 Sew_Error aResult = SEW_OK;
7760 // ====================================
7761 // find side nodes and elements
7762 // ====================================
7764 list< const SMDS_MeshNode* > nSide[ 2 ];
7765 list< const SMDS_MeshElement* > eSide[ 2 ];
7766 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7767 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7771 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7772 nSide[0], eSide[0])) {
7773 MESSAGE(" Free Border 1 not found " );
7774 aResult = SEW_BORDER1_NOT_FOUND;
7776 if (theSideIsFreeBorder) {
7779 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7780 nSide[1], eSide[1])) {
7781 MESSAGE(" Free Border 2 not found " );
7782 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7785 if ( aResult != SEW_OK )
7788 if (!theSideIsFreeBorder) {
7792 // -------------------------------------------------------------------------
7794 // 1. If nodes to merge are not coincident, move nodes of the free border
7795 // from the coord sys defined by the direction from the first to last
7796 // nodes of the border to the correspondent sys of the side 2
7797 // 2. On the side 2, find the links most co-directed with the correspondent
7798 // links of the free border
7799 // -------------------------------------------------------------------------
7801 // 1. Since sewing may break if there are volumes to split on the side 2,
7802 // we won't move nodes but just compute new coordinates for them
7803 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7804 TNodeXYZMap nBordXYZ;
7805 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7806 list< const SMDS_MeshNode* >::iterator nBordIt;
7808 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7809 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7810 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7811 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7812 double tol2 = 1.e-8;
7813 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7814 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7815 // Need node movement.
7817 // find X and Z axes to create trsf
7818 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7820 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7822 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7825 gp_Ax3 toBordAx( Pb1, Zb, X );
7826 gp_Ax3 fromSideAx( Ps1, Zs, X );
7827 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7829 gp_Trsf toBordSys, fromSide2Sys;
7830 toBordSys.SetTransformation( toBordAx );
7831 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7832 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7835 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7836 const SMDS_MeshNode* n = *nBordIt;
7837 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7838 toBordSys.Transforms( xyz );
7839 fromSide2Sys.Transforms( xyz );
7840 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7844 // just insert nodes XYZ in the nBordXYZ map
7845 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7846 const SMDS_MeshNode* n = *nBordIt;
7847 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7851 // 2. On the side 2, find the links most co-directed with the correspondent
7852 // links of the free border
7854 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7855 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7856 sideNodes.push_back( theSideFirstNode );
7858 bool hasVolumes = false;
7859 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7860 set<long> foundSideLinkIDs, checkedLinkIDs;
7861 SMDS_VolumeTool volume;
7862 //const SMDS_MeshNode* faceNodes[ 4 ];
7864 const SMDS_MeshNode* sideNode;
7865 const SMDS_MeshElement* sideElem = 0;
7866 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7867 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7868 nBordIt = bordNodes.begin();
7870 // border node position and border link direction to compare with
7871 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7872 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7873 // choose next side node by link direction or by closeness to
7874 // the current border node:
7875 bool searchByDir = ( *nBordIt != theBordLastNode );
7877 // find the next node on the Side 2
7879 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7881 checkedLinkIDs.clear();
7882 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7884 // loop on inverse elements of current node (prevSideNode) on the Side 2
7885 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7886 while ( invElemIt->more() )
7888 const SMDS_MeshElement* elem = invElemIt->next();
7889 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7890 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7891 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7892 bool isVolume = volume.Set( elem );
7893 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7894 if ( isVolume ) // --volume
7896 else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7897 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7898 SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
7899 while ( nIt->more() ) {
7900 nodes[ iNode ] = cast2Node( nIt->next() );
7901 if ( nodes[ iNode++ ] == prevSideNode )
7902 iPrevNode = iNode - 1;
7904 // there are 2 links to check
7909 // loop on links, to be precise, on the second node of links
7910 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7911 const SMDS_MeshNode* n = nodes[ iNode ];
7913 if ( !volume.IsLinked( n, prevSideNode ))
7917 if ( iNode ) // a node before prevSideNode
7918 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7919 else // a node after prevSideNode
7920 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7922 // check if this link was already used
7923 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7924 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7925 if (!isJustChecked &&
7926 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7928 // test a link geometrically
7929 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7930 bool linkIsBetter = false;
7931 double dot = 0.0, dist = 0.0;
7932 if ( searchByDir ) { // choose most co-directed link
7933 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7934 linkIsBetter = ( dot > maxDot );
7936 else { // choose link with the node closest to bordPos
7937 dist = ( nextXYZ - bordPos ).SquareModulus();
7938 linkIsBetter = ( dist < minDist );
7940 if ( linkIsBetter ) {
7949 } // loop on inverse elements of prevSideNode
7952 MESSAGE(" Can't find path by links of the Side 2 ");
7953 return SEW_BAD_SIDE_NODES;
7955 sideNodes.push_back( sideNode );
7956 sideElems.push_back( sideElem );
7957 foundSideLinkIDs.insert ( linkID );
7958 prevSideNode = sideNode;
7960 if ( *nBordIt == theBordLastNode )
7961 searchByDir = false;
7963 // find the next border link to compare with
7964 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7965 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7966 // move to next border node if sideNode is before forward border node (bordPos)
7967 while ( *nBordIt != theBordLastNode && !searchByDir ) {
7968 prevBordNode = *nBordIt;
7970 bordPos = nBordXYZ[ *nBordIt ];
7971 bordDir = bordPos - nBordXYZ[ prevBordNode ];
7972 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7976 while ( sideNode != theSideSecondNode );
7978 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7979 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7980 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7982 } // end nodes search on the side 2
7984 // ============================
7985 // sew the border to the side 2
7986 // ============================
7988 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
7989 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
7991 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
7992 if ( toMergeConformal && toCreatePolygons )
7994 // do not merge quadrangles if polygons are OK (IPAL0052824)
7995 eIt[0] = eSide[0].begin();
7996 eIt[1] = eSide[1].begin();
7997 bool allQuads[2] = { true, true };
7998 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7999 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8000 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8002 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8005 TListOfListOfNodes nodeGroupsToMerge;
8006 if (( toMergeConformal ) ||
8007 ( theSideIsFreeBorder && !theSideThirdNode )) {
8009 // all nodes are to be merged
8011 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8012 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8013 nIt[0]++, nIt[1]++ )
8015 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8016 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8017 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8022 // insert new nodes into the border and the side to get equal nb of segments
8024 // get normalized parameters of nodes on the borders
8025 vector< double > param[ 2 ];
8026 param[0].resize( maxNbNodes );
8027 param[1].resize( maxNbNodes );
8029 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8030 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8031 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8032 const SMDS_MeshNode* nPrev = *nIt;
8033 double bordLength = 0;
8034 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8035 const SMDS_MeshNode* nCur = *nIt;
8036 gp_XYZ segment (nCur->X() - nPrev->X(),
8037 nCur->Y() - nPrev->Y(),
8038 nCur->Z() - nPrev->Z());
8039 double segmentLen = segment.Modulus();
8040 bordLength += segmentLen;
8041 param[ iBord ][ iNode ] = bordLength;
8044 // normalize within [0,1]
8045 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8046 param[ iBord ][ iNode ] /= bordLength;
8050 // loop on border segments
8051 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8052 int i[ 2 ] = { 0, 0 };
8053 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8054 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8056 // element can be split while iterating on border if it has two edges in the border
8057 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8058 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8060 TElemOfNodeListMap insertMap;
8061 TElemOfNodeListMap::iterator insertMapIt;
8063 // key: elem to insert nodes into
8064 // value: 2 nodes to insert between + nodes to be inserted
8066 bool next[ 2 ] = { false, false };
8068 // find min adjacent segment length after sewing
8069 double nextParam = 10., prevParam = 0;
8070 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8071 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8072 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8073 if ( i[ iBord ] > 0 )
8074 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8076 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8077 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8078 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8080 // choose to insert or to merge nodes
8081 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8082 if ( Abs( du ) <= minSegLen * 0.2 ) {
8085 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8086 const SMDS_MeshNode* n0 = *nIt[0];
8087 const SMDS_MeshNode* n1 = *nIt[1];
8088 nodeGroupsToMerge.back().push_back( n1 );
8089 nodeGroupsToMerge.back().push_back( n0 );
8090 // position of node of the border changes due to merge
8091 param[ 0 ][ i[0] ] += du;
8092 // move n1 for the sake of elem shape evaluation during insertion.
8093 // n1 will be removed by MergeNodes() anyway
8094 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8095 next[0] = next[1] = true;
8100 int intoBord = ( du < 0 ) ? 0 : 1;
8101 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8102 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8103 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8104 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8105 if ( intoBord == 1 ) {
8106 // move node of the border to be on a link of elem of the side
8107 SMESH_NodeXYZ p1( n1 ), p2( n2 );
8108 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8109 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8110 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8112 elemReplaceMapIt = elemReplaceMap.find( elem );
8113 if ( elemReplaceMapIt != elemReplaceMap.end() )
8114 elem = elemReplaceMapIt->second;
8116 insertMapIt = insertMap.find( elem );
8117 bool notFound = ( insertMapIt == insertMap.end() );
8118 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8120 // insert into another link of the same element:
8121 // 1. perform insertion into the other link of the elem
8122 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8123 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8124 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8125 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8126 // 2. perform insertion into the link of adjacent faces
8127 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8128 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8130 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8131 InsertNodesIntoLink( seg, n12, n22, nodeList );
8133 if (toCreatePolyedrs) {
8134 // perform insertion into the links of adjacent volumes
8135 UpdateVolumes(n12, n22, nodeList);
8137 // 3. find an element appeared on n1 and n2 after the insertion
8138 insertMap.erase( insertMapIt );
8139 const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8140 elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8143 if ( notFound || otherLink ) {
8144 // add element and nodes of the side into the insertMap
8145 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8146 (*insertMapIt).second.push_back( n1 );
8147 (*insertMapIt).second.push_back( n2 );
8149 // add node to be inserted into elem
8150 (*insertMapIt).second.push_back( nIns );
8151 next[ 1 - intoBord ] = true;
8154 // go to the next segment
8155 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8156 if ( next[ iBord ] ) {
8157 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8159 nPrev[ iBord ] = *nIt[ iBord ];
8160 nIt[ iBord ]++; i[ iBord ]++;
8164 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8166 // perform insertion of nodes into elements
8168 for (insertMapIt = insertMap.begin();
8169 insertMapIt != insertMap.end();
8172 const SMDS_MeshElement* elem = (*insertMapIt).first;
8173 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8174 if ( nodeList.size() < 3 ) continue;
8175 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8176 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8178 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8180 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8181 InsertNodesIntoLink( seg, n1, n2, nodeList );
8184 if ( !theSideIsFreeBorder ) {
8185 // look for and insert nodes into the faces adjacent to elem
8186 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8187 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8190 if (toCreatePolyedrs) {
8191 // perform insertion into the links of adjacent volumes
8192 UpdateVolumes(n1, n2, nodeList);
8195 } // end: insert new nodes
8197 MergeNodes ( nodeGroupsToMerge );
8200 // Remove coincident segments
8203 TIDSortedElemSet segments;
8204 SMESH_SequenceOfElemPtr newFaces;
8205 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8207 if ( !myLastCreatedElems[i] ) continue;
8208 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8209 segments.insert( segments.end(), myLastCreatedElems[i] );
8211 newFaces.push_back( myLastCreatedElems[i] );
8213 // get segments adjacent to merged nodes
8214 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8215 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8217 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8218 if ( nodes.front()->IsNull() ) continue;
8219 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8220 while ( segIt->more() )
8221 segments.insert( segIt->next() );
8225 TListOfListOfElementsID equalGroups;
8226 if ( !segments.empty() )
8227 FindEqualElements( segments, equalGroups );
8228 if ( !equalGroups.empty() )
8230 // remove from segments those that will be removed
8231 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8232 for ( ; itGroups != equalGroups.end(); ++itGroups )
8234 list< int >& group = *itGroups;
8235 list< int >::iterator id = group.begin();
8236 for ( ++id; id != group.end(); ++id )
8237 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8238 segments.erase( seg );
8240 // remove equal segments
8241 MergeElements( equalGroups );
8243 // restore myLastCreatedElems
8244 myLastCreatedElems = newFaces;
8245 TIDSortedElemSet::iterator seg = segments.begin();
8246 for ( ; seg != segments.end(); ++seg )
8247 myLastCreatedElems.push_back( *seg );
8253 //=======================================================================
8254 //function : InsertNodesIntoLink
8255 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8256 // and theBetweenNode2 and split theElement
8257 //=======================================================================
8259 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8260 const SMDS_MeshNode* theBetweenNode1,
8261 const SMDS_MeshNode* theBetweenNode2,
8262 list<const SMDS_MeshNode*>& theNodesToInsert,
8263 const bool toCreatePoly)
8265 if ( !theElement ) return;
8267 SMESHDS_Mesh *aMesh = GetMeshDS();
8268 vector<const SMDS_MeshElement*> newElems;
8270 if ( theElement->GetType() == SMDSAbs_Edge )
8272 theNodesToInsert.push_front( theBetweenNode1 );
8273 theNodesToInsert.push_back ( theBetweenNode2 );
8274 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8275 const SMDS_MeshNode* n1 = *n;
8276 for ( ++n; n != theNodesToInsert.end(); ++n )
8278 const SMDS_MeshNode* n2 = *n;
8279 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8280 AddToSameGroups( seg, theElement, aMesh );
8282 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8285 theNodesToInsert.pop_front();
8286 theNodesToInsert.pop_back();
8288 if ( theElement->IsQuadratic() ) // add a not split part
8290 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8291 theElement->end_nodes() );
8292 int iOther = 0, nbN = nodes.size();
8293 for ( ; iOther < nbN; ++iOther )
8294 if ( nodes[iOther] != theBetweenNode1 &&
8295 nodes[iOther] != theBetweenNode2 )
8299 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8300 AddToSameGroups( seg, theElement, aMesh );
8302 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8304 else if ( iOther == 2 )
8306 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8307 AddToSameGroups( seg, theElement, aMesh );
8309 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8312 // treat new elements
8313 for ( size_t i = 0; i < newElems.size(); ++i )
8316 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8317 myLastCreatedElems.push_back( newElems[i] );
8319 ReplaceElemInGroups( theElement, newElems, aMesh );
8320 aMesh->RemoveElement( theElement );
8323 } // if ( theElement->GetType() == SMDSAbs_Edge )
8325 const SMDS_MeshElement* theFace = theElement;
8326 if ( theFace->GetType() != SMDSAbs_Face ) return;
8328 // find indices of 2 link nodes and of the rest nodes
8329 int iNode = 0, il1, il2, i3, i4;
8330 il1 = il2 = i3 = i4 = -1;
8331 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8333 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8334 while ( nodeIt->more() ) {
8335 const SMDS_MeshNode* n = nodeIt->next();
8336 if ( n == theBetweenNode1 )
8338 else if ( n == theBetweenNode2 )
8344 nodes[ iNode++ ] = n;
8346 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8349 // arrange link nodes to go one after another regarding the face orientation
8350 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8351 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8356 aNodesToInsert.reverse();
8358 // check that not link nodes of a quadrangles are in good order
8359 int nbFaceNodes = theFace->NbNodes();
8360 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8366 if (toCreatePoly || theFace->IsPoly()) {
8369 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8371 // add nodes of face up to first node of link
8373 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8374 while ( nodeIt->more() && !isFLN ) {
8375 const SMDS_MeshNode* n = nodeIt->next();
8376 poly_nodes[iNode++] = n;
8377 isFLN = ( n == nodes[il1] );
8379 // add nodes to insert
8380 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8381 for (; nIt != aNodesToInsert.end(); nIt++) {
8382 poly_nodes[iNode++] = *nIt;
8384 // add nodes of face starting from last node of link
8385 while ( nodeIt->more() ) {
8386 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8387 poly_nodes[iNode++] = n;
8391 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8394 else if ( !theFace->IsQuadratic() )
8396 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8397 int nbLinkNodes = 2 + aNodesToInsert.size();
8398 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8399 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8400 linkNodes[ 0 ] = nodes[ il1 ];
8401 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8402 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8403 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8404 linkNodes[ iNode++ ] = *nIt;
8406 // decide how to split a quadrangle: compare possible variants
8407 // and choose which of splits to be a quadrangle
8408 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8409 if ( nbFaceNodes == 3 ) {
8410 iBestQuad = nbSplits;
8413 else if ( nbFaceNodes == 4 ) {
8414 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8415 double aBestRate = DBL_MAX;
8416 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8418 double aBadRate = 0;
8419 // evaluate elements quality
8420 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8421 if ( iSplit == iQuad ) {
8422 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8426 aBadRate += getBadRate( &quad, aCrit );
8429 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8431 nodes[ iSplit < iQuad ? i4 : i3 ]);
8432 aBadRate += getBadRate( &tria, aCrit );
8436 if ( aBadRate < aBestRate ) {
8438 aBestRate = aBadRate;
8443 // create new elements
8445 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8447 if ( iSplit == iBestQuad )
8448 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8453 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8455 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8458 const SMDS_MeshNode* newNodes[ 4 ];
8459 newNodes[ 0 ] = linkNodes[ i1 ];
8460 newNodes[ 1 ] = linkNodes[ i2 ];
8461 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8462 newNodes[ 3 ] = nodes[ i4 ];
8463 if (iSplit == iBestQuad)
8464 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8466 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8468 } // end if(!theFace->IsQuadratic())
8470 else { // theFace is quadratic
8471 // we have to split theFace on simple triangles and one simple quadrangle
8473 int nbshift = tmp*2;
8474 // shift nodes in nodes[] by nbshift
8476 for(i=0; i<nbshift; i++) {
8477 const SMDS_MeshNode* n = nodes[0];
8478 for(j=0; j<nbFaceNodes-1; j++) {
8479 nodes[j] = nodes[j+1];
8481 nodes[nbFaceNodes-1] = n;
8483 il1 = il1 - nbshift;
8484 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8485 // n0 n1 n2 n0 n1 n2
8486 // +-----+-----+ +-----+-----+
8495 // create new elements
8497 if ( nbFaceNodes == 6 ) { // quadratic triangle
8498 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8499 if ( theFace->IsMediumNode(nodes[il1]) ) {
8500 // create quadrangle
8501 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8507 // create quadrangle
8508 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8514 else { // nbFaceNodes==8 - quadratic quadrangle
8515 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8516 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8517 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8518 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8519 // create quadrangle
8520 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8526 // create quadrangle
8527 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8533 // create needed triangles using n1,n2,n3 and inserted nodes
8534 int nbn = 2 + aNodesToInsert.size();
8535 vector<const SMDS_MeshNode*> aNodes(nbn);
8536 aNodes[0 ] = nodes[n1];
8537 aNodes[nbn-1] = nodes[n2];
8538 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8539 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8540 aNodes[iNode++] = *nIt;
8542 for ( i = 1; i < nbn; i++ )
8543 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8546 // remove the old face
8547 for ( size_t i = 0; i < newElems.size(); ++i )
8550 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8551 myLastCreatedElems.push_back( newElems[i] );
8553 ReplaceElemInGroups( theFace, newElems, aMesh );
8554 aMesh->RemoveElement(theFace);
8556 } // InsertNodesIntoLink()
8558 //=======================================================================
8559 //function : UpdateVolumes
8561 //=======================================================================
8563 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8564 const SMDS_MeshNode* theBetweenNode2,
8565 list<const SMDS_MeshNode*>& theNodesToInsert)
8569 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8570 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8571 const SMDS_MeshElement* elem = invElemIt->next();
8573 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8574 SMDS_VolumeTool aVolume (elem);
8575 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8578 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8579 int iface, nbFaces = aVolume.NbFaces();
8580 vector<const SMDS_MeshNode *> poly_nodes;
8581 vector<int> quantities (nbFaces);
8583 for (iface = 0; iface < nbFaces; iface++) {
8584 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8585 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8586 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8588 for (int inode = 0; inode < nbFaceNodes; inode++) {
8589 poly_nodes.push_back(faceNodes[inode]);
8591 if (nbInserted == 0) {
8592 if (faceNodes[inode] == theBetweenNode1) {
8593 if (faceNodes[inode + 1] == theBetweenNode2) {
8594 nbInserted = theNodesToInsert.size();
8596 // add nodes to insert
8597 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8598 for (; nIt != theNodesToInsert.end(); nIt++) {
8599 poly_nodes.push_back(*nIt);
8603 else if (faceNodes[inode] == theBetweenNode2) {
8604 if (faceNodes[inode + 1] == theBetweenNode1) {
8605 nbInserted = theNodesToInsert.size();
8607 // add nodes to insert in reversed order
8608 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8610 for (; nIt != theNodesToInsert.begin(); nIt--) {
8611 poly_nodes.push_back(*nIt);
8613 poly_nodes.push_back(*nIt);
8620 quantities[iface] = nbFaceNodes + nbInserted;
8623 // Replace the volume
8624 SMESHDS_Mesh *aMesh = GetMeshDS();
8626 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8628 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8629 myLastCreatedElems.push_back( newElem );
8630 ReplaceElemInGroups( elem, newElem, aMesh );
8632 aMesh->RemoveElement( elem );
8638 //================================================================================
8640 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8642 //================================================================================
8644 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8645 vector<const SMDS_MeshNode *> & nodes,
8646 vector<int> & nbNodeInFaces )
8649 nbNodeInFaces.clear();
8650 SMDS_VolumeTool vTool ( elem );
8651 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8653 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8654 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8655 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8660 //=======================================================================
8662 * \brief Convert elements contained in a sub-mesh to quadratic
8663 * \return int - nb of checked elements
8665 //=======================================================================
8667 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8668 SMESH_MesherHelper& theHelper,
8669 const bool theForce3d)
8671 //MESSAGE("convertElemToQuadratic");
8673 if( !theSm ) return nbElem;
8675 vector<int> nbNodeInFaces;
8676 vector<const SMDS_MeshNode *> nodes;
8677 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8678 while(ElemItr->more())
8681 const SMDS_MeshElement* elem = ElemItr->next();
8682 if( !elem ) continue;
8684 // analyse a necessity of conversion
8685 const SMDSAbs_ElementType aType = elem->GetType();
8686 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8688 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8689 bool hasCentralNodes = false;
8690 if ( elem->IsQuadratic() )
8693 switch ( aGeomType ) {
8694 case SMDSEntity_Quad_Triangle:
8695 case SMDSEntity_Quad_Quadrangle:
8696 case SMDSEntity_Quad_Hexa:
8697 case SMDSEntity_Quad_Penta:
8698 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8700 case SMDSEntity_BiQuad_Triangle:
8701 case SMDSEntity_BiQuad_Quadrangle:
8702 case SMDSEntity_TriQuad_Hexa:
8703 case SMDSEntity_BiQuad_Penta:
8704 alreadyOK = theHelper.GetIsBiQuadratic();
8705 hasCentralNodes = true;
8710 // take into account already present medium nodes
8712 case SMDSAbs_Volume:
8713 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8715 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8717 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8723 // get elem data needed to re-create it
8725 const int id = elem->GetID();
8726 const int nbNodes = elem->NbCornerNodes();
8727 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8728 if ( aGeomType == SMDSEntity_Polyhedra )
8729 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8730 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8731 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8733 // remove a linear element
8734 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8736 // remove central nodes of biquadratic elements (biquad->quad conversion)
8737 if ( hasCentralNodes )
8738 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8739 if ( nodes[i]->NbInverseElements() == 0 )
8740 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8742 const SMDS_MeshElement* NewElem = 0;
8748 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8756 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8759 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8762 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8766 case SMDSAbs_Volume :
8770 case SMDSEntity_Tetra:
8771 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8773 case SMDSEntity_Pyramid:
8774 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8776 case SMDSEntity_Penta:
8777 case SMDSEntity_Quad_Penta:
8778 case SMDSEntity_BiQuad_Penta:
8779 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8781 case SMDSEntity_Hexa:
8782 case SMDSEntity_Quad_Hexa:
8783 case SMDSEntity_TriQuad_Hexa:
8784 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8785 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8787 case SMDSEntity_Hexagonal_Prism:
8789 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8796 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8797 if( NewElem && NewElem->getshapeId() < 1 )
8798 theSm->AddElement( NewElem );
8802 //=======================================================================
8803 //function : ConvertToQuadratic
8805 //=======================================================================
8807 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8809 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8810 SMESHDS_Mesh* meshDS = GetMeshDS();
8812 SMESH_MesherHelper aHelper(*myMesh);
8814 aHelper.SetIsQuadratic( true );
8815 aHelper.SetIsBiQuadratic( theToBiQuad );
8816 aHelper.SetElementsOnShape(true);
8817 aHelper.ToFixNodeParameters( true );
8819 // convert elements assigned to sub-meshes
8820 int nbCheckedElems = 0;
8821 if ( myMesh->HasShapeToMesh() )
8823 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8825 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8826 while ( smIt->more() ) {
8827 SMESH_subMesh* sm = smIt->next();
8828 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8829 aHelper.SetSubShape( sm->GetSubShape() );
8830 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8836 // convert elements NOT assigned to sub-meshes
8837 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8838 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8840 aHelper.SetElementsOnShape(false);
8841 SMESHDS_SubMesh *smDS = 0;
8844 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8845 while( aEdgeItr->more() )
8847 const SMDS_MeshEdge* edge = aEdgeItr->next();
8848 if ( !edge->IsQuadratic() )
8850 int id = edge->GetID();
8851 const SMDS_MeshNode* n1 = edge->GetNode(0);
8852 const SMDS_MeshNode* n2 = edge->GetNode(1);
8854 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8856 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8857 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8861 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8866 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8867 while( aFaceItr->more() )
8869 const SMDS_MeshFace* face = aFaceItr->next();
8870 if ( !face ) continue;
8872 const SMDSAbs_EntityType type = face->GetEntityType();
8876 case SMDSEntity_Quad_Triangle:
8877 case SMDSEntity_Quad_Quadrangle:
8878 alreadyOK = !theToBiQuad;
8879 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8881 case SMDSEntity_BiQuad_Triangle:
8882 case SMDSEntity_BiQuad_Quadrangle:
8883 alreadyOK = theToBiQuad;
8884 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8886 default: alreadyOK = false;
8891 const int id = face->GetID();
8892 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8894 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8896 SMDS_MeshFace * NewFace = 0;
8899 case SMDSEntity_Triangle:
8900 case SMDSEntity_Quad_Triangle:
8901 case SMDSEntity_BiQuad_Triangle:
8902 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8903 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8904 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8907 case SMDSEntity_Quadrangle:
8908 case SMDSEntity_Quad_Quadrangle:
8909 case SMDSEntity_BiQuad_Quadrangle:
8910 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8911 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8912 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8916 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8918 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8922 vector<int> nbNodeInFaces;
8923 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8924 while(aVolumeItr->more())
8926 const SMDS_MeshVolume* volume = aVolumeItr->next();
8927 if ( !volume ) continue;
8929 const SMDSAbs_EntityType type = volume->GetEntityType();
8930 if ( volume->IsQuadratic() )
8935 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8936 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8937 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
8938 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
8939 default: alreadyOK = true;
8943 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8947 const int id = volume->GetID();
8948 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8949 if ( type == SMDSEntity_Polyhedra )
8950 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
8951 else if ( type == SMDSEntity_Hexagonal_Prism )
8952 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8954 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8956 SMDS_MeshVolume * NewVolume = 0;
8959 case SMDSEntity_Tetra:
8960 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8962 case SMDSEntity_Hexa:
8963 case SMDSEntity_Quad_Hexa:
8964 case SMDSEntity_TriQuad_Hexa:
8965 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8966 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8967 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8968 if ( nodes[i]->NbInverseElements() == 0 )
8969 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8971 case SMDSEntity_Pyramid:
8972 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8973 nodes[3], nodes[4], id, theForce3d);
8975 case SMDSEntity_Penta:
8976 case SMDSEntity_Quad_Penta:
8977 case SMDSEntity_BiQuad_Penta:
8978 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8979 nodes[3], nodes[4], nodes[5], id, theForce3d);
8980 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
8981 if ( nodes[i]->NbInverseElements() == 0 )
8982 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8984 case SMDSEntity_Hexagonal_Prism:
8986 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8988 ReplaceElemInGroups(volume, NewVolume, meshDS);
8993 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8994 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8995 // aHelper.FixQuadraticElements(myError);
8996 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9000 //================================================================================
9002 * \brief Makes given elements quadratic
9003 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9004 * \param theElements - elements to make quadratic
9006 //================================================================================
9008 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9009 TIDSortedElemSet& theElements,
9010 const bool theToBiQuad)
9012 if ( theElements.empty() ) return;
9014 // we believe that all theElements are of the same type
9015 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9017 // get all nodes shared by theElements
9018 TIDSortedNodeSet allNodes;
9019 TIDSortedElemSet::iterator eIt = theElements.begin();
9020 for ( ; eIt != theElements.end(); ++eIt )
9021 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9023 // complete theElements with elements of lower dim whose all nodes are in allNodes
9025 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9026 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9027 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9028 for ( ; nIt != allNodes.end(); ++nIt )
9030 const SMDS_MeshNode* n = *nIt;
9031 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9032 while ( invIt->more() )
9034 const SMDS_MeshElement* e = invIt->next();
9035 const SMDSAbs_ElementType type = e->GetType();
9036 if ( e->IsQuadratic() )
9038 quadAdjacentElems[ type ].insert( e );
9041 switch ( e->GetEntityType() ) {
9042 case SMDSEntity_Quad_Triangle:
9043 case SMDSEntity_Quad_Quadrangle:
9044 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9045 case SMDSEntity_BiQuad_Triangle:
9046 case SMDSEntity_BiQuad_Quadrangle:
9047 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9048 default: alreadyOK = true;
9053 if ( type >= elemType )
9054 continue; // same type or more complex linear element
9056 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9057 continue; // e is already checked
9061 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9062 while ( nodeIt->more() && allIn )
9063 allIn = allNodes.count( nodeIt->next() );
9065 theElements.insert(e );
9069 SMESH_MesherHelper helper(*myMesh);
9070 helper.SetIsQuadratic( true );
9071 helper.SetIsBiQuadratic( theToBiQuad );
9073 // add links of quadratic adjacent elements to the helper
9075 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9076 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9077 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9079 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9081 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9082 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9083 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9085 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9087 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9088 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9089 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9091 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9094 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9096 SMESHDS_Mesh* meshDS = GetMeshDS();
9097 SMESHDS_SubMesh* smDS = 0;
9098 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9100 const SMDS_MeshElement* elem = *eIt;
9103 int nbCentralNodes = 0;
9104 switch ( elem->GetEntityType() ) {
9105 // linear convertible
9106 case SMDSEntity_Edge:
9107 case SMDSEntity_Triangle:
9108 case SMDSEntity_Quadrangle:
9109 case SMDSEntity_Tetra:
9110 case SMDSEntity_Pyramid:
9111 case SMDSEntity_Hexa:
9112 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9113 // quadratic that can become bi-quadratic
9114 case SMDSEntity_Quad_Triangle:
9115 case SMDSEntity_Quad_Quadrangle:
9116 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9118 case SMDSEntity_BiQuad_Triangle:
9119 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9120 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9122 default: alreadyOK = true;
9124 if ( alreadyOK ) continue;
9126 const SMDSAbs_ElementType type = elem->GetType();
9127 const int id = elem->GetID();
9128 const int nbNodes = elem->NbCornerNodes();
9129 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9131 helper.SetSubShape( elem->getshapeId() );
9133 if ( !smDS || !smDS->Contains( elem ))
9134 smDS = meshDS->MeshElements( elem->getshapeId() );
9135 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9137 SMDS_MeshElement * newElem = 0;
9140 case 4: // cases for most frequently used element types go first (for optimization)
9141 if ( type == SMDSAbs_Volume )
9142 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9144 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9147 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9148 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9151 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9154 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9157 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9158 nodes[4], id, theForce3d);
9161 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9162 nodes[4], nodes[5], id, theForce3d);
9166 ReplaceElemInGroups( elem, newElem, meshDS);
9167 if( newElem && smDS )
9168 smDS->AddElement( newElem );
9170 // remove central nodes
9171 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9172 if ( nodes[i]->NbInverseElements() == 0 )
9173 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9175 } // loop on theElements
9178 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9179 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9180 // helper.FixQuadraticElements( myError );
9181 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9185 //=======================================================================
9187 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9188 * \return int - nb of checked elements
9190 //=======================================================================
9192 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9193 SMDS_ElemIteratorPtr theItr,
9194 const int theShapeID)
9197 SMESHDS_Mesh* meshDS = GetMeshDS();
9198 ElemFeatures elemType;
9199 vector<const SMDS_MeshNode *> nodes;
9201 while( theItr->more() )
9203 const SMDS_MeshElement* elem = theItr->next();
9205 if( elem && elem->IsQuadratic())
9208 int nbCornerNodes = elem->NbCornerNodes();
9209 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9211 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9213 //remove a quadratic element
9214 if ( !theSm || !theSm->Contains( elem ))
9215 theSm = meshDS->MeshElements( elem->getshapeId() );
9216 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9218 // remove medium nodes
9219 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9220 if ( nodes[i]->NbInverseElements() == 0 )
9221 meshDS->RemoveFreeNode( nodes[i], theSm );
9223 // add a linear element
9224 nodes.resize( nbCornerNodes );
9225 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9226 ReplaceElemInGroups(elem, newElem, meshDS);
9227 if( theSm && newElem )
9228 theSm->AddElement( newElem );
9234 //=======================================================================
9235 //function : ConvertFromQuadratic
9237 //=======================================================================
9239 bool SMESH_MeshEditor::ConvertFromQuadratic()
9241 int nbCheckedElems = 0;
9242 if ( myMesh->HasShapeToMesh() )
9244 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9246 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9247 while ( smIt->more() ) {
9248 SMESH_subMesh* sm = smIt->next();
9249 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9250 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9256 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9257 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9259 SMESHDS_SubMesh *aSM = 0;
9260 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9268 //================================================================================
9270 * \brief Return true if all medium nodes of the element are in the node set
9272 //================================================================================
9274 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9276 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9277 if ( !nodeSet.count( elem->GetNode(i) ))
9283 //================================================================================
9285 * \brief Makes given elements linear
9287 //================================================================================
9289 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9291 if ( theElements.empty() ) return;
9293 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9294 set<int> mediumNodeIDs;
9295 TIDSortedElemSet::iterator eIt = theElements.begin();
9296 for ( ; eIt != theElements.end(); ++eIt )
9298 const SMDS_MeshElement* e = *eIt;
9299 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9300 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9303 // replace given elements by linear ones
9304 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9305 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9307 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9308 // except those elements sharing medium nodes of quadratic element whose medium nodes
9309 // are not all in mediumNodeIDs
9311 // get remaining medium nodes
9312 TIDSortedNodeSet mediumNodes;
9313 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9314 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9315 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9316 mediumNodes.insert( mediumNodes.end(), n );
9318 // find more quadratic elements to convert
9319 TIDSortedElemSet moreElemsToConvert;
9320 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9321 for ( ; nIt != mediumNodes.end(); ++nIt )
9323 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9324 while ( invIt->more() )
9326 const SMDS_MeshElement* e = invIt->next();
9327 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9329 // find a more complex element including e and
9330 // whose medium nodes are not in mediumNodes
9331 bool complexFound = false;
9332 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9334 SMDS_ElemIteratorPtr invIt2 =
9335 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9336 while ( invIt2->more() )
9338 const SMDS_MeshElement* eComplex = invIt2->next();
9339 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9341 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9342 if ( nbCommonNodes == e->NbNodes())
9344 complexFound = true;
9345 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9351 if ( !complexFound )
9352 moreElemsToConvert.insert( e );
9356 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9357 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9360 //=======================================================================
9361 //function : SewSideElements
9363 //=======================================================================
9365 SMESH_MeshEditor::Sew_Error
9366 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9367 TIDSortedElemSet& theSide2,
9368 const SMDS_MeshNode* theFirstNode1,
9369 const SMDS_MeshNode* theFirstNode2,
9370 const SMDS_MeshNode* theSecondNode1,
9371 const SMDS_MeshNode* theSecondNode2)
9375 if ( theSide1.size() != theSide2.size() )
9376 return SEW_DIFF_NB_OF_ELEMENTS;
9378 Sew_Error aResult = SEW_OK;
9380 // 1. Build set of faces representing each side
9381 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9382 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9384 // =======================================================================
9385 // 1. Build set of faces representing each side:
9386 // =======================================================================
9387 // a. build set of nodes belonging to faces
9388 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9389 // c. create temporary faces representing side of volumes if correspondent
9390 // face does not exist
9392 SMESHDS_Mesh* aMesh = GetMeshDS();
9393 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9394 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9395 TIDSortedElemSet faceSet1, faceSet2;
9396 set<const SMDS_MeshElement*> volSet1, volSet2;
9397 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9398 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9399 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9400 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9401 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9402 int iSide, iFace, iNode;
9404 list<const SMDS_MeshElement* > tempFaceList;
9405 for ( iSide = 0; iSide < 2; iSide++ ) {
9406 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9407 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9408 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9409 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9410 set<const SMDS_MeshElement*>::iterator vIt;
9411 TIDSortedElemSet::iterator eIt;
9412 set<const SMDS_MeshNode*>::iterator nIt;
9414 // check that given nodes belong to given elements
9415 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9416 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9417 int firstIndex = -1, secondIndex = -1;
9418 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9419 const SMDS_MeshElement* elem = *eIt;
9420 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9421 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9422 if ( firstIndex > -1 && secondIndex > -1 ) break;
9424 if ( firstIndex < 0 || secondIndex < 0 ) {
9425 // we can simply return until temporary faces created
9426 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9429 // -----------------------------------------------------------
9430 // 1a. Collect nodes of existing faces
9431 // and build set of face nodes in order to detect missing
9432 // faces corresponding to sides of volumes
9433 // -----------------------------------------------------------
9435 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9437 // loop on the given element of a side
9438 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9439 //const SMDS_MeshElement* elem = *eIt;
9440 const SMDS_MeshElement* elem = *eIt;
9441 if ( elem->GetType() == SMDSAbs_Face ) {
9442 faceSet->insert( elem );
9443 set <const SMDS_MeshNode*> faceNodeSet;
9444 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9445 while ( nodeIt->more() ) {
9446 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9447 nodeSet->insert( n );
9448 faceNodeSet.insert( n );
9450 setOfFaceNodeSet.insert( faceNodeSet );
9452 else if ( elem->GetType() == SMDSAbs_Volume )
9453 volSet->insert( elem );
9455 // ------------------------------------------------------------------------------
9456 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9457 // ------------------------------------------------------------------------------
9459 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9460 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9461 while ( fIt->more() ) { // loop on faces sharing a node
9462 const SMDS_MeshElement* f = fIt->next();
9463 if ( faceSet->find( f ) == faceSet->end() ) {
9464 // check if all nodes are in nodeSet and
9465 // complete setOfFaceNodeSet if they are
9466 set <const SMDS_MeshNode*> faceNodeSet;
9467 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9468 bool allInSet = true;
9469 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9470 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9471 if ( nodeSet->find( n ) == nodeSet->end() )
9474 faceNodeSet.insert( n );
9477 faceSet->insert( f );
9478 setOfFaceNodeSet.insert( faceNodeSet );
9484 // -------------------------------------------------------------------------
9485 // 1c. Create temporary faces representing sides of volumes if correspondent
9486 // face does not exist
9487 // -------------------------------------------------------------------------
9489 if ( !volSet->empty() ) {
9490 //int nodeSetSize = nodeSet->size();
9492 // loop on given volumes
9493 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9494 SMDS_VolumeTool vol (*vIt);
9495 // loop on volume faces: find free faces
9496 // --------------------------------------
9497 list<const SMDS_MeshElement* > freeFaceList;
9498 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9499 if ( !vol.IsFreeFace( iFace ))
9501 // check if there is already a face with same nodes in a face set
9502 const SMDS_MeshElement* aFreeFace = 0;
9503 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9504 int nbNodes = vol.NbFaceNodes( iFace );
9505 set <const SMDS_MeshNode*> faceNodeSet;
9506 vol.GetFaceNodes( iFace, faceNodeSet );
9507 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9509 // no such a face is given but it still can exist, check it
9510 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9511 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9514 // create a temporary face
9515 if ( nbNodes == 3 ) {
9516 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9517 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9519 else if ( nbNodes == 4 ) {
9520 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9521 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9524 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9525 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9526 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9529 tempFaceList.push_back( aFreeFace );
9533 freeFaceList.push_back( aFreeFace );
9535 } // loop on faces of a volume
9537 // choose one of several free faces of a volume
9538 // --------------------------------------------
9539 if ( freeFaceList.size() > 1 ) {
9540 // choose a face having max nb of nodes shared by other elems of a side
9541 int maxNbNodes = -1;
9542 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9543 while ( fIt != freeFaceList.end() ) { // loop on free faces
9544 int nbSharedNodes = 0;
9545 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9546 while ( nodeIt->more() ) { // loop on free face nodes
9547 const SMDS_MeshNode* n =
9548 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9549 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9550 while ( invElemIt->more() ) {
9551 const SMDS_MeshElement* e = invElemIt->next();
9552 nbSharedNodes += faceSet->count( e );
9553 nbSharedNodes += elemSet->count( e );
9556 if ( nbSharedNodes > maxNbNodes ) {
9557 maxNbNodes = nbSharedNodes;
9558 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9560 else if ( nbSharedNodes == maxNbNodes ) {
9564 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9567 if ( freeFaceList.size() > 1 )
9569 // could not choose one face, use another way
9570 // choose a face most close to the bary center of the opposite side
9571 gp_XYZ aBC( 0., 0., 0. );
9572 set <const SMDS_MeshNode*> addedNodes;
9573 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9574 eIt = elemSet2->begin();
9575 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9576 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9577 while ( nodeIt->more() ) { // loop on free face nodes
9578 const SMDS_MeshNode* n =
9579 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9580 if ( addedNodes.insert( n ).second )
9581 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9584 aBC /= addedNodes.size();
9585 double minDist = DBL_MAX;
9586 fIt = freeFaceList.begin();
9587 while ( fIt != freeFaceList.end() ) { // loop on free faces
9589 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9590 while ( nodeIt->more() ) { // loop on free face nodes
9591 const SMDS_MeshNode* n =
9592 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9593 gp_XYZ p( n->X(),n->Y(),n->Z() );
9594 dist += ( aBC - p ).SquareModulus();
9596 if ( dist < minDist ) {
9598 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9601 fIt = freeFaceList.erase( fIt++ );
9604 } // choose one of several free faces of a volume
9606 if ( freeFaceList.size() == 1 ) {
9607 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9608 faceSet->insert( aFreeFace );
9609 // complete a node set with nodes of a found free face
9610 // for ( iNode = 0; iNode < ; iNode++ )
9611 // nodeSet->insert( fNodes[ iNode ] );
9614 } // loop on volumes of a side
9616 // // complete a set of faces if new nodes in a nodeSet appeared
9617 // // ----------------------------------------------------------
9618 // if ( nodeSetSize != nodeSet->size() ) {
9619 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9620 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9621 // while ( fIt->more() ) { // loop on faces sharing a node
9622 // const SMDS_MeshElement* f = fIt->next();
9623 // if ( faceSet->find( f ) == faceSet->end() ) {
9624 // // check if all nodes are in nodeSet and
9625 // // complete setOfFaceNodeSet if they are
9626 // set <const SMDS_MeshNode*> faceNodeSet;
9627 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9628 // bool allInSet = true;
9629 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9630 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9631 // if ( nodeSet->find( n ) == nodeSet->end() )
9632 // allInSet = false;
9634 // faceNodeSet.insert( n );
9636 // if ( allInSet ) {
9637 // faceSet->insert( f );
9638 // setOfFaceNodeSet.insert( faceNodeSet );
9644 } // Create temporary faces, if there are volumes given
9647 if ( faceSet1.size() != faceSet2.size() ) {
9648 // delete temporary faces: they are in reverseElements of actual nodes
9649 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9650 // while ( tmpFaceIt->more() )
9651 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9652 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9653 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9654 // aMesh->RemoveElement(*tmpFaceIt);
9655 MESSAGE("Diff nb of faces");
9656 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9659 // ============================================================
9660 // 2. Find nodes to merge:
9661 // bind a node to remove to a node to put instead
9662 // ============================================================
9664 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9665 if ( theFirstNode1 != theFirstNode2 )
9666 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9667 if ( theSecondNode1 != theSecondNode2 )
9668 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9670 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9671 set< long > linkIdSet; // links to process
9672 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9674 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9675 list< NLink > linkList[2];
9676 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9677 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9678 // loop on links in linkList; find faces by links and append links
9679 // of the found faces to linkList
9680 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9681 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9683 NLink link[] = { *linkIt[0], *linkIt[1] };
9684 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9685 if ( !linkIdSet.count( linkID ) )
9688 // by links, find faces in the face sets,
9689 // and find indices of link nodes in the found faces;
9690 // in a face set, there is only one or no face sharing a link
9691 // ---------------------------------------------------------------
9693 const SMDS_MeshElement* face[] = { 0, 0 };
9694 vector<const SMDS_MeshNode*> fnodes[2];
9695 int iLinkNode[2][2];
9696 TIDSortedElemSet avoidSet;
9697 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9698 const SMDS_MeshNode* n1 = link[iSide].first;
9699 const SMDS_MeshNode* n2 = link[iSide].second;
9700 //cout << "Side " << iSide << " ";
9701 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9702 // find a face by two link nodes
9703 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9704 *faceSetPtr[ iSide ], avoidSet,
9705 &iLinkNode[iSide][0],
9706 &iLinkNode[iSide][1] );
9709 //cout << " F " << face[ iSide]->GetID() <<endl;
9710 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9711 // put face nodes to fnodes
9712 SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9713 fnodes[ iSide ].assign( nIt, nEnd );
9714 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9718 // check similarity of elements of the sides
9719 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9720 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9721 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9722 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9725 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9727 break; // do not return because it's necessary to remove tmp faces
9730 // set nodes to merge
9731 // -------------------
9733 if ( face[0] && face[1] ) {
9734 const int nbNodes = face[0]->NbNodes();
9735 if ( nbNodes != face[1]->NbNodes() ) {
9736 MESSAGE("Diff nb of face nodes");
9737 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9738 break; // do not return because it s necessary to remove tmp faces
9740 bool reverse[] = { false, false }; // order of nodes in the link
9741 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9742 // analyse link orientation in faces
9743 int i1 = iLinkNode[ iSide ][ 0 ];
9744 int i2 = iLinkNode[ iSide ][ 1 ];
9745 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9747 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9748 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9749 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9751 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9752 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9755 // add other links of the faces to linkList
9756 // -----------------------------------------
9758 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9759 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9760 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9761 if ( !iter_isnew.second ) { // already in a set: no need to process
9762 linkIdSet.erase( iter_isnew.first );
9764 else // new in set == encountered for the first time: add
9766 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9767 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9768 linkList[0].push_back ( NLink( n1, n2 ));
9769 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9774 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9777 } // loop on link lists
9779 if ( aResult == SEW_OK &&
9780 ( //linkIt[0] != linkList[0].end() ||
9781 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9782 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9783 " " << (faceSetPtr[1]->empty()));
9784 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9787 // ====================================================================
9788 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9789 // ====================================================================
9791 // delete temporary faces
9792 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9793 // while ( tmpFaceIt->more() )
9794 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9795 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9796 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9797 aMesh->RemoveElement(*tmpFaceIt);
9799 if ( aResult != SEW_OK)
9802 list< int > nodeIDsToRemove;
9803 vector< const SMDS_MeshNode*> nodes;
9804 ElemFeatures elemType;
9806 // loop on nodes replacement map
9807 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9808 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9809 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9811 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9812 nodeIDsToRemove.push_back( nToRemove->GetID() );
9813 // loop on elements sharing nToRemove
9814 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9815 while ( invElemIt->more() ) {
9816 const SMDS_MeshElement* e = invElemIt->next();
9817 // get a new suite of nodes: make replacement
9818 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9819 nodes.resize( nbNodes );
9820 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9821 while ( nIt->more() ) {
9822 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9823 nnIt = nReplaceMap.find( n );
9824 if ( nnIt != nReplaceMap.end() ) {
9830 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9831 // elemIDsToRemove.push_back( e->GetID() );
9835 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9836 aMesh->RemoveElement( e );
9838 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9840 AddToSameGroups( newElem, e, aMesh );
9841 if ( int aShapeId = e->getshapeId() )
9842 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9848 Remove( nodeIDsToRemove, true );
9853 //================================================================================
9855 * \brief Find corresponding nodes in two sets of faces
9856 * \param theSide1 - first face set
9857 * \param theSide2 - second first face
9858 * \param theFirstNode1 - a boundary node of set 1
9859 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9860 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9861 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9862 * \param nReplaceMap - output map of corresponding nodes
9863 * \return bool - is a success or not
9865 //================================================================================
9868 //#define DEBUG_MATCHING_NODES
9871 SMESH_MeshEditor::Sew_Error
9872 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9873 set<const SMDS_MeshElement*>& theSide2,
9874 const SMDS_MeshNode* theFirstNode1,
9875 const SMDS_MeshNode* theFirstNode2,
9876 const SMDS_MeshNode* theSecondNode1,
9877 const SMDS_MeshNode* theSecondNode2,
9878 TNodeNodeMap & nReplaceMap)
9880 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9882 nReplaceMap.clear();
9883 if ( theFirstNode1 != theFirstNode2 )
9884 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9885 if ( theSecondNode1 != theSecondNode2 )
9886 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9888 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9889 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9891 list< NLink > linkList[2];
9892 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9893 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9895 // loop on links in linkList; find faces by links and append links
9896 // of the found faces to linkList
9897 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9898 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9899 NLink link[] = { *linkIt[0], *linkIt[1] };
9900 if ( linkSet.find( link[0] ) == linkSet.end() )
9903 // by links, find faces in the face sets,
9904 // and find indices of link nodes in the found faces;
9905 // in a face set, there is only one or no face sharing a link
9906 // ---------------------------------------------------------------
9908 const SMDS_MeshElement* face[] = { 0, 0 };
9909 list<const SMDS_MeshNode*> notLinkNodes[2];
9910 //bool reverse[] = { false, false }; // order of notLinkNodes
9912 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9914 const SMDS_MeshNode* n1 = link[iSide].first;
9915 const SMDS_MeshNode* n2 = link[iSide].second;
9916 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9917 set< const SMDS_MeshElement* > facesOfNode1;
9918 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9920 // during a loop of the first node, we find all faces around n1,
9921 // during a loop of the second node, we find one face sharing both n1 and n2
9922 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9923 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9924 while ( fIt->more() ) { // loop on faces sharing a node
9925 const SMDS_MeshElement* f = fIt->next();
9926 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9927 ! facesOfNode1.insert( f ).second ) // f encounters twice
9929 if ( face[ iSide ] ) {
9930 MESSAGE( "2 faces per link " );
9931 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9934 faceSet->erase( f );
9936 // get not link nodes
9937 int nbN = f->NbNodes();
9938 if ( f->IsQuadratic() )
9940 nbNodes[ iSide ] = nbN;
9941 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9942 int i1 = f->GetNodeIndex( n1 );
9943 int i2 = f->GetNodeIndex( n2 );
9944 int iEnd = nbN, iBeg = -1, iDelta = 1;
9945 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9947 std::swap( iEnd, iBeg ); iDelta = -1;
9952 if ( i == iEnd ) i = iBeg + iDelta;
9953 if ( i == i1 ) break;
9954 nodes.push_back ( f->GetNode( i ) );
9960 // check similarity of elements of the sides
9961 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9962 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9963 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9964 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9967 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9971 // set nodes to merge
9972 // -------------------
9974 if ( face[0] && face[1] ) {
9975 if ( nbNodes[0] != nbNodes[1] ) {
9976 MESSAGE("Diff nb of face nodes");
9977 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9979 #ifdef DEBUG_MATCHING_NODES
9980 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9981 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9982 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9984 int nbN = nbNodes[0];
9986 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9987 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9988 for ( int i = 0 ; i < nbN - 2; ++i ) {
9989 #ifdef DEBUG_MATCHING_NODES
9990 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9992 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9996 // add other links of the face 1 to linkList
9997 // -----------------------------------------
9999 const SMDS_MeshElement* f0 = face[0];
10000 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10001 for ( int i = 0; i < nbN; i++ )
10003 const SMDS_MeshNode* n2 = f0->GetNode( i );
10004 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10005 linkSet.insert( SMESH_TLink( n1, n2 ));
10006 if ( !iter_isnew.second ) { // already in a set: no need to process
10007 linkSet.erase( iter_isnew.first );
10009 else // new in set == encountered for the first time: add
10011 #ifdef DEBUG_MATCHING_NODES
10012 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10013 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10015 linkList[0].push_back ( NLink( n1, n2 ));
10016 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10021 } // loop on link lists
10026 namespace // automatically find theAffectedElems for DoubleNodes()
10028 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10030 //--------------------------------------------------------------------------------
10031 // Nodes shared by adjacent FissureBorder's.
10032 // 1 node if FissureBorder separates faces
10033 // 2 nodes if FissureBorder separates volumes
10036 const SMDS_MeshNode* _nodes[2];
10039 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10043 _nbNodes = bool( n1 ) + bool( n2 );
10044 if ( _nbNodes == 2 && n1 > n2 )
10045 std::swap( _nodes[0], _nodes[1] );
10047 bool operator<( const SubBorder& other ) const
10049 for ( int i = 0; i < _nbNodes; ++i )
10051 if ( _nodes[i] < other._nodes[i] ) return true;
10052 if ( _nodes[i] > other._nodes[i] ) return false;
10058 //--------------------------------------------------------------------------------
10059 // Map a SubBorder to all FissureBorder it bounds
10060 struct FissureBorder;
10061 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10062 typedef TBorderLinks::iterator TMappedSub;
10064 //--------------------------------------------------------------------------------
10066 * \brief Element border (volume facet or face edge) at a fissure
10068 struct FissureBorder
10070 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10071 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10073 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10074 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10076 FissureBorder( FissureBorder && from ) // move constructor
10078 std::swap( _nodes, from._nodes );
10079 std::swap( _sortedNodes, from._sortedNodes );
10080 _elems[0] = from._elems[0];
10081 _elems[1] = from._elems[1];
10084 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10085 std::vector< const SMDS_MeshElement* > & adjElems)
10086 : _nodes( elemToDuplicate->NbCornerNodes() )
10088 for ( size_t i = 0; i < _nodes.size(); ++i )
10089 _nodes[i] = elemToDuplicate->GetNode( i );
10091 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10092 findAdjacent( type, adjElems );
10095 FissureBorder( const SMDS_MeshNode** nodes,
10096 const size_t nbNodes,
10097 const SMDSAbs_ElementType adjElemsType,
10098 std::vector< const SMDS_MeshElement* > & adjElems)
10099 : _nodes( nodes, nodes + nbNodes )
10101 findAdjacent( adjElemsType, adjElems );
10104 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10105 std::vector< const SMDS_MeshElement* > & adjElems)
10107 _elems[0] = _elems[1] = 0;
10109 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10110 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10111 _elems[i] = adjElems[i];
10114 bool operator<( const FissureBorder& other ) const
10116 return GetSortedNodes() < other.GetSortedNodes();
10119 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10121 if ( _sortedNodes.empty() && !_nodes.empty() )
10123 FissureBorder* me = const_cast<FissureBorder*>( this );
10124 me->_sortedNodes = me->_nodes;
10125 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10127 return _sortedNodes;
10130 size_t NbSub() const
10132 return _nodes.size();
10135 SubBorder Sub(size_t i) const
10137 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10140 void AddSelfTo( TBorderLinks& borderLinks )
10142 _mappedSubs.resize( NbSub() );
10143 for ( size_t i = 0; i < NbSub(); ++i )
10145 TBorderLinks::iterator s2b =
10146 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10147 s2b->second.push_back( this );
10148 _mappedSubs[ i ] = s2b;
10157 const SMDS_MeshElement* GetMarkedElem() const
10159 if ( _nodes.empty() ) return 0; // cleared
10160 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10161 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10165 gp_XYZ GetNorm() const // normal to the border
10168 if ( _nodes.size() == 2 )
10170 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10171 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10173 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10176 gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10177 norm = bordDir ^ avgNorm;
10181 SMESH_NodeXYZ p0( _nodes[0] );
10182 SMESH_NodeXYZ p1( _nodes[1] );
10183 SMESH_NodeXYZ p2( _nodes[2] );
10184 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10186 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10192 void ChooseSide() // mark an _elem located at positive side of fissure
10194 _elems[0]->setIsMarked( true );
10195 gp_XYZ norm = GetNorm();
10196 double maxX = norm.Coord(1);
10197 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10198 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10201 _elems[0]->setIsMarked( false );
10202 _elems[1]->setIsMarked( true );
10206 }; // struct FissureBorder
10208 //--------------------------------------------------------------------------------
10210 * \brief Classifier of elements at fissure edge
10212 class FissureNormal
10214 std::vector< gp_XYZ > _normals;
10218 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10221 _normals.reserve(2);
10222 _normals.push_back( bord.GetNorm() );
10223 if ( _normals.size() == 2 )
10224 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10227 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10230 switch ( _normals.size() ) {
10233 isIn = !isOut( n, _normals[0], elem );
10238 bool in1 = !isOut( n, _normals[0], elem );
10239 bool in2 = !isOut( n, _normals[1], elem );
10240 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10247 //================================================================================
10249 * \brief Classify an element by a plane passing through a node
10251 //================================================================================
10253 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10255 SMESH_NodeXYZ p = n;
10257 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10259 SMESH_NodeXYZ pi = elem->GetNode( i );
10260 sumDot += norm * ( pi - p );
10262 return sumDot < -1e-100;
10265 //================================================================================
10267 * \brief Find FissureBorder's by nodes to duplicate
10269 //================================================================================
10271 void findFissureBorders( const TIDSortedElemSet& theNodes,
10272 std::vector< FissureBorder > & theFissureBorders )
10274 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10275 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10277 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10278 if ( n->NbInverseElements( elemType ) == 0 )
10280 elemType = SMDSAbs_Face;
10281 if ( n->NbInverseElements( elemType ) == 0 )
10284 // unmark elements touching the fissure
10285 for ( ; nIt != theNodes.end(); ++nIt )
10286 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10288 // loop on elements touching the fissure to get their borders belonging to the fissure
10289 std::set< FissureBorder > fissureBorders;
10290 std::vector< const SMDS_MeshElement* > adjElems;
10291 std::vector< const SMDS_MeshNode* > nodes;
10292 SMDS_VolumeTool volTool;
10293 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10295 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10296 while ( invIt->more() )
10298 const SMDS_MeshElement* eInv = invIt->next();
10299 if ( eInv->isMarked() ) continue;
10300 eInv->setIsMarked( true );
10302 if ( elemType == SMDSAbs_Volume )
10304 volTool.Set( eInv );
10305 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10306 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10308 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10309 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10311 bool allOnFissure = true;
10312 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10313 if (( allOnFissure = theNodes.count( nn[ iN ])))
10314 nodes.push_back( nn[ iN ]);
10315 if ( allOnFissure )
10316 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10317 elemType, adjElems )));
10320 else // elemType == SMDSAbs_Face
10322 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10323 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10324 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10326 nn[1] = eInv->GetNode( iN );
10327 onFissure1 = theNodes.count( nn[1] );
10328 if ( onFissure0 && onFissure1 )
10329 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10331 onFissure0 = onFissure1;
10337 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10338 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10339 for ( ; bord != fissureBorders.end(); ++bord )
10341 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10344 } // findFissureBorders()
10346 //================================================================================
10348 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10349 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10350 * \param [in] theNodesNot - nodes not to duplicate
10351 * \param [out] theAffectedElems - the found elements
10353 //================================================================================
10355 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10356 TIDSortedElemSet& theAffectedElems)
10358 if ( theElemsOrNodes.empty() ) return;
10360 // find FissureBorder's
10362 std::vector< FissureBorder > fissure;
10363 std::vector< const SMDS_MeshElement* > elemsByFacet;
10365 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10366 if ( (*elIt)->GetType() == SMDSAbs_Node )
10368 findFissureBorders( theElemsOrNodes, fissure );
10372 fissure.reserve( theElemsOrNodes.size() );
10373 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10374 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10376 if ( fissure.empty() )
10379 // fill borderLinks
10381 TBorderLinks borderLinks;
10383 for ( size_t i = 0; i < fissure.size(); ++i )
10385 fissure[i].AddSelfTo( borderLinks );
10388 // get theAffectedElems
10390 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10391 for ( size_t i = 0; i < fissure.size(); ++i )
10392 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10394 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10395 false, /*markElem=*/true );
10398 std::vector<const SMDS_MeshNode *> facetNodes;
10399 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10400 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10402 // choose a side of fissure
10403 fissure[0].ChooseSide();
10404 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10406 size_t nbCheckedBorders = 0;
10407 while ( nbCheckedBorders < fissure.size() )
10409 // find a FissureBorder to treat
10410 FissureBorder* bord = 0;
10411 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10412 if ( fissure[i].GetMarkedElem() )
10413 bord = & fissure[i];
10414 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10415 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10417 bord = & fissure[i];
10418 bord->ChooseSide();
10419 theAffectedElems.insert( bord->GetMarkedElem() );
10421 if ( !bord ) return;
10422 ++nbCheckedBorders;
10424 // treat FissureBorder's linked to bord
10425 fissureNodes.clear();
10426 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10427 for ( size_t i = 0; i < bord->NbSub(); ++i )
10429 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10430 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10431 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10432 const SubBorder& sb = l2b->first;
10433 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10435 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10437 for ( int j = 0; j < sb._nbNodes; ++j )
10438 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10442 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10443 // until an elem adjacent to a neighbour FissureBorder is found
10444 facetNodes.clear();
10445 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10446 facetNodes.resize( sb._nbNodes + 1 );
10450 // check if bordElem is adjacent to a neighbour FissureBorder
10451 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10453 FissureBorder* bord2 = linkedBorders[j];
10454 if ( bord2 == bord ) continue;
10455 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10458 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10463 // find the next bordElem
10464 const SMDS_MeshElement* nextBordElem = 0;
10465 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10467 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10468 if ( fissureNodes.count( n )) continue;
10470 facetNodes[ sb._nbNodes ] = n;
10471 elemsByFacet.clear();
10472 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10474 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10475 if ( elemsByFacet[ iE ] != bordElem &&
10476 !elemsByFacet[ iE ]->isMarked() )
10478 theAffectedElems.insert( elemsByFacet[ iE ]);
10479 elemsByFacet[ iE ]->setIsMarked( true );
10480 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10481 nextBordElem = elemsByFacet[ iE ];
10485 bordElem = nextBordElem;
10487 } // while ( bordElem )
10489 linkedBorders.clear(); // not to treat this link any more
10491 } // loop on SubBorder's of a FissureBorder
10495 } // loop on FissureBorder's
10498 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10500 // mark nodes of theAffectedElems
10501 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10503 // unmark nodes of the fissure
10504 elIt = theElemsOrNodes.begin();
10505 if ( (*elIt)->GetType() == SMDSAbs_Node )
10506 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10508 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10510 std::vector< gp_XYZ > normVec;
10512 // loop on nodes of the fissure, add elements having marked nodes
10513 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10515 const SMDS_MeshElement* e = (*elIt);
10516 if ( e->GetType() != SMDSAbs_Node )
10517 e->setIsMarked( true ); // avoid adding a fissure element
10519 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10521 const SMDS_MeshNode* n = e->GetNode( iN );
10522 if ( fissEdgeNodes2Norm.count( n ))
10525 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10526 while ( invIt->more() )
10528 const SMDS_MeshElement* eInv = invIt->next();
10529 if ( eInv->isMarked() ) continue;
10530 eInv->setIsMarked( true );
10532 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10533 while( nIt->more() )
10534 if ( nIt->next()->isMarked())
10536 theAffectedElems.insert( eInv );
10537 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10538 n->setIsMarked( false );
10545 // add elements on the fissure edge
10546 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10547 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10549 const SMDS_MeshNode* edgeNode = n2N->first;
10550 const FissureNormal & normals = n2N->second;
10552 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10553 while ( invIt->more() )
10555 const SMDS_MeshElement* eInv = invIt->next();
10556 if ( eInv->isMarked() ) continue;
10557 eInv->setIsMarked( true );
10559 // classify eInv using normals
10560 bool toAdd = normals.IsIn( edgeNode, eInv );
10561 if ( toAdd ) // check if all nodes lie on the fissure edge
10563 bool notOnEdge = false;
10564 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
10565 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10570 theAffectedElems.insert( eInv );
10576 } // findAffectedElems()
10579 //================================================================================
10581 * \brief Create elements equal (on same nodes) to given ones
10582 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10583 * elements of the uppest dimension are duplicated.
10585 //================================================================================
10587 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10589 ClearLastCreated();
10590 SMESHDS_Mesh* mesh = GetMeshDS();
10592 // get an element type and an iterator over elements
10594 SMDSAbs_ElementType type = SMDSAbs_All;
10595 SMDS_ElemIteratorPtr elemIt;
10596 if ( theElements.empty() )
10598 if ( mesh->NbNodes() == 0 )
10600 // get most complex type
10601 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10602 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10603 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10605 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10606 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10609 elemIt = mesh->elementsIterator( type );
10615 //type = (*theElements.begin())->GetType();
10616 elemIt = SMESHUtils::elemSetIterator( theElements );
10619 // un-mark all elements to avoid duplicating just created elements
10620 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10622 // duplicate elements
10624 ElemFeatures elemType;
10626 vector< const SMDS_MeshNode* > nodes;
10627 while ( elemIt->more() )
10629 const SMDS_MeshElement* elem = elemIt->next();
10630 if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10631 ( elem->isMarked() ))
10634 elemType.Init( elem, /*basicOnly=*/false );
10635 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10637 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10638 newElem->setIsMarked( true );
10642 //================================================================================
10644 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10645 \param theElems - the list of elements (edges or faces) to be replicated
10646 The nodes for duplication could be found from these elements
10647 \param theNodesNot - list of nodes to NOT replicate
10648 \param theAffectedElems - the list of elements (cells and edges) to which the
10649 replicated nodes should be associated to.
10650 \return TRUE if operation has been completed successfully, FALSE otherwise
10652 //================================================================================
10654 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10655 const TIDSortedElemSet& theNodesNot,
10656 const TIDSortedElemSet& theAffectedElems )
10658 ClearLastCreated();
10660 if ( theElems.size() == 0 )
10663 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10668 TNodeNodeMap anOldNodeToNewNode;
10669 // duplicate elements and nodes
10670 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10671 // replce nodes by duplications
10672 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10676 //================================================================================
10678 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10679 \param theMeshDS - mesh instance
10680 \param theElems - the elements replicated or modified (nodes should be changed)
10681 \param theNodesNot - nodes to NOT replicate
10682 \param theNodeNodeMap - relation of old node to new created node
10683 \param theIsDoubleElem - flag os to replicate element or modify
10684 \return TRUE if operation has been completed successfully, FALSE otherwise
10686 //================================================================================
10688 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10689 const TIDSortedElemSet& theElems,
10690 const TIDSortedElemSet& theNodesNot,
10691 TNodeNodeMap& theNodeNodeMap,
10692 const bool theIsDoubleElem )
10694 // iterate through element and duplicate them (by nodes duplication)
10696 std::vector<const SMDS_MeshNode*> newNodes;
10697 ElemFeatures elemType;
10699 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10700 for ( ; elemItr != theElems.end(); ++elemItr )
10702 const SMDS_MeshElement* anElem = *elemItr;
10706 // duplicate nodes to duplicate element
10707 bool isDuplicate = false;
10708 newNodes.resize( anElem->NbNodes() );
10709 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10711 while ( anIter->more() )
10713 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10714 const SMDS_MeshNode* aNewNode = aCurrNode;
10715 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10716 if ( n2n != theNodeNodeMap.end() )
10718 aNewNode = n2n->second;
10720 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10723 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10724 copyPosition( aCurrNode, aNewNode );
10725 theNodeNodeMap[ aCurrNode ] = aNewNode;
10726 myLastCreatedNodes.push_back( aNewNode );
10728 isDuplicate |= (aCurrNode != aNewNode);
10729 newNodes[ ind++ ] = aNewNode;
10731 if ( !isDuplicate )
10734 if ( theIsDoubleElem )
10735 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10737 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10744 //================================================================================
10746 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10747 \param theNodes - identifiers of nodes to be doubled
10748 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10749 nodes. If list of element identifiers is empty then nodes are doubled but
10750 they not assigned to elements
10751 \return TRUE if operation has been completed successfully, FALSE otherwise
10753 //================================================================================
10755 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10756 const std::list< int >& theListOfModifiedElems )
10758 ClearLastCreated();
10760 if ( theListOfNodes.size() == 0 )
10763 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10767 // iterate through nodes and duplicate them
10769 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10771 std::list< int >::const_iterator aNodeIter;
10772 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10774 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10780 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10783 copyPosition( aNode, aNewNode );
10784 anOldNodeToNewNode[ aNode ] = aNewNode;
10785 myLastCreatedNodes.push_back( aNewNode );
10789 // Change nodes of elements
10791 std::vector<const SMDS_MeshNode*> aNodeArr;
10793 std::list< int >::const_iterator anElemIter;
10794 for ( anElemIter = theListOfModifiedElems.begin();
10795 anElemIter != theListOfModifiedElems.end();
10798 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10802 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10803 for( size_t i = 0; i < aNodeArr.size(); ++i )
10805 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10806 anOldNodeToNewNode.find( aNodeArr[ i ]);
10807 if ( n2n != anOldNodeToNewNode.end() )
10808 aNodeArr[ i ] = n2n->second;
10810 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10818 //================================================================================
10820 \brief Check if element located inside shape
10821 \return TRUE if IN or ON shape, FALSE otherwise
10823 //================================================================================
10825 template<class Classifier>
10826 bool isInside(const SMDS_MeshElement* theElem,
10827 Classifier& theClassifier,
10828 const double theTol)
10830 gp_XYZ centerXYZ (0, 0, 0);
10831 for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10832 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10834 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10835 theClassifier.Perform(aPnt, theTol);
10836 TopAbs_State aState = theClassifier.State();
10837 return (aState == TopAbs_IN || aState == TopAbs_ON );
10840 //================================================================================
10842 * \brief Classifier of the 3D point on the TopoDS_Face
10843 * with interaface suitable for isInside()
10845 //================================================================================
10847 struct _FaceClassifier
10849 Extrema_ExtPS _extremum;
10850 BRepAdaptor_Surface _surface;
10851 TopAbs_State _state;
10853 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10855 _extremum.Initialize( _surface,
10856 _surface.FirstUParameter(), _surface.LastUParameter(),
10857 _surface.FirstVParameter(), _surface.LastVParameter(),
10858 _surface.Tolerance(), _surface.Tolerance() );
10860 void Perform(const gp_Pnt& aPnt, double theTol)
10863 _state = TopAbs_OUT;
10864 _extremum.Perform(aPnt);
10865 if ( _extremum.IsDone() )
10866 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10867 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10869 TopAbs_State State() const
10876 //================================================================================
10878 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10879 This method is the first step of DoubleNodeElemGroupsInRegion.
10880 \param theElems - list of groups of elements (edges or faces) to be replicated
10881 \param theNodesNot - list of groups of nodes not to replicated
10882 \param theShape - shape to detect affected elements (element which geometric center
10883 located on or inside shape). If the shape is null, detection is done on faces orientations
10884 (select elements with a gravity center on the side given by faces normals).
10885 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10886 The replicated nodes should be associated to affected elements.
10888 \sa DoubleNodeElemGroupsInRegion()
10890 //================================================================================
10892 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10893 const TIDSortedElemSet& theNodesNot,
10894 const TopoDS_Shape& theShape,
10895 TIDSortedElemSet& theAffectedElems)
10897 if ( theShape.IsNull() )
10899 findAffectedElems( theElems, theAffectedElems );
10903 const double aTol = Precision::Confusion();
10904 std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10905 std::unique_ptr<_FaceClassifier> aFaceClassifier;
10906 if ( theShape.ShapeType() == TopAbs_SOLID )
10908 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10909 bsc3d->PerformInfinitePoint(aTol);
10911 else if (theShape.ShapeType() == TopAbs_FACE )
10913 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10916 // iterates on indicated elements and get elements by back references from their nodes
10917 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10918 for ( ; elemItr != theElems.end(); ++elemItr )
10920 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10921 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10922 while ( nodeItr->more() )
10924 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10925 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10927 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10928 while ( backElemItr->more() )
10930 const SMDS_MeshElement* curElem = backElemItr->next();
10931 if ( curElem && theElems.find(curElem) == theElems.end() &&
10933 isInside( curElem, *bsc3d, aTol ) :
10934 isInside( curElem, *aFaceClassifier, aTol )))
10935 theAffectedElems.insert( curElem );
10943 //================================================================================
10945 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10946 \param theElems - group of of elements (edges or faces) to be replicated
10947 \param theNodesNot - group of nodes not to replicate
10948 \param theShape - shape to detect affected elements (element which geometric center
10949 located on or inside shape).
10950 The replicated nodes should be associated to affected elements.
10951 \return TRUE if operation has been completed successfully, FALSE otherwise
10953 //================================================================================
10955 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10956 const TIDSortedElemSet& theNodesNot,
10957 const TopoDS_Shape& theShape )
10959 if ( theShape.IsNull() )
10962 const double aTol = Precision::Confusion();
10963 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10964 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
10965 if ( theShape.ShapeType() == TopAbs_SOLID )
10967 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10968 bsc3d->PerformInfinitePoint(aTol);
10970 else if (theShape.ShapeType() == TopAbs_FACE )
10972 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
10975 // iterates on indicated elements and get elements by back references from their nodes
10976 TIDSortedElemSet anAffected;
10977 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10978 for ( ; elemItr != theElems.end(); ++elemItr )
10980 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10984 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10985 while ( nodeItr->more() )
10987 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10988 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10990 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10991 while ( backElemItr->more() )
10993 const SMDS_MeshElement* curElem = backElemItr->next();
10994 if ( curElem && theElems.find(curElem) == theElems.end() &&
10996 isInside( curElem, *bsc3d, aTol ) :
10997 isInside( curElem, *aFaceClassifier, aTol )))
10998 anAffected.insert( curElem );
11002 return DoubleNodes( theElems, theNodesNot, anAffected );
11006 * \brief compute an oriented angle between two planes defined by four points.
11007 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11008 * @param p0 base of the rotation axe
11009 * @param p1 extremity of the rotation axe
11010 * @param g1 belongs to the first plane
11011 * @param g2 belongs to the second plane
11013 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11015 gp_Vec vref(p0, p1);
11018 gp_Vec n1 = vref.Crossed(v1);
11019 gp_Vec n2 = vref.Crossed(v2);
11021 return n2.AngleWithRef(n1, vref);
11023 catch ( Standard_Failure ) {
11025 return Max( v1.Magnitude(), v2.Magnitude() );
11029 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11030 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11031 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11032 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11033 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11034 * 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.
11035 * 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.
11036 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11037 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11038 * \param theElems - list of groups of volumes, where a group of volume is a set of
11039 * SMDS_MeshElements sorted by Id.
11040 * \param createJointElems - if TRUE, create the elements
11041 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11042 * the boundary between \a theDomains and the rest mesh
11043 * \return TRUE if operation has been completed successfully, FALSE otherwise
11045 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11046 bool createJointElems,
11047 bool onAllBoundaries)
11049 // MESSAGE("----------------------------------------------");
11050 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11051 // MESSAGE("----------------------------------------------");
11053 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11054 meshDS->BuildDownWardConnectivity(true);
11056 SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11058 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11059 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11060 // build the list of nodes shared by 2 or more domains, with their domain indexes
11062 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11063 std::map<int,int>celldom; // cell vtkId --> domain
11064 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11065 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11066 faceDomains.clear();
11068 cellDomains.clear();
11069 nodeDomains.clear();
11070 std::map<int,int> emptyMap;
11071 std::set<int> emptySet;
11074 //MESSAGE(".. Number of domains :"<<theElems.size());
11076 TIDSortedElemSet theRestDomElems;
11077 const int iRestDom = -1;
11078 const int idom0 = onAllBoundaries ? iRestDom : 0;
11079 const int nbDomains = theElems.size();
11081 // Check if the domains do not share an element
11082 for (int idom = 0; idom < nbDomains-1; idom++)
11084 // MESSAGE("... Check of domain #" << idom);
11085 const TIDSortedElemSet& domain = theElems[idom];
11086 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11087 for (; elemItr != domain.end(); ++elemItr)
11089 const SMDS_MeshElement* anElem = *elemItr;
11090 int idombisdeb = idom + 1 ;
11091 // check if the element belongs to a domain further in the list
11092 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11094 const TIDSortedElemSet& domainbis = theElems[idombis];
11095 if ( domainbis.count( anElem ))
11097 MESSAGE(".... Domain #" << idom);
11098 MESSAGE(".... Domain #" << idombis);
11099 throw SALOME_Exception("The domains are not disjoint.");
11106 for (int idom = 0; idom < nbDomains; idom++)
11109 // --- build a map (face to duplicate --> volume to modify)
11110 // with all the faces shared by 2 domains (group of elements)
11111 // and corresponding volume of this domain, for each shared face.
11112 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11114 //MESSAGE("... Neighbors of domain #" << idom);
11115 const TIDSortedElemSet& domain = theElems[idom];
11116 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11117 for (; elemItr != domain.end(); ++elemItr)
11119 const SMDS_MeshElement* anElem = *elemItr;
11122 int vtkId = anElem->GetVtkID();
11123 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11124 int neighborsVtkIds[NBMAXNEIGHBORS];
11125 int downIds[NBMAXNEIGHBORS];
11126 unsigned char downTypes[NBMAXNEIGHBORS];
11127 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11128 for (int n = 0; n < nbNeighbors; n++)
11130 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11131 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11132 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11135 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11137 // MESSAGE("Domain " << idombis);
11138 const TIDSortedElemSet& domainbis = theElems[idombis];
11139 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11141 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11143 DownIdType face(downIds[n], downTypes[n]);
11144 if (!faceDomains[face].count(idom))
11146 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11147 celldom[vtkId] = idom;
11148 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11152 theRestDomElems.insert( elem );
11153 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11154 celldom[neighborsVtkIds[n]] = iRestDom;
11162 //MESSAGE("Number of shared faces " << faceDomains.size());
11163 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11165 // --- explore the shared faces domain by domain,
11166 // explore the nodes of the face and see if they belong to a cell in the domain,
11167 // which has only a node or an edge on the border (not a shared face)
11169 for (int idomain = idom0; idomain < nbDomains; idomain++)
11171 //MESSAGE("Domain " << idomain);
11172 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11173 itface = faceDomains.begin();
11174 for (; itface != faceDomains.end(); ++itface)
11176 const std::map<int, int>& domvol = itface->second;
11177 if (!domvol.count(idomain))
11179 DownIdType face = itface->first;
11180 //MESSAGE(" --- face " << face.cellId);
11181 std::set<int> oldNodes;
11183 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11184 std::set<int>::iterator itn = oldNodes.begin();
11185 for (; itn != oldNodes.end(); ++itn)
11188 //MESSAGE(" node " << oldId);
11189 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11190 for (int i=0; i<l.ncells; i++)
11192 int vtkId = l.cells[i];
11193 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11194 if (!domain.count(anElem))
11196 int vtkType = grid->GetCellType(vtkId);
11197 int downId = grid->CellIdToDownId(vtkId);
11200 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11201 continue; // not OK at this stage of the algorithm:
11202 //no cells created after BuildDownWardConnectivity
11204 DownIdType aCell(downId, vtkType);
11205 cellDomains[aCell][idomain] = vtkId;
11206 celldom[vtkId] = idomain;
11207 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11213 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11214 // for each shared face, get the nodes
11215 // for each node, for each domain of the face, create a clone of the node
11217 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11218 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11219 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11221 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11222 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11223 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11225 //MESSAGE(".. Duplication of the nodes");
11226 for (int idomain = idom0; idomain < nbDomains; idomain++)
11228 itface = faceDomains.begin();
11229 for (; itface != faceDomains.end(); ++itface)
11231 const std::map<int, int>& domvol = itface->second;
11232 if (!domvol.count(idomain))
11234 DownIdType face = itface->first;
11235 //MESSAGE(" --- face " << face.cellId);
11236 std::set<int> oldNodes;
11238 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11239 std::set<int>::iterator itn = oldNodes.begin();
11240 for (; itn != oldNodes.end(); ++itn)
11243 if (nodeDomains[oldId].empty())
11245 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11246 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11248 std::map<int, int>::const_iterator itdom = domvol.begin();
11249 for (; itdom != domvol.end(); ++itdom)
11251 int idom = itdom->first;
11252 //MESSAGE(" domain " << idom);
11253 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11255 if (nodeDomains[oldId].size() >= 2) // a multiple node
11257 vector<int> orderedDoms;
11258 //MESSAGE("multiple node " << oldId);
11259 if (mutipleNodes.count(oldId))
11260 orderedDoms = mutipleNodes[oldId];
11263 map<int,int>::iterator it = nodeDomains[oldId].begin();
11264 for (; it != nodeDomains[oldId].end(); ++it)
11265 orderedDoms.push_back(it->first);
11267 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11268 //stringstream txt;
11269 //for (int i=0; i<orderedDoms.size(); i++)
11270 // txt << orderedDoms[i] << " ";
11271 //MESSAGE("orderedDoms " << txt.str());
11272 mutipleNodes[oldId] = orderedDoms;
11274 double *coords = grid->GetPoint(oldId);
11275 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11276 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11277 int newId = newNode->GetVtkID();
11278 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11279 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11286 //MESSAGE(".. Creation of elements");
11287 for (int idomain = idom0; idomain < nbDomains; idomain++)
11289 itface = faceDomains.begin();
11290 for (; itface != faceDomains.end(); ++itface)
11292 std::map<int, int> domvol = itface->second;
11293 if (!domvol.count(idomain))
11295 DownIdType face = itface->first;
11296 //MESSAGE(" --- face " << face.cellId);
11297 std::set<int> oldNodes;
11299 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11300 int nbMultipleNodes = 0;
11301 std::set<int>::iterator itn = oldNodes.begin();
11302 for (; itn != oldNodes.end(); ++itn)
11305 if (mutipleNodes.count(oldId))
11308 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11310 //MESSAGE("multiple Nodes detected on a shared face");
11311 int downId = itface->first.cellId;
11312 unsigned char cellType = itface->first.cellType;
11313 // --- shared edge or shared face ?
11314 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11317 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11318 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11319 if (mutipleNodes.count(nodes[i]))
11320 if (!mutipleNodesToFace.count(nodes[i]))
11321 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11323 else // shared face (between two volumes)
11325 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11326 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11327 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11328 for (int ie =0; ie < nbEdges; ie++)
11331 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11332 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11334 vector<int> vn0 = mutipleNodes[nodes[0]];
11335 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11337 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11338 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11339 if ( vn0[i0] == vn1[i1] )
11340 doms.push_back( vn0[ i0 ]);
11341 if ( doms.size() > 2 )
11343 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11344 double *coords = grid->GetPoint(nodes[0]);
11345 gp_Pnt p0(coords[0], coords[1], coords[2]);
11346 coords = grid->GetPoint(nodes[nbNodes - 1]);
11347 gp_Pnt p1(coords[0], coords[1], coords[2]);
11349 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11350 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11351 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11352 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11353 for ( size_t id = 0; id < doms.size(); id++ )
11355 int idom = doms[id];
11356 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11357 for ( int ivol = 0; ivol < nbvol; ivol++ )
11359 int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11360 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11361 if (domain.count(elem))
11363 const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11364 domvol[idom] = (SMDS_MeshVolume*) svol;
11365 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11366 double values[3] = { 0,0,0 };
11367 vtkIdType npts = 0;
11368 vtkIdType* pts = 0;
11369 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11370 for ( vtkIdType i = 0; i < npts; ++i )
11372 double *coords = grid->GetPoint( pts[i] );
11373 for ( int j = 0; j < 3; ++j )
11374 values[j] += coords[j] / npts;
11378 gref.SetCoord( values[0], values[1], values[2] );
11379 angleDom[idom] = 0;
11383 gp_Pnt g( values[0], values[1], values[2] );
11384 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11385 //MESSAGE(" angle=" << angleDom[idom]);
11391 map<double, int> sortedDom; // sort domains by angle
11392 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11393 sortedDom[ia->second] = ia->first;
11394 vector<int> vnodes;
11396 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11398 vdom.push_back(ib->second);
11399 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11401 for (int ino = 0; ino < nbNodes; ino++)
11402 vnodes.push_back(nodes[ino]);
11403 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11412 // --- iterate on shared faces (volumes to modify, face to extrude)
11413 // get node id's of the face (id SMDS = id VTK)
11414 // create flat element with old and new nodes if requested
11416 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11417 // (domain1 X domain2) = domain1 + MAXINT*domain2
11419 std::map<int, std::map<long,int> > nodeQuadDomains;
11420 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11422 //MESSAGE(".. Creation of elements: simple junction");
11423 if (createJointElems)
11425 string joints2DName = "joints2D";
11426 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11427 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11428 string joints3DName = "joints3D";
11429 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11430 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11432 itface = faceDomains.begin();
11433 for (; itface != faceDomains.end(); ++itface)
11435 DownIdType face = itface->first;
11436 std::set<int> oldNodes;
11437 std::set<int>::iterator itn;
11439 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11441 std::map<int, int> domvol = itface->second;
11442 std::map<int, int>::iterator itdom = domvol.begin();
11443 int dom1 = itdom->first;
11444 int vtkVolId = itdom->second;
11446 int dom2 = itdom->first;
11447 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11449 stringstream grpname;
11452 grpname << dom1 << "_" << dom2;
11454 grpname << dom2 << "_" << dom1;
11455 string namegrp = grpname.str();
11456 if (!mapOfJunctionGroups.count(namegrp))
11457 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11458 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11460 sgrp->Add(vol->GetID());
11461 if (vol->GetType() == SMDSAbs_Volume)
11462 joints3DGrp->Add(vol->GetID());
11463 else if (vol->GetType() == SMDSAbs_Face)
11464 joints2DGrp->Add(vol->GetID());
11468 // --- create volumes on multiple domain intersection if requested
11469 // iterate on mutipleNodesToFace
11470 // iterate on edgesMultiDomains
11472 //MESSAGE(".. Creation of elements: multiple junction");
11473 if (createJointElems)
11475 // --- iterate on mutipleNodesToFace
11477 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11478 for (; itn != mutipleNodesToFace.end(); ++itn)
11480 int node = itn->first;
11481 vector<int> orderDom = itn->second;
11482 vector<vtkIdType> orderedNodes;
11483 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11484 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11485 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11487 stringstream grpname;
11489 grpname << 0 << "_" << 0;
11490 string namegrp = grpname.str();
11491 if (!mapOfJunctionGroups.count(namegrp))
11492 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11493 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11495 sgrp->Add(face->GetID());
11498 // --- iterate on edgesMultiDomains
11500 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11501 for (; ite != edgesMultiDomains.end(); ++ite)
11503 vector<int> nodes = ite->first;
11504 vector<int> orderDom = ite->second;
11505 vector<vtkIdType> orderedNodes;
11506 if (nodes.size() == 2)
11508 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11509 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11510 if ( orderDom.size() == 3 )
11511 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11512 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11514 for (int idom = orderDom.size()-1; idom >=0; idom--)
11515 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11516 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11518 string namegrp = "jointsMultiples";
11519 if (!mapOfJunctionGroups.count(namegrp))
11520 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11521 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11523 sgrp->Add(vol->GetID());
11527 //INFOS("Quadratic multiple joints not implemented");
11528 // TODO quadratic nodes
11533 // --- list the explicit faces and edges of the mesh that need to be modified,
11534 // i.e. faces and edges built with one or more duplicated nodes.
11535 // associate these faces or edges to their corresponding domain.
11536 // only the first domain found is kept when a face or edge is shared
11538 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11539 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11540 faceOrEdgeDom.clear();
11543 //MESSAGE(".. Modification of elements");
11544 for (int idomain = idom0; idomain < nbDomains; idomain++)
11546 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11547 for (; itnod != nodeDomains.end(); ++itnod)
11549 int oldId = itnod->first;
11550 //MESSAGE(" node " << oldId);
11551 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11552 for (int i = 0; i < l.ncells; i++)
11554 int vtkId = l.cells[i];
11555 int vtkType = grid->GetCellType(vtkId);
11556 int downId = grid->CellIdToDownId(vtkId);
11558 continue; // new cells: not to be modified
11559 DownIdType aCell(downId, vtkType);
11560 int volParents[1000];
11561 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11562 for (int j = 0; j < nbvol; j++)
11563 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11564 if (!feDom.count(vtkId))
11566 feDom[vtkId] = idomain;
11567 faceOrEdgeDom[aCell] = emptyMap;
11568 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11569 //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11570 // << " type " << vtkType << " downId " << downId);
11576 // --- iterate on shared faces (volumes to modify, face to extrude)
11577 // get node id's of the face
11578 // replace old nodes by new nodes in volumes, and update inverse connectivity
11580 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11581 for (int m=0; m<3; m++)
11583 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11584 itface = (*amap).begin();
11585 for (; itface != (*amap).end(); ++itface)
11587 DownIdType face = itface->first;
11588 std::set<int> oldNodes;
11589 std::set<int>::iterator itn;
11591 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11592 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11593 std::map<int, int> localClonedNodeIds;
11595 std::map<int, int> domvol = itface->second;
11596 std::map<int, int>::iterator itdom = domvol.begin();
11597 for (; itdom != domvol.end(); ++itdom)
11599 int idom = itdom->first;
11600 int vtkVolId = itdom->second;
11601 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11602 localClonedNodeIds.clear();
11603 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11606 if (nodeDomains[oldId].count(idom))
11608 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11609 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11612 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11617 // Remove empty groups (issue 0022812)
11618 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11619 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11621 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11622 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11625 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11626 grid->DeleteLinks();
11634 * \brief Double nodes on some external faces and create flat elements.
11635 * Flat elements are mainly used by some types of mechanic calculations.
11637 * Each group of the list must be constituted of faces.
11638 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11639 * @param theElems - list of groups of faces, where a group of faces is a set of
11640 * SMDS_MeshElements sorted by Id.
11641 * @return TRUE if operation has been completed successfully, FALSE otherwise
11643 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11645 // MESSAGE("-------------------------------------------------");
11646 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11647 // MESSAGE("-------------------------------------------------");
11649 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11651 // --- For each group of faces
11652 // duplicate the nodes, create a flat element based on the face
11653 // replace the nodes of the faces by their clones
11655 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11656 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11657 clonedNodes.clear();
11658 intermediateNodes.clear();
11659 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11660 mapOfJunctionGroups.clear();
11662 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11664 const TIDSortedElemSet& domain = theElems[idom];
11665 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11666 for ( ; elemItr != domain.end(); ++elemItr )
11668 const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11671 // MESSAGE("aFace=" << aFace->GetID());
11672 bool isQuad = aFace->IsQuadratic();
11673 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11675 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11677 SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11678 while (nodeIt->more())
11680 const SMDS_MeshNode* node = nodeIt->next();
11681 bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11683 ln2.push_back(node);
11685 ln0.push_back(node);
11687 const SMDS_MeshNode* clone = 0;
11688 if (!clonedNodes.count(node))
11690 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11691 copyPosition( node, clone );
11692 clonedNodes[node] = clone;
11695 clone = clonedNodes[node];
11698 ln3.push_back(clone);
11700 ln1.push_back(clone);
11702 const SMDS_MeshNode* inter = 0;
11703 if (isQuad && (!isMedium))
11705 if (!intermediateNodes.count(node))
11707 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11708 copyPosition( node, inter );
11709 intermediateNodes[node] = inter;
11712 inter = intermediateNodes[node];
11713 ln4.push_back(inter);
11717 // --- extrude the face
11719 vector<const SMDS_MeshNode*> ln;
11720 SMDS_MeshVolume* vol = 0;
11721 vtkIdType aType = aFace->GetVtkType();
11725 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11726 // MESSAGE("vol prism " << vol->GetID());
11727 ln.push_back(ln1[0]);
11728 ln.push_back(ln1[1]);
11729 ln.push_back(ln1[2]);
11732 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11733 // MESSAGE("vol hexa " << vol->GetID());
11734 ln.push_back(ln1[0]);
11735 ln.push_back(ln1[1]);
11736 ln.push_back(ln1[2]);
11737 ln.push_back(ln1[3]);
11739 case VTK_QUADRATIC_TRIANGLE:
11740 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11741 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11742 // MESSAGE("vol quad prism " << vol->GetID());
11743 ln.push_back(ln1[0]);
11744 ln.push_back(ln1[1]);
11745 ln.push_back(ln1[2]);
11746 ln.push_back(ln3[0]);
11747 ln.push_back(ln3[1]);
11748 ln.push_back(ln3[2]);
11750 case VTK_QUADRATIC_QUAD:
11751 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11752 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11753 // ln4[0], ln4[1], ln4[2], ln4[3]);
11754 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11755 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11756 ln4[0], ln4[1], ln4[2], ln4[3]);
11757 // MESSAGE("vol quad hexa " << vol->GetID());
11758 ln.push_back(ln1[0]);
11759 ln.push_back(ln1[1]);
11760 ln.push_back(ln1[2]);
11761 ln.push_back(ln1[3]);
11762 ln.push_back(ln3[0]);
11763 ln.push_back(ln3[1]);
11764 ln.push_back(ln3[2]);
11765 ln.push_back(ln3[3]);
11775 stringstream grpname;
11778 string namegrp = grpname.str();
11779 if (!mapOfJunctionGroups.count(namegrp))
11780 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11781 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11783 sgrp->Add(vol->GetID());
11786 // --- modify the face
11788 const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11795 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11796 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11797 * groups of faces to remove inside the object, (idem edges).
11798 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11800 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11801 const TopoDS_Shape& theShape,
11802 SMESH_NodeSearcher* theNodeSearcher,
11803 const char* groupName,
11804 std::vector<double>& nodesCoords,
11805 std::vector<std::vector<int> >& listOfListOfNodes)
11807 // MESSAGE("--------------------------------");
11808 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11809 // MESSAGE("--------------------------------");
11811 // --- zone of volumes to remove is given :
11812 // 1 either by a geom shape (one or more vertices) and a radius,
11813 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11814 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11815 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11816 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11817 // defined by it's name.
11819 SMESHDS_GroupBase* groupDS = 0;
11820 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11821 while ( groupIt->more() )
11824 SMESH_Group * group = groupIt->next();
11825 if ( !group ) continue;
11826 groupDS = group->GetGroupDS();
11827 if ( !groupDS || groupDS->IsEmpty() ) continue;
11828 std::string grpName = group->GetName();
11829 //MESSAGE("grpName=" << grpName);
11830 if (grpName == groupName)
11836 bool isNodeGroup = false;
11837 bool isNodeCoords = false;
11840 if (groupDS->GetType() != SMDSAbs_Node)
11842 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11845 if (nodesCoords.size() > 0)
11846 isNodeCoords = true; // a list o nodes given by their coordinates
11847 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11849 // --- define groups to build
11851 // --- group of SMDS volumes
11852 string grpvName = groupName;
11853 grpvName += "_vol";
11854 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11857 MESSAGE("group not created " << grpvName);
11860 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11862 // --- group of SMDS faces on the skin
11863 string grpsName = groupName;
11864 grpsName += "_skin";
11865 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11868 MESSAGE("group not created " << grpsName);
11871 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11873 // --- group of SMDS faces internal (several shapes)
11874 string grpiName = groupName;
11875 grpiName += "_internalFaces";
11876 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11879 MESSAGE("group not created " << grpiName);
11882 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11884 // --- group of SMDS faces internal (several shapes)
11885 string grpeiName = groupName;
11886 grpeiName += "_internalEdges";
11887 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11890 MESSAGE("group not created " << grpeiName);
11893 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11895 // --- build downward connectivity
11897 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11898 meshDS->BuildDownWardConnectivity(true);
11899 SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11901 // --- set of volumes detected inside
11903 std::set<int> setOfInsideVol;
11904 std::set<int> setOfVolToCheck;
11906 std::vector<gp_Pnt> gpnts;
11909 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11911 //MESSAGE("group of nodes provided");
11912 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11913 while ( elemIt->more() )
11915 const SMDS_MeshElement* elem = elemIt->next();
11918 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11921 SMDS_MeshElement* vol = 0;
11922 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11923 while (volItr->more())
11925 vol = (SMDS_MeshElement*)volItr->next();
11926 setOfInsideVol.insert(vol->GetVtkID());
11927 sgrp->Add(vol->GetID());
11931 else if (isNodeCoords)
11933 //MESSAGE("list of nodes coordinates provided");
11936 while ( i < nodesCoords.size()-2 )
11938 double x = nodesCoords[i++];
11939 double y = nodesCoords[i++];
11940 double z = nodesCoords[i++];
11941 gp_Pnt p = gp_Pnt(x, y ,z);
11942 gpnts.push_back(p);
11943 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11947 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11949 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11950 TopTools_IndexedMapOfShape vertexMap;
11951 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11952 gp_Pnt p = gp_Pnt(0,0,0);
11953 if (vertexMap.Extent() < 1)
11956 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11958 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11959 p = BRep_Tool::Pnt(vertex);
11960 gpnts.push_back(p);
11961 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11965 if (gpnts.size() > 0)
11967 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11968 //MESSAGE("startNode->nodeId " << nodeId);
11970 double radius2 = radius*radius;
11971 //MESSAGE("radius2 " << radius2);
11973 // --- volumes on start node
11975 setOfVolToCheck.clear();
11976 SMDS_MeshElement* startVol = 0;
11977 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11978 while (volItr->more())
11980 startVol = (SMDS_MeshElement*)volItr->next();
11981 setOfVolToCheck.insert(startVol->GetVtkID());
11983 if (setOfVolToCheck.empty())
11985 MESSAGE("No volumes found");
11989 // --- starting with central volumes then their neighbors, check if they are inside
11990 // or outside the domain, until no more new neighbor volume is inside.
11991 // Fill the group of inside volumes
11993 std::map<int, double> mapOfNodeDistance2;
11994 mapOfNodeDistance2.clear();
11995 std::set<int> setOfOutsideVol;
11996 while (!setOfVolToCheck.empty())
11998 std::set<int>::iterator it = setOfVolToCheck.begin();
12000 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12001 bool volInside = false;
12002 vtkIdType npts = 0;
12003 vtkIdType* pts = 0;
12004 grid->GetCellPoints(vtkId, npts, pts);
12005 for (int i=0; i<npts; i++)
12007 double distance2 = 0;
12008 if (mapOfNodeDistance2.count(pts[i]))
12010 distance2 = mapOfNodeDistance2[pts[i]];
12011 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12015 double *coords = grid->GetPoint(pts[i]);
12016 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12018 for ( size_t j = 0; j < gpnts.size(); j++ )
12020 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12021 if (d2 < distance2)
12024 if (distance2 < radius2)
12028 mapOfNodeDistance2[pts[i]] = distance2;
12029 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12031 if (distance2 < radius2)
12033 volInside = true; // one or more nodes inside the domain
12034 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12040 setOfInsideVol.insert(vtkId);
12041 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12042 int neighborsVtkIds[NBMAXNEIGHBORS];
12043 int downIds[NBMAXNEIGHBORS];
12044 unsigned char downTypes[NBMAXNEIGHBORS];
12045 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12046 for (int n = 0; n < nbNeighbors; n++)
12047 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12048 setOfVolToCheck.insert(neighborsVtkIds[n]);
12052 setOfOutsideVol.insert(vtkId);
12053 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12055 setOfVolToCheck.erase(vtkId);
12059 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12060 // If yes, add the volume to the inside set
12062 bool addedInside = true;
12063 std::set<int> setOfVolToReCheck;
12064 while (addedInside)
12066 //MESSAGE(" --------------------------- re check");
12067 addedInside = false;
12068 std::set<int>::iterator itv = setOfInsideVol.begin();
12069 for (; itv != setOfInsideVol.end(); ++itv)
12072 int neighborsVtkIds[NBMAXNEIGHBORS];
12073 int downIds[NBMAXNEIGHBORS];
12074 unsigned char downTypes[NBMAXNEIGHBORS];
12075 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12076 for (int n = 0; n < nbNeighbors; n++)
12077 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12078 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12080 setOfVolToCheck = setOfVolToReCheck;
12081 setOfVolToReCheck.clear();
12082 while (!setOfVolToCheck.empty())
12084 std::set<int>::iterator it = setOfVolToCheck.begin();
12086 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12088 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12089 int countInside = 0;
12090 int neighborsVtkIds[NBMAXNEIGHBORS];
12091 int downIds[NBMAXNEIGHBORS];
12092 unsigned char downTypes[NBMAXNEIGHBORS];
12093 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12094 for (int n = 0; n < nbNeighbors; n++)
12095 if (setOfInsideVol.count(neighborsVtkIds[n]))
12097 //MESSAGE("countInside " << countInside);
12098 if (countInside > 1)
12100 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12101 setOfInsideVol.insert(vtkId);
12102 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12103 addedInside = true;
12106 setOfVolToReCheck.insert(vtkId);
12108 setOfVolToCheck.erase(vtkId);
12112 // --- map of Downward faces at the boundary, inside the global volume
12113 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12114 // fill group of SMDS faces inside the volume (when several volume shapes)
12115 // fill group of SMDS faces on the skin of the global volume (if skin)
12117 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12118 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12119 std::set<int>::iterator it = setOfInsideVol.begin();
12120 for (; it != setOfInsideVol.end(); ++it)
12123 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12124 int neighborsVtkIds[NBMAXNEIGHBORS];
12125 int downIds[NBMAXNEIGHBORS];
12126 unsigned char downTypes[NBMAXNEIGHBORS];
12127 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12128 for (int n = 0; n < nbNeighbors; n++)
12130 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12131 if (neighborDim == 3)
12133 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12135 DownIdType face(downIds[n], downTypes[n]);
12136 boundaryFaces[face] = vtkId;
12138 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12139 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12140 if (vtkFaceId >= 0)
12142 sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12143 // find also the smds edges on this face
12144 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12145 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12146 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12147 for (int i = 0; i < nbEdges; i++)
12149 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12150 if (vtkEdgeId >= 0)
12151 sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12155 else if (neighborDim == 2) // skin of the volume
12157 DownIdType face(downIds[n], downTypes[n]);
12158 skinFaces[face] = vtkId;
12159 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12160 if (vtkFaceId >= 0)
12161 sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12166 // --- identify the edges constituting the wire of each subshape on the skin
12167 // define polylines with the nodes of edges, equivalent to wires
12168 // project polylines on subshapes, and partition, to get geom faces
12170 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12171 std::set<int> emptySet;
12173 std::set<int> shapeIds;
12175 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12176 while (itelem->more())
12178 const SMDS_MeshElement *elem = itelem->next();
12179 int shapeId = elem->getshapeId();
12180 int vtkId = elem->GetVtkID();
12181 if (!shapeIdToVtkIdSet.count(shapeId))
12183 shapeIdToVtkIdSet[shapeId] = emptySet;
12184 shapeIds.insert(shapeId);
12186 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12189 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12190 std::set<DownIdType, DownIdCompare> emptyEdges;
12191 emptyEdges.clear();
12193 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12194 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12196 int shapeId = itShape->first;
12197 //MESSAGE(" --- Shape ID --- "<< shapeId);
12198 shapeIdToEdges[shapeId] = emptyEdges;
12200 std::vector<int> nodesEdges;
12202 std::set<int>::iterator its = itShape->second.begin();
12203 for (; its != itShape->second.end(); ++its)
12206 //MESSAGE(" " << vtkId);
12207 int neighborsVtkIds[NBMAXNEIGHBORS];
12208 int downIds[NBMAXNEIGHBORS];
12209 unsigned char downTypes[NBMAXNEIGHBORS];
12210 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12211 for (int n = 0; n < nbNeighbors; n++)
12213 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12215 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12216 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12217 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12219 DownIdType edge(downIds[n], downTypes[n]);
12220 if (!shapeIdToEdges[shapeId].count(edge))
12222 shapeIdToEdges[shapeId].insert(edge);
12224 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12225 nodesEdges.push_back(vtkNodeId[0]);
12226 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12227 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12233 std::list<int> order;
12235 if (nodesEdges.size() > 0)
12237 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12238 nodesEdges[0] = -1;
12239 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12240 nodesEdges[1] = -1; // do not reuse this edge
12244 int nodeTofind = order.back(); // try first to push back
12246 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12247 if (nodesEdges[i] == nodeTofind)
12249 if ( i == (int) nodesEdges.size() )
12250 found = false; // no follower found on back
12253 if (i%2) // odd ==> use the previous one
12254 if (nodesEdges[i-1] < 0)
12258 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12259 nodesEdges[i-1] = -1;
12261 else // even ==> use the next one
12262 if (nodesEdges[i+1] < 0)
12266 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12267 nodesEdges[i+1] = -1;
12272 // try to push front
12274 nodeTofind = order.front(); // try to push front
12275 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12276 if ( nodesEdges[i] == nodeTofind )
12278 if ( i == (int)nodesEdges.size() )
12280 found = false; // no predecessor found on front
12283 if (i%2) // odd ==> use the previous one
12284 if (nodesEdges[i-1] < 0)
12288 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12289 nodesEdges[i-1] = -1;
12291 else // even ==> use the next one
12292 if (nodesEdges[i+1] < 0)
12296 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12297 nodesEdges[i+1] = -1;
12303 std::vector<int> nodes;
12304 nodes.push_back(shapeId);
12305 std::list<int>::iterator itl = order.begin();
12306 for (; itl != order.end(); itl++)
12308 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12309 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12311 listOfListOfNodes.push_back(nodes);
12314 // partition geom faces with blocFissure
12315 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12316 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12322 //================================================================================
12324 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12325 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12326 * \return TRUE if operation has been completed successfully, FALSE otherwise
12328 //================================================================================
12330 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12332 // iterates on volume elements and detect all free faces on them
12333 SMESHDS_Mesh* aMesh = GetMeshDS();
12337 ElemFeatures faceType( SMDSAbs_Face );
12338 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12339 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12342 const SMDS_MeshVolume* volume = vIt->next();
12343 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12344 vTool.SetExternalNormal();
12345 const int iQuad = volume->IsQuadratic();
12346 faceType.SetQuad( iQuad );
12347 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12349 if (!vTool.IsFreeFace(iface))
12352 vector<const SMDS_MeshNode *> nodes;
12353 int nbFaceNodes = vTool.NbFaceNodes(iface);
12354 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12356 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12357 nodes.push_back(faceNodes[inode]);
12359 if (iQuad) // add medium nodes
12361 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12362 nodes.push_back(faceNodes[inode]);
12363 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12364 nodes.push_back(faceNodes[8]);
12366 // add new face based on volume nodes
12367 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12369 nbExisted++; // face already exists
12373 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12378 return ( nbFree == ( nbExisted + nbCreated ));
12383 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12385 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12387 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12390 //================================================================================
12392 * \brief Creates missing boundary elements
12393 * \param elements - elements whose boundary is to be checked
12394 * \param dimension - defines type of boundary elements to create
12395 * \param group - a group to store created boundary elements in
12396 * \param targetMesh - a mesh to store created boundary elements in
12397 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12398 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12399 * boundary elements will be copied into the targetMesh
12400 * \param toAddExistingBondary - if true, not only new but also pre-existing
12401 * boundary elements will be added into the new group
12402 * \param aroundElements - if true, elements will be created on boundary of given
12403 * elements else, on boundary of the whole mesh.
12404 * \return nb of added boundary elements
12406 //================================================================================
12408 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12409 Bnd_Dimension dimension,
12410 SMESH_Group* group/*=0*/,
12411 SMESH_Mesh* targetMesh/*=0*/,
12412 bool toCopyElements/*=false*/,
12413 bool toCopyExistingBoundary/*=false*/,
12414 bool toAddExistingBondary/*= false*/,
12415 bool aroundElements/*= false*/)
12417 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12418 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12419 // hope that all elements are of the same type, do not check them all
12420 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12421 throw SALOME_Exception(LOCALIZED("wrong element type"));
12424 toCopyElements = toCopyExistingBoundary = false;
12426 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12427 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12428 int nbAddedBnd = 0;
12430 // editor adding present bnd elements and optionally holding elements to add to the group
12431 SMESH_MeshEditor* presentEditor;
12432 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12433 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12435 SMESH_MesherHelper helper( *myMesh );
12436 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12437 SMDS_VolumeTool vTool;
12438 TIDSortedElemSet avoidSet;
12439 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12442 typedef vector<const SMDS_MeshNode*> TConnectivity;
12443 TConnectivity tgtNodes;
12444 ElemFeatures elemKind( missType ), elemToCopy;
12446 vector<const SMDS_MeshElement*> presentBndElems;
12447 vector<TConnectivity> missingBndElems;
12448 vector<int> freeFacets;
12449 TConnectivity nodes, elemNodes;
12451 SMDS_ElemIteratorPtr eIt;
12452 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12453 else eIt = SMESHUtils::elemSetIterator( elements );
12455 while ( eIt->more() )
12457 const SMDS_MeshElement* elem = eIt->next();
12458 const int iQuad = elem->IsQuadratic();
12459 elemKind.SetQuad( iQuad );
12461 // ------------------------------------------------------------------------------------
12462 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12463 // ------------------------------------------------------------------------------------
12464 presentBndElems.clear();
12465 missingBndElems.clear();
12466 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12467 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12469 const SMDS_MeshElement* otherVol = 0;
12470 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12472 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12473 ( !aroundElements || elements.count( otherVol )))
12475 freeFacets.push_back( iface );
12477 if ( missType == SMDSAbs_Face )
12478 vTool.SetExternalNormal();
12479 for ( size_t i = 0; i < freeFacets.size(); ++i )
12481 int iface = freeFacets[i];
12482 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12483 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12484 if ( missType == SMDSAbs_Edge ) // boundary edges
12486 nodes.resize( 2+iQuad );
12487 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12489 for ( size_t j = 0; j < nodes.size(); ++j )
12490 nodes[ j ] = nn[ i+j ];
12491 if ( const SMDS_MeshElement* edge =
12492 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12493 presentBndElems.push_back( edge );
12495 missingBndElems.push_back( nodes );
12498 else // boundary face
12501 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12502 nodes.push_back( nn[inode] ); // add corner nodes
12504 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12505 nodes.push_back( nn[inode] ); // add medium nodes
12506 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12508 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12510 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12511 SMDSAbs_Face, /*noMedium=*/false ))
12512 presentBndElems.push_back( f );
12514 missingBndElems.push_back( nodes );
12516 if ( targetMesh != myMesh )
12518 // add 1D elements on face boundary to be added to a new mesh
12519 const SMDS_MeshElement* edge;
12520 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12523 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12525 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12526 if ( edge && avoidSet.insert( edge ).second )
12527 presentBndElems.push_back( edge );
12533 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12535 avoidSet.clear(), avoidSet.insert( elem );
12536 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12537 SMDS_MeshElement::iterator() );
12538 elemNodes.push_back( elemNodes[0] );
12539 nodes.resize( 2 + iQuad );
12540 const int nbLinks = elem->NbCornerNodes();
12541 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12543 nodes[0] = elemNodes[iN];
12544 nodes[1] = elemNodes[iN+1+iQuad];
12545 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12546 continue; // not free link
12548 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12549 if ( const SMDS_MeshElement* edge =
12550 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12551 presentBndElems.push_back( edge );
12553 missingBndElems.push_back( nodes );
12557 // ---------------------------------
12558 // 2. Add missing boundary elements
12559 // ---------------------------------
12560 if ( targetMesh != myMesh )
12561 // instead of making a map of nodes in this mesh and targetMesh,
12562 // we create nodes with same IDs.
12563 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12565 TConnectivity& srcNodes = missingBndElems[i];
12566 tgtNodes.resize( srcNodes.size() );
12567 for ( inode = 0; inode < srcNodes.size(); ++inode )
12568 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12569 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12571 /*noMedium=*/false))
12573 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12577 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12579 TConnectivity& nodes = missingBndElems[ i ];
12580 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12582 /*noMedium=*/false))
12584 SMDS_MeshElement* newElem =
12585 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12586 nbAddedBnd += bool( newElem );
12588 // try to set a new element to a shape
12589 if ( myMesh->HasShapeToMesh() )
12592 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12593 const size_t nbN = nodes.size() / (iQuad+1 );
12594 for ( inode = 0; inode < nbN && ok; ++inode )
12596 pair<int, TopAbs_ShapeEnum> i_stype =
12597 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12598 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12599 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12601 if ( ok && mediumShapes.size() > 1 )
12603 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12604 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12605 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12607 if (( ok = ( stype_i->first != stype_i_0.first )))
12608 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12609 aMesh->IndexToShape( stype_i_0.second ));
12612 if ( ok && mediumShapes.begin()->first == missShapeType )
12613 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12617 // ----------------------------------
12618 // 3. Copy present boundary elements
12619 // ----------------------------------
12620 if ( toCopyExistingBoundary )
12621 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12623 const SMDS_MeshElement* e = presentBndElems[i];
12624 tgtNodes.resize( e->NbNodes() );
12625 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12626 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12627 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12629 else // store present elements to add them to a group
12630 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12632 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12635 } // loop on given elements
12637 // ---------------------------------------------
12638 // 4. Fill group with boundary elements
12639 // ---------------------------------------------
12642 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12643 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12644 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12646 tgtEditor.myLastCreatedElems.clear();
12647 tgtEditor2.myLastCreatedElems.clear();
12649 // -----------------------
12650 // 5. Copy given elements
12651 // -----------------------
12652 if ( toCopyElements && targetMesh != myMesh )
12654 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12655 else eIt = SMESHUtils::elemSetIterator( elements );
12656 while (eIt->more())
12658 const SMDS_MeshElement* elem = eIt->next();
12659 tgtNodes.resize( elem->NbNodes() );
12660 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12661 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12662 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12664 tgtEditor.myLastCreatedElems.clear();
12670 //================================================================================
12672 * \brief Copy node position and set \a to node on the same geometry
12674 //================================================================================
12676 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12677 const SMDS_MeshNode* to )
12679 if ( !from || !to ) return;
12681 SMDS_PositionPtr pos = from->GetPosition();
12682 if ( !pos || from->getshapeId() < 1 ) return;
12684 switch ( pos->GetTypeOfPosition() )
12686 case SMDS_TOP_3DSPACE: break;
12688 case SMDS_TOP_FACE:
12690 SMDS_FacePositionPtr fPos = pos;
12691 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12692 fPos->GetUParameter(), fPos->GetVParameter() );
12695 case SMDS_TOP_EDGE:
12697 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12698 SMDS_EdgePositionPtr ePos = pos;
12699 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12702 case SMDS_TOP_VERTEX:
12704 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12707 case SMDS_TOP_UNSPEC: