1 // Copyright (C) 2007-2019 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
49 #include "utilities.h"
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_ListOfInteger.hxx>
66 #include <TopAbs_State.hxx>
68 #include <TopExp_Explorer.hxx>
69 #include <TopTools_ListIteratorOfListOfShape.hxx>
70 #include <TopTools_ListOfShape.hxx>
71 #include <TopTools_SequenceOfShape.hxx>
73 #include <TopoDS_Edge.hxx>
74 #include <TopoDS_Face.hxx>
75 #include <TopoDS_Solid.hxx>
81 #include <gp_Trsf.hxx>
95 #include <boost/tuple/tuple.hpp>
96 #include <boost/container/flat_set.hpp>
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
101 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
103 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
106 using namespace SMESH::Controls;
108 //=======================================================================
109 //function : SMESH_MeshEditor
111 //=======================================================================
113 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
114 :myMesh( theMesh ) // theMesh may be NULL
118 //================================================================================
120 * \brief Return mesh DS
122 //================================================================================
124 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
126 return myMesh->GetMeshDS();
130 //================================================================================
132 * \brief Clears myLastCreatedNodes and myLastCreatedElems
134 //================================================================================
136 void SMESH_MeshEditor::ClearLastCreated()
138 SMESHUtils::FreeVector( myLastCreatedElems );
139 SMESHUtils::FreeVector( myLastCreatedNodes );
142 //================================================================================
144 * \brief Initializes members by an existing element
145 * \param [in] elem - the source element
146 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
148 //================================================================================
150 SMESH_MeshEditor::ElemFeatures&
151 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
155 myType = elem->GetType();
156 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
158 myIsPoly = elem->IsPoly();
161 myIsQuad = elem->IsQuadratic();
162 if ( myType == SMDSAbs_Volume && !basicOnly )
164 vector<int> quant = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
165 myPolyhedQuantities.swap( quant );
169 else if ( myType == SMDSAbs_Ball && !basicOnly )
171 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
177 //=======================================================================
181 //=======================================================================
184 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
185 const ElemFeatures& features)
187 SMDS_MeshElement* e = 0;
188 int nbnode = node.size();
189 SMESHDS_Mesh* mesh = GetMeshDS();
190 const int ID = features.myID;
192 switch ( features.myType ) {
194 if ( !features.myIsPoly ) {
196 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
197 else e = mesh->AddFace (node[0], node[1], node[2] );
199 else if (nbnode == 4) {
200 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
201 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
203 else if (nbnode == 6) {
204 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
205 node[4], node[5], ID);
206 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
209 else if (nbnode == 7) {
210 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
211 node[4], node[5], node[6], ID);
212 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
213 node[4], node[5], node[6] );
215 else if (nbnode == 8) {
216 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
217 node[4], node[5], node[6], node[7], ID);
218 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
219 node[4], node[5], node[6], node[7] );
221 else if (nbnode == 9) {
222 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
223 node[4], node[5], node[6], node[7], node[8], ID);
224 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
225 node[4], node[5], node[6], node[7], node[8] );
228 else if ( !features.myIsQuad )
230 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
231 else e = mesh->AddPolygonalFace (node );
233 else if ( nbnode % 2 == 0 ) // just a protection
235 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
236 else e = mesh->AddQuadPolygonalFace (node );
241 if ( !features.myIsPoly ) {
243 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
244 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
246 else if (nbnode == 5) {
247 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
249 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
252 else if (nbnode == 6) {
253 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
254 node[4], node[5], ID);
255 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
258 else if (nbnode == 8) {
259 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
260 node[4], node[5], node[6], node[7], ID);
261 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
262 node[4], node[5], node[6], node[7] );
264 else if (nbnode == 10) {
265 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
266 node[4], node[5], node[6], node[7],
267 node[8], node[9], ID);
268 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
269 node[4], node[5], node[6], node[7],
272 else if (nbnode == 12) {
273 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
274 node[4], node[5], node[6], node[7],
275 node[8], node[9], node[10], node[11], ID);
276 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
277 node[4], node[5], node[6], node[7],
278 node[8], node[9], node[10], node[11] );
280 else if (nbnode == 13) {
281 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
282 node[4], node[5], node[6], node[7],
283 node[8], node[9], node[10],node[11],
285 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
286 node[4], node[5], node[6], node[7],
287 node[8], node[9], node[10],node[11],
290 else if (nbnode == 15) {
291 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
292 node[4], node[5], node[6], node[7],
293 node[8], node[9], node[10],node[11],
294 node[12],node[13],node[14],ID);
295 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
296 node[4], node[5], node[6], node[7],
297 node[8], node[9], node[10],node[11],
298 node[12],node[13],node[14] );
300 else if (nbnode == 20) {
301 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
302 node[4], node[5], node[6], node[7],
303 node[8], node[9], node[10],node[11],
304 node[12],node[13],node[14],node[15],
305 node[16],node[17],node[18],node[19],ID);
306 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
307 node[4], node[5], node[6], node[7],
308 node[8], node[9], node[10],node[11],
309 node[12],node[13],node[14],node[15],
310 node[16],node[17],node[18],node[19] );
312 else if (nbnode == 27) {
313 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
314 node[4], node[5], node[6], node[7],
315 node[8], node[9], node[10],node[11],
316 node[12],node[13],node[14],node[15],
317 node[16],node[17],node[18],node[19],
318 node[20],node[21],node[22],node[23],
319 node[24],node[25],node[26], ID);
320 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
321 node[4], node[5], node[6], node[7],
322 node[8], node[9], node[10],node[11],
323 node[12],node[13],node[14],node[15],
324 node[16],node[17],node[18],node[19],
325 node[20],node[21],node[22],node[23],
326 node[24],node[25],node[26] );
329 else if ( !features.myIsQuad )
331 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
332 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
336 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
337 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
343 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
344 else e = mesh->AddEdge (node[0], node[1] );
346 else if ( nbnode == 3 ) {
347 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
348 else e = mesh->AddEdge (node[0], node[1], node[2] );
352 case SMDSAbs_0DElement:
354 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
355 else e = mesh->Add0DElement (node[0] );
360 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
361 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
365 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
366 else e = mesh->AddBall (node[0], features.myBallDiameter );
371 if ( e ) myLastCreatedElems.push_back( e );
375 //=======================================================================
379 //=======================================================================
381 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
382 const ElemFeatures& features)
384 vector<const SMDS_MeshNode*> nodes;
385 nodes.reserve( nodeIDs.size() );
386 vector<int>::const_iterator id = nodeIDs.begin();
387 while ( id != nodeIDs.end() ) {
388 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
389 nodes.push_back( node );
393 return AddElement( nodes, features );
396 //=======================================================================
398 //purpose : Remove a node or an element.
399 // Modify a compute state of sub-meshes which become empty
400 //=======================================================================
402 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
407 SMESHDS_Mesh* aMesh = GetMeshDS();
408 set< SMESH_subMesh *> smmap;
411 list<int>::const_iterator it = theIDs.begin();
412 for ( ; it != theIDs.end(); it++ ) {
413 const SMDS_MeshElement * elem;
415 elem = aMesh->FindNode( *it );
417 elem = aMesh->FindElement( *it );
421 // Notify VERTEX sub-meshes about modification
423 const SMDS_MeshNode* node = cast2Node( elem );
424 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
425 if ( int aShapeID = node->getshapeId() )
426 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
429 // Find sub-meshes to notify about modification
430 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
431 // while ( nodeIt->more() ) {
432 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
433 // const SMDS_PositionPtr& aPosition = node->GetPosition();
434 // if ( aPosition.get() ) {
435 // if ( int aShapeID = aPosition->GetShapeId() ) {
436 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
437 // smmap.insert( sm );
444 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
446 aMesh->RemoveElement( elem );
450 // Notify sub-meshes about modification
451 if ( !smmap.empty() ) {
452 set< SMESH_subMesh *>::iterator smIt;
453 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
454 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
457 // // Check if the whole mesh becomes empty
458 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
459 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
464 //================================================================================
466 * \brief Create 0D elements on all nodes of the given object.
467 * \param elements - Elements on whose nodes to create 0D elements; if empty,
468 * the all mesh is treated
469 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
470 * \param duplicateElements - to add one more 0D element to a node or not
472 //================================================================================
474 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
475 TIDSortedElemSet& all0DElems,
476 const bool duplicateElements )
478 SMDS_ElemIteratorPtr elemIt;
479 if ( elements.empty() )
481 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
485 elemIt = SMESHUtils::elemSetIterator( elements );
488 while ( elemIt->more() )
490 const SMDS_MeshElement* e = elemIt->next();
491 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
492 while ( nodeIt->more() )
494 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
495 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
496 if ( duplicateElements || !it0D->more() )
498 myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
499 all0DElems.insert( myLastCreatedElems.back() );
501 while ( it0D->more() )
502 all0DElems.insert( it0D->next() );
507 //=======================================================================
508 //function : FindShape
509 //purpose : Return an index of the shape theElem is on
510 // or zero if a shape not found
511 //=======================================================================
513 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
517 SMESHDS_Mesh * aMesh = GetMeshDS();
518 if ( aMesh->ShapeToMesh().IsNull() )
521 int aShapeID = theElem->getshapeId();
525 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
526 if ( sm->Contains( theElem ))
529 if ( theElem->GetType() == SMDSAbs_Node ) {
530 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
533 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
536 TopoDS_Shape aShape; // the shape a node of theElem is on
537 if ( theElem->GetType() != SMDSAbs_Node )
539 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
540 while ( nodeIt->more() ) {
541 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
542 if ((aShapeID = node->getshapeId()) > 0) {
543 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
544 if ( sm->Contains( theElem ))
546 if ( aShape.IsNull() )
547 aShape = aMesh->IndexToShape( aShapeID );
553 // None of nodes is on a proper shape,
554 // find the shape among ancestors of aShape on which a node is
555 if ( !aShape.IsNull() ) {
556 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
557 for ( ; ancIt.More(); ancIt.Next() ) {
558 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
559 if ( sm && sm->Contains( theElem ))
560 return aMesh->ShapeToIndex( ancIt.Value() );
565 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
566 while ( const SMESHDS_SubMesh* sm = smIt->next() )
567 if ( sm->Contains( theElem ))
574 //=======================================================================
575 //function : IsMedium
577 //=======================================================================
579 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
580 const SMDSAbs_ElementType typeToCheck)
582 bool isMedium = false;
583 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
584 while (it->more() && !isMedium ) {
585 const SMDS_MeshElement* elem = it->next();
586 isMedium = elem->IsMediumNode(node);
591 //=======================================================================
592 //function : shiftNodesQuadTria
593 //purpose : Shift nodes in the array corresponded to quadratic triangle
594 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
595 //=======================================================================
597 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
599 const SMDS_MeshNode* nd1 = aNodes[0];
600 aNodes[0] = aNodes[1];
601 aNodes[1] = aNodes[2];
603 const SMDS_MeshNode* nd2 = aNodes[3];
604 aNodes[3] = aNodes[4];
605 aNodes[4] = aNodes[5];
609 //=======================================================================
610 //function : getNodesFromTwoTria
612 //=======================================================================
614 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
615 const SMDS_MeshElement * theTria2,
616 vector< const SMDS_MeshNode*>& N1,
617 vector< const SMDS_MeshNode*>& N2)
619 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
620 if ( N1.size() < 6 ) return false;
621 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
622 if ( N2.size() < 6 ) return false;
624 int sames[3] = {-1,-1,-1};
636 if(nbsames!=2) return false;
638 shiftNodesQuadTria(N1);
640 shiftNodesQuadTria(N1);
643 i = sames[0] + sames[1] + sames[2];
645 shiftNodesQuadTria(N2);
647 // now we receive following N1 and N2 (using numeration as in the image below)
648 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
649 // i.e. first nodes from both arrays form a new diagonal
653 //=======================================================================
654 //function : InverseDiag
655 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
656 // but having other common link.
657 // Return False if args are improper
658 //=======================================================================
660 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
661 const SMDS_MeshElement * theTria2 )
665 if ( !theTria1 || !theTria2 ||
666 !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
667 !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
668 theTria1->GetType() != SMDSAbs_Face ||
669 theTria2->GetType() != SMDSAbs_Face )
672 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
673 (theTria2->GetEntityType() == SMDSEntity_Triangle))
675 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
676 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
680 // put nodes in array and find out indices of the same ones
681 const SMDS_MeshNode* aNodes [6];
682 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
684 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
685 while ( it->more() ) {
686 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
688 if ( i > 2 ) // theTria2
689 // find same node of theTria1
690 for ( int j = 0; j < 3; j++ )
691 if ( aNodes[ i ] == aNodes[ j ]) {
700 return false; // theTria1 is not a triangle
701 it = theTria2->nodesIterator();
703 if ( i == 6 && it->more() )
704 return false; // theTria2 is not a triangle
707 // find indices of 1,2 and of A,B in theTria1
708 int iA = -1, iB = 0, i1 = 0, i2 = 0;
709 for ( i = 0; i < 6; i++ ) {
710 if ( sameInd [ i ] == -1 ) {
715 if ( iA >= 0) iB = i;
719 // nodes 1 and 2 should not be the same
720 if ( aNodes[ i1 ] == aNodes[ i2 ] )
724 aNodes[ iA ] = aNodes[ i2 ];
726 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
728 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
729 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
733 } // end if(F1 && F2)
735 // check case of quadratic faces
736 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
737 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
739 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
740 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
744 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
745 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
753 vector< const SMDS_MeshNode* > N1;
754 vector< const SMDS_MeshNode* > N2;
755 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
757 // now we receive following N1 and N2 (using numeration as above image)
758 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
759 // i.e. first nodes from both arrays determ new diagonal
761 vector< const SMDS_MeshNode*> N1new( N1.size() );
762 vector< const SMDS_MeshNode*> N2new( N2.size() );
763 N1new.back() = N1.back(); // central node of biquadratic
764 N2new.back() = N2.back();
765 N1new[0] = N1[0]; N2new[0] = N1[0];
766 N1new[1] = N2[0]; N2new[1] = N1[1];
767 N1new[2] = N2[1]; N2new[2] = N2[0];
768 N1new[3] = N1[4]; N2new[3] = N1[3];
769 N1new[4] = N2[3]; N2new[4] = N2[5];
770 N1new[5] = N1[5]; N2new[5] = N1[4];
771 // change nodes in faces
772 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
773 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
775 // move the central node of biquadratic triangle
776 SMESH_MesherHelper helper( *GetMesh() );
777 for ( int is2nd = 0; is2nd < 2; ++is2nd )
779 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
780 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
781 if ( nodes.size() < 7 )
783 helper.SetSubShape( tria->getshapeId() );
784 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
788 xyz = ( SMESH_NodeXYZ( nodes[3] ) +
789 SMESH_NodeXYZ( nodes[4] ) +
790 SMESH_NodeXYZ( nodes[5] )) / 3.;
795 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
796 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
797 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
799 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
800 xyz = S->Value( uv.X(), uv.Y() );
801 xyz.Transform( loc );
802 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
803 nodes[6]->getshapeId() > 0 )
804 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
806 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
811 //=======================================================================
812 //function : findTriangles
813 //purpose : find triangles sharing theNode1-theNode2 link
814 //=======================================================================
816 static bool findTriangles(const SMDS_MeshNode * theNode1,
817 const SMDS_MeshNode * theNode2,
818 const SMDS_MeshElement*& theTria1,
819 const SMDS_MeshElement*& theTria2)
821 if ( !theNode1 || !theNode2 ) return false;
823 theTria1 = theTria2 = 0;
825 set< const SMDS_MeshElement* > emap;
826 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
828 const SMDS_MeshElement* elem = it->next();
829 if ( elem->NbCornerNodes() == 3 )
832 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
834 const SMDS_MeshElement* elem = it->next();
835 if ( emap.count( elem )) {
843 // theTria1 must be element with minimum ID
844 if ( theTria2->GetID() < theTria1->GetID() )
845 std::swap( theTria2, theTria1 );
853 //=======================================================================
854 //function : InverseDiag
855 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
856 // with ones built on the same 4 nodes but having other common link.
857 // Return false if proper faces not found
858 //=======================================================================
860 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
861 const SMDS_MeshNode * theNode2)
865 const SMDS_MeshElement *tr1, *tr2;
866 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
869 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
870 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
873 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
874 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
876 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
877 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
881 // put nodes in array
882 // and find indices of 1,2 and of A in tr1 and of B in tr2
883 int i, iA1 = 0, i1 = 0;
884 const SMDS_MeshNode* aNodes1 [3];
885 SMDS_ElemIteratorPtr it;
886 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
887 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
888 if ( aNodes1[ i ] == theNode1 )
889 iA1 = i; // node A in tr1
890 else if ( aNodes1[ i ] != theNode2 )
894 const SMDS_MeshNode* aNodes2 [3];
895 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
896 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
897 if ( aNodes2[ i ] == theNode2 )
898 iB2 = i; // node B in tr2
899 else if ( aNodes2[ i ] != theNode1 )
903 // nodes 1 and 2 should not be the same
904 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
908 aNodes1[ iA1 ] = aNodes2[ i2 ];
910 aNodes2[ iB2 ] = aNodes1[ i1 ];
912 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
913 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
918 // check case of quadratic faces
919 return InverseDiag(tr1,tr2);
922 //=======================================================================
923 //function : getQuadrangleNodes
924 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
925 // fusion of triangles tr1 and tr2 having shared link on
926 // theNode1 and theNode2
927 //=======================================================================
929 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
930 const SMDS_MeshNode * theNode1,
931 const SMDS_MeshNode * theNode2,
932 const SMDS_MeshElement * tr1,
933 const SMDS_MeshElement * tr2 )
935 if( tr1->NbNodes() != tr2->NbNodes() )
937 // find the 4-th node to insert into tr1
938 const SMDS_MeshNode* n4 = 0;
939 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
941 while ( !n4 && i<3 ) {
942 const SMDS_MeshNode * n = cast2Node( it->next() );
944 bool isDiag = ( n == theNode1 || n == theNode2 );
948 // Make an array of nodes to be in a quadrangle
949 int iNode = 0, iFirstDiag = -1;
950 it = tr1->nodesIterator();
953 const SMDS_MeshNode * n = cast2Node( it->next() );
955 bool isDiag = ( n == theNode1 || n == theNode2 );
957 if ( iFirstDiag < 0 )
959 else if ( iNode - iFirstDiag == 1 )
960 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
962 else if ( n == n4 ) {
963 return false; // tr1 and tr2 should not have all the same nodes
965 theQuadNodes[ iNode++ ] = n;
967 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
968 theQuadNodes[ iNode ] = n4;
973 //=======================================================================
974 //function : DeleteDiag
975 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
976 // with a quadrangle built on the same 4 nodes.
977 // Return false if proper faces not found
978 //=======================================================================
980 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
981 const SMDS_MeshNode * theNode2)
985 const SMDS_MeshElement *tr1, *tr2;
986 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
989 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
990 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
993 SMESHDS_Mesh * aMesh = GetMeshDS();
995 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
996 (tr2->GetEntityType() == SMDSEntity_Triangle))
998 const SMDS_MeshNode* aNodes [ 4 ];
999 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1002 const SMDS_MeshElement* newElem = 0;
1003 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1004 myLastCreatedElems.push_back(newElem);
1005 AddToSameGroups( newElem, tr1, aMesh );
1006 int aShapeId = tr1->getshapeId();
1008 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1010 aMesh->RemoveElement( tr1 );
1011 aMesh->RemoveElement( tr2 );
1016 // check case of quadratic faces
1017 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1019 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1023 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1024 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1032 vector< const SMDS_MeshNode* > N1;
1033 vector< const SMDS_MeshNode* > N2;
1034 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1036 // now we receive following N1 and N2 (using numeration as above image)
1037 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1038 // i.e. first nodes from both arrays determ new diagonal
1040 const SMDS_MeshNode* aNodes[8];
1050 const SMDS_MeshElement* newElem = 0;
1051 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1052 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1053 myLastCreatedElems.push_back(newElem);
1054 AddToSameGroups( newElem, tr1, aMesh );
1055 int aShapeId = tr1->getshapeId();
1058 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1060 aMesh->RemoveElement( tr1 );
1061 aMesh->RemoveElement( tr2 );
1063 // remove middle node (9)
1064 GetMeshDS()->RemoveNode( N1[4] );
1069 //=======================================================================
1070 //function : Reorient
1071 //purpose : Reverse theElement orientation
1072 //=======================================================================
1074 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1080 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1081 if ( !it || !it->more() )
1084 const SMDSAbs_ElementType type = theElem->GetType();
1085 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1088 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1089 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1091 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1093 MESSAGE("Warning: bad volumic element");
1096 const int nbFaces = aPolyedre->NbFaces();
1097 vector<const SMDS_MeshNode *> poly_nodes;
1098 vector<int> quantities (nbFaces);
1100 // reverse each face of the polyedre
1101 for (int iface = 1; iface <= nbFaces; iface++) {
1102 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1103 quantities[iface - 1] = nbFaceNodes;
1105 for (inode = nbFaceNodes; inode >= 1; inode--) {
1106 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1107 poly_nodes.push_back(curNode);
1110 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1112 else // other elements
1114 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1115 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1116 if ( interlace.empty() )
1118 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1122 SMDS_MeshCell::applyInterlace( interlace, nodes );
1124 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1129 //================================================================================
1131 * \brief Reorient faces.
1132 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1133 * \param theDirection - desired direction of normal of \a theFace
1134 * \param theFace - one of \a theFaces that should be oriented according to
1135 * \a theDirection and whose orientation defines orientation of other faces
1136 * \return number of reoriented faces.
1138 //================================================================================
1140 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1141 const gp_Dir& theDirection,
1142 const SMDS_MeshElement * theFace)
1145 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1147 if ( theFaces.empty() )
1149 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
1150 while ( fIt->more() )
1151 theFaces.insert( theFaces.end(), fIt->next() );
1154 // orient theFace according to theDirection
1156 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1157 if ( normal * theDirection.XYZ() < 0 )
1158 nbReori += Reorient( theFace );
1160 // Orient other faces
1162 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1163 TIDSortedElemSet avoidSet;
1164 set< SMESH_TLink > checkedLinks;
1165 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1167 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1168 theFaces.erase( theFace );
1169 startFaces.insert( theFace );
1171 int nodeInd1, nodeInd2;
1172 const SMDS_MeshElement* otherFace;
1173 vector< const SMDS_MeshElement* > facesNearLink;
1174 vector< std::pair< int, int > > nodeIndsOfFace;
1176 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1177 while ( !startFaces.empty() )
1179 startFace = startFaces.begin();
1180 theFace = *startFace;
1181 startFaces.erase( startFace );
1182 if ( !visitedFaces.insert( theFace ).second )
1186 avoidSet.insert(theFace);
1188 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1190 const int nbNodes = theFace->NbCornerNodes();
1191 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1193 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1194 linkIt_isNew = checkedLinks.insert( link );
1195 if ( !linkIt_isNew.second )
1197 // link has already been checked and won't be encountered more
1198 // if the group (theFaces) is manifold
1199 //checkedLinks.erase( linkIt_isNew.first );
1203 facesNearLink.clear();
1204 nodeIndsOfFace.clear();
1205 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1207 &nodeInd1, &nodeInd2 )))
1208 if ( otherFace != theFace)
1210 facesNearLink.push_back( otherFace );
1211 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1212 avoidSet.insert( otherFace );
1214 if ( facesNearLink.size() > 1 )
1216 // NON-MANIFOLD mesh shell !
1217 // select a face most co-directed with theFace,
1218 // other faces won't be visited this time
1220 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1221 double proj, maxProj = -1;
1222 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1223 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1224 if (( proj = Abs( NF * NOF )) > maxProj ) {
1226 otherFace = facesNearLink[i];
1227 nodeInd1 = nodeIndsOfFace[i].first;
1228 nodeInd2 = nodeIndsOfFace[i].second;
1231 // not to visit rejected faces
1232 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1233 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1234 visitedFaces.insert( facesNearLink[i] );
1236 else if ( facesNearLink.size() == 1 )
1238 otherFace = facesNearLink[0];
1239 nodeInd1 = nodeIndsOfFace.back().first;
1240 nodeInd2 = nodeIndsOfFace.back().second;
1242 if ( otherFace && otherFace != theFace)
1244 // link must be reverse in otherFace if orientation to otherFace
1245 // is same as that of theFace
1246 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1248 nbReori += Reorient( otherFace );
1250 startFaces.insert( otherFace );
1253 std::swap( link.first, link.second ); // reverse the link
1259 //================================================================================
1261 * \brief Reorient faces basing on orientation of adjacent volumes.
1262 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1263 * \param theVolumes - reference volumes.
1264 * \param theOutsideNormal - to orient faces to have their normal
1265 * pointing either \a outside or \a inside the adjacent volumes.
1266 * \return number of reoriented faces.
1268 //================================================================================
1270 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1271 TIDSortedElemSet & theVolumes,
1272 const bool theOutsideNormal)
1276 SMDS_ElemIteratorPtr faceIt;
1277 if ( theFaces.empty() )
1278 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1280 faceIt = SMESHUtils::elemSetIterator( theFaces );
1282 vector< const SMDS_MeshNode* > faceNodes;
1283 TIDSortedElemSet checkedVolumes;
1284 set< const SMDS_MeshNode* > faceNodesSet;
1285 SMDS_VolumeTool volumeTool;
1287 while ( faceIt->more() ) // loop on given faces
1289 const SMDS_MeshElement* face = faceIt->next();
1290 if ( face->GetType() != SMDSAbs_Face )
1293 const size_t nbCornersNodes = face->NbCornerNodes();
1294 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1296 checkedVolumes.clear();
1297 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1298 while ( vIt->more() )
1300 const SMDS_MeshElement* volume = vIt->next();
1302 if ( !checkedVolumes.insert( volume ).second )
1304 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1307 // is volume adjacent?
1308 bool allNodesCommon = true;
1309 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1310 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1311 if ( !allNodesCommon )
1314 // get nodes of a corresponding volume facet
1315 faceNodesSet.clear();
1316 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1317 volumeTool.Set( volume );
1318 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1319 if ( facetID < 0 ) continue;
1320 volumeTool.SetExternalNormal();
1321 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1323 // compare order of faceNodes and facetNodes
1324 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1326 for ( int i = 0; i < 2; ++i )
1328 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1329 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1330 if ( faceNodes[ iN ] == n )
1336 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1337 if ( isOutside != theOutsideNormal )
1338 nbReori += Reorient( face );
1340 } // loop on given faces
1345 //=======================================================================
1346 //function : getBadRate
1348 //=======================================================================
1350 static double getBadRate (const SMDS_MeshElement* theElem,
1351 SMESH::Controls::NumericalFunctorPtr& theCrit)
1353 SMESH::Controls::TSequenceOfXYZ P;
1354 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1356 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1357 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1360 //=======================================================================
1361 //function : QuadToTri
1362 //purpose : Cut quadrangles into triangles.
1363 // theCrit is used to select a diagonal to cut
1364 //=======================================================================
1366 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1367 SMESH::Controls::NumericalFunctorPtr theCrit)
1371 if ( !theCrit.get() )
1374 SMESHDS_Mesh * aMesh = GetMeshDS();
1375 Handle(Geom_Surface) surface;
1376 SMESH_MesherHelper helper( *GetMesh() );
1378 myLastCreatedElems.reserve( theElems.size() * 2 );
1380 TIDSortedElemSet::iterator itElem;
1381 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1383 const SMDS_MeshElement* elem = *itElem;
1384 if ( !elem || elem->GetType() != SMDSAbs_Face )
1386 if ( elem->NbCornerNodes() != 4 )
1389 // retrieve element nodes
1390 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1392 // compare two sets of possible triangles
1393 double aBadRate1, aBadRate2; // to what extent a set is bad
1394 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1395 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1396 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1398 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1399 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1400 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1402 const int aShapeId = FindShape( elem );
1403 const SMDS_MeshElement* newElem1 = 0;
1404 const SMDS_MeshElement* newElem2 = 0;
1406 if ( !elem->IsQuadratic() ) // split linear quadrangle
1408 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1409 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1410 if ( aBadRate1 <= aBadRate2 ) {
1411 // tr1 + tr2 is better
1412 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1413 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1416 // tr3 + tr4 is better
1417 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1418 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1421 else // split quadratic quadrangle
1423 helper.SetIsQuadratic( true );
1424 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1426 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1427 if ( aNodes.size() == 9 )
1429 helper.SetIsBiQuadratic( true );
1430 if ( aBadRate1 <= aBadRate2 )
1431 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1433 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1435 // create a new element
1436 if ( aBadRate1 <= aBadRate2 ) {
1437 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1438 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1441 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1442 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1446 // care of a new element
1448 myLastCreatedElems.push_back(newElem1);
1449 myLastCreatedElems.push_back(newElem2);
1450 AddToSameGroups( newElem1, elem, aMesh );
1451 AddToSameGroups( newElem2, elem, aMesh );
1453 // put a new triangle on the same shape
1455 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1456 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1458 aMesh->RemoveElement( elem );
1463 //=======================================================================
1465 * \brief Split each of given quadrangles into 4 triangles.
1466 * \param theElems - The faces to be split. If empty all faces are split.
1468 //=======================================================================
1470 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1473 myLastCreatedElems.reserve( theElems.size() * 4 );
1475 SMESH_MesherHelper helper( *GetMesh() );
1476 helper.SetElementsOnShape( true );
1478 SMDS_ElemIteratorPtr faceIt;
1479 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1480 else faceIt = SMESHUtils::elemSetIterator( theElems );
1483 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1485 vector< const SMDS_MeshNode* > nodes;
1486 SMESHDS_SubMesh* subMeshDS = 0;
1488 Handle(Geom_Surface) surface;
1489 TopLoc_Location loc;
1491 while ( faceIt->more() )
1493 const SMDS_MeshElement* quad = faceIt->next();
1494 if ( !quad || quad->NbCornerNodes() != 4 )
1497 // get a surface the quad is on
1499 if ( quad->getshapeId() < 1 )
1502 helper.SetSubShape( 0 );
1505 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1507 helper.SetSubShape( quad->getshapeId() );
1508 if ( !helper.GetSubShape().IsNull() &&
1509 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1511 F = TopoDS::Face( helper.GetSubShape() );
1512 surface = BRep_Tool::Surface( F, loc );
1513 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1517 helper.SetSubShape( 0 );
1522 // create a central node
1524 const SMDS_MeshNode* nCentral;
1525 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1527 if ( nodes.size() == 9 )
1529 nCentral = nodes.back();
1536 for ( ; iN < nodes.size(); ++iN )
1537 xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1539 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1540 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1542 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1543 xyz[0], xyz[1], xyz[2], xyz[3],
1544 xyz[4], xyz[5], xyz[6], xyz[7] );
1548 for ( ; iN < nodes.size(); ++iN )
1549 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1551 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1552 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1554 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1555 uv[0], uv[1], uv[2], uv[3],
1556 uv[4], uv[5], uv[6], uv[7] );
1558 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1562 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1563 uv[8].X(), uv[8].Y() );
1564 myLastCreatedNodes.push_back( nCentral );
1567 // create 4 triangles
1569 helper.SetIsQuadratic ( nodes.size() > 4 );
1570 helper.SetIsBiQuadratic( nodes.size() == 9 );
1571 if ( helper.GetIsQuadratic() )
1572 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1574 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1576 for ( int i = 0; i < 4; ++i )
1578 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1581 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1582 myLastCreatedElems.push_back( tria );
1587 //=======================================================================
1588 //function : BestSplit
1589 //purpose : Find better diagonal for cutting.
1590 //=======================================================================
1592 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1593 SMESH::Controls::NumericalFunctorPtr theCrit)
1600 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1603 if( theQuad->NbNodes()==4 ||
1604 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1606 // retrieve element nodes
1607 const SMDS_MeshNode* aNodes [4];
1608 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1610 //while (itN->more())
1612 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1614 // compare two sets of possible triangles
1615 double aBadRate1, aBadRate2; // to what extent a set is bad
1616 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1617 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1618 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1620 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1621 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1622 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1623 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1624 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1625 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1626 return 1; // diagonal 1-3
1628 return 2; // diagonal 2-4
1635 // Methods of splitting volumes into tetra
1637 const int theHexTo5_1[5*4+1] =
1639 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1641 const int theHexTo5_2[5*4+1] =
1643 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1645 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1647 const int theHexTo6_1[6*4+1] =
1649 1, 5, 6, 0, 0, 1, 2, 6, 0, 4, 5, 6, 0, 4, 6, 7, 0, 2, 3, 6, 0, 3, 7, 6, -1
1651 const int theHexTo6_2[6*4+1] =
1653 2, 6, 7, 1, 1, 2, 3, 7, 1, 5, 6, 7, 1, 5, 7, 4, 1, 3, 0, 7, 1, 0, 4, 7, -1
1655 const int theHexTo6_3[6*4+1] =
1657 3, 7, 4, 2, 2, 3, 0, 4, 2, 6, 7, 4, 2, 6, 4, 5, 2, 0, 1, 4, 2, 1, 5, 4, -1
1659 const int theHexTo6_4[6*4+1] =
1661 0, 4, 5, 3, 3, 0, 1, 5, 3, 7, 4, 5, 3, 7, 5, 6, 3, 1, 2, 5, 3, 2, 6, 5, -1
1663 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1665 const int thePyraTo2_1[2*4+1] =
1667 0, 1, 2, 4, 0, 2, 3, 4, -1
1669 const int thePyraTo2_2[2*4+1] =
1671 1, 2, 3, 4, 1, 3, 0, 4, -1
1673 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1675 const int thePentaTo3_1[3*4+1] =
1677 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1679 const int thePentaTo3_2[3*4+1] =
1681 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1683 const int thePentaTo3_3[3*4+1] =
1685 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1687 const int thePentaTo3_4[3*4+1] =
1689 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1691 const int thePentaTo3_5[3*4+1] =
1693 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1695 const int thePentaTo3_6[3*4+1] =
1697 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1699 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1700 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1702 // Methods of splitting hexahedron into prisms
1704 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1706 0, 1, 8, 4, 5, 9, 1, 2, 8, 5, 6, 9, 2, 3, 8, 6, 7, 9, 3, 0, 8, 7, 4, 9, -1
1708 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1710 1, 0, 8, 2, 3, 9, 0, 4, 8, 3, 7, 9, 4, 5, 8, 7, 6, 9, 5, 1, 8, 6, 2, 9, -1
1712 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1714 0, 3, 9, 1, 2, 8, 3, 7, 9, 2, 6, 8, 7, 4, 9, 6, 5, 8, 4, 0, 9, 5, 1, 8, -1
1717 const int theHexTo2Prisms_BT_1[6*2+1] =
1719 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1721 const int theHexTo2Prisms_BT_2[6*2+1] =
1723 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1725 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1727 const int theHexTo2Prisms_LR_1[6*2+1] =
1729 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1731 const int theHexTo2Prisms_LR_2[6*2+1] =
1733 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1735 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1737 const int theHexTo2Prisms_FB_1[6*2+1] =
1739 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1741 const int theHexTo2Prisms_FB_2[6*2+1] =
1743 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1745 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1748 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1751 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1752 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1753 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1754 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1760 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1761 bool _baryNode; //!< additional node is to be created at cell barycenter
1762 bool _ownConn; //!< to delete _connectivity in destructor
1763 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1765 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1766 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1767 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1768 bool hasFacet( const TTriangleFacet& facet ) const
1770 if ( _nbCorners == 4 )
1772 const int* tetConn = _connectivity;
1773 for ( ; tetConn[0] >= 0; tetConn += 4 )
1774 if (( facet.contains( tetConn[0] ) +
1775 facet.contains( tetConn[1] ) +
1776 facet.contains( tetConn[2] ) +
1777 facet.contains( tetConn[3] )) == 3 )
1780 else // prism, _nbCorners == 6
1782 const int* prismConn = _connectivity;
1783 for ( ; prismConn[0] >= 0; prismConn += 6 )
1785 if (( facet.contains( prismConn[0] ) &&
1786 facet.contains( prismConn[1] ) &&
1787 facet.contains( prismConn[2] ))
1789 ( facet.contains( prismConn[3] ) &&
1790 facet.contains( prismConn[4] ) &&
1791 facet.contains( prismConn[5] )))
1799 //=======================================================================
1801 * \brief return TSplitMethod for the given element to split into tetrahedra
1803 //=======================================================================
1805 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1807 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1809 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1810 // an edge and a face barycenter; tertaherdons are based on triangles and
1811 // a volume barycenter
1812 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1814 // Find out how adjacent volumes are split
1816 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1817 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1818 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1820 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1821 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1822 if ( nbNodes < 4 ) continue;
1824 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1825 const int* nInd = vol.GetFaceNodesIndices( iF );
1828 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1829 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1830 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1831 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1835 int iCom = 0; // common node of triangle faces to split into
1836 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1838 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1839 nInd[ iQ * ( (iCom+1)%nbNodes )],
1840 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1841 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1842 nInd[ iQ * ( (iCom+2)%nbNodes )],
1843 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1844 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1846 triaSplits.push_back( t012 );
1847 triaSplits.push_back( t023 );
1852 if ( !triaSplits.empty() )
1853 hasAdjacentSplits = true;
1856 // Among variants of split method select one compliant with adjacent volumes
1858 TSplitMethod method;
1859 if ( !vol.Element()->IsPoly() && !is24TetMode )
1861 int nbVariants = 2, nbTet = 0;
1862 const int** connVariants = 0;
1863 switch ( vol.Element()->GetEntityType() )
1865 case SMDSEntity_Hexa:
1866 case SMDSEntity_Quad_Hexa:
1867 case SMDSEntity_TriQuad_Hexa:
1868 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1869 connVariants = theHexTo5, nbTet = 5;
1871 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1873 case SMDSEntity_Pyramid:
1874 case SMDSEntity_Quad_Pyramid:
1875 connVariants = thePyraTo2; nbTet = 2;
1877 case SMDSEntity_Penta:
1878 case SMDSEntity_Quad_Penta:
1879 case SMDSEntity_BiQuad_Penta:
1880 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1885 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1887 // check method compliancy with adjacent tetras,
1888 // all found splits must be among facets of tetras described by this method
1889 method = TSplitMethod( nbTet, connVariants[variant] );
1890 if ( hasAdjacentSplits && method._nbSplits > 0 )
1892 bool facetCreated = true;
1893 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1895 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1896 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1897 facetCreated = method.hasFacet( *facet );
1899 if ( !facetCreated )
1900 method = TSplitMethod(0); // incompatible method
1904 if ( method._nbSplits < 1 )
1906 // No standard method is applicable, use a generic solution:
1907 // each facet of a volume is split into triangles and
1908 // each of triangles and a volume barycenter form a tetrahedron.
1910 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1912 int* connectivity = new int[ maxTetConnSize + 1 ];
1913 method._connectivity = connectivity;
1914 method._ownConn = true;
1915 method._baryNode = !isHex27; // to create central node or not
1918 int baryCenInd = vol.NbNodes() - int( isHex27 );
1919 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1921 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1922 const int* nInd = vol.GetFaceNodesIndices( iF );
1923 // find common node of triangle facets of tetra to create
1924 int iCommon = 0; // index in linear numeration
1925 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1926 if ( !triaSplits.empty() )
1929 const TTriangleFacet* facet = &triaSplits.front();
1930 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1931 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1932 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1935 else if ( nbNodes > 3 && !is24TetMode )
1937 // find the best method of splitting into triangles by aspect ratio
1938 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1939 map< double, int > badness2iCommon;
1940 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1941 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1942 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1945 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1947 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1948 nodes[ iQ*((iLast-1)%nbNodes)],
1949 nodes[ iQ*((iLast )%nbNodes)]);
1950 badness += getBadRate( &tria, aspectRatio );
1952 badness2iCommon.insert( make_pair( badness, iCommon ));
1954 // use iCommon with lowest badness
1955 iCommon = badness2iCommon.begin()->second;
1957 if ( iCommon >= nbNodes )
1958 iCommon = 0; // something wrong
1960 // fill connectivity of tetrahedra based on a current face
1961 int nbTet = nbNodes - 2;
1962 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1967 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1968 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1972 method._faceBaryNode[ iF ] = 0;
1973 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1976 for ( int i = 0; i < nbTet; ++i )
1978 int i1 = i, i2 = (i+1) % nbNodes;
1979 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1980 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1981 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1982 connectivity[ connSize++ ] = faceBaryCenInd;
1983 connectivity[ connSize++ ] = baryCenInd;
1988 for ( int i = 0; i < nbTet; ++i )
1990 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1991 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1992 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1993 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1994 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1995 connectivity[ connSize++ ] = baryCenInd;
1998 method._nbSplits += nbTet;
2000 } // loop on volume faces
2002 connectivity[ connSize++ ] = -1;
2004 } // end of generic solution
2008 //=======================================================================
2010 * \brief return TSplitMethod to split haxhedron into prisms
2012 //=======================================================================
2014 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2015 const int methodFlags,
2016 const int facetToSplit)
2018 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2020 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2022 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2024 static TSplitMethod to4methods[4]; // order BT, LR, FB
2025 if ( to4methods[iF]._nbSplits == 0 )
2029 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2030 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2031 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2034 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2035 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2036 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2039 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2040 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2041 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2043 default: return to4methods[3];
2045 to4methods[iF]._nbSplits = 4;
2046 to4methods[iF]._nbCorners = 6;
2048 return to4methods[iF];
2050 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2052 TSplitMethod method;
2054 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2056 const int nbVariants = 2, nbSplits = 2;
2057 const int** connVariants = 0;
2059 case 0: connVariants = theHexTo2Prisms_BT; break;
2060 case 1: connVariants = theHexTo2Prisms_LR; break;
2061 case 2: connVariants = theHexTo2Prisms_FB; break;
2062 default: return method;
2065 // look for prisms adjacent via facetToSplit and an opposite one
2066 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2068 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2069 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2070 if ( nbNodes != 4 ) return method;
2072 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2073 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2074 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2076 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2078 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2083 // there are adjacent prism
2084 for ( int variant = 0; variant < nbVariants; ++variant )
2086 // check method compliancy with adjacent prisms,
2087 // the found prism facets must be among facets of prisms described by current method
2088 method._nbSplits = nbSplits;
2089 method._nbCorners = 6;
2090 method._connectivity = connVariants[ variant ];
2091 if ( method.hasFacet( *t ))
2096 // No adjacent prisms. Select a variant with a best aspect ratio.
2098 double badness[2] = { 0., 0. };
2099 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2100 const SMDS_MeshNode** nodes = vol.GetNodes();
2101 for ( int variant = 0; variant < nbVariants; ++variant )
2102 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2104 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2105 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2107 method._connectivity = connVariants[ variant ];
2108 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2109 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2110 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2112 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2115 badness[ variant ] += getBadRate( &tria, aspectRatio );
2117 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2119 method._nbSplits = nbSplits;
2120 method._nbCorners = 6;
2121 method._connectivity = connVariants[ iBetter ];
2126 //================================================================================
2128 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2130 //================================================================================
2132 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2133 const SMDSAbs_GeometryType geom ) const
2135 // find the tetrahedron including the three nodes of facet
2136 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2137 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2138 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2139 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2140 while ( volIt1->more() )
2142 const SMDS_MeshElement* v = volIt1->next();
2143 if ( v->GetGeomType() != geom )
2145 const int lastCornerInd = v->NbCornerNodes() - 1;
2146 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2147 continue; // medium node not allowed
2148 const int ind2 = v->GetNodeIndex( n2 );
2149 if ( ind2 < 0 || lastCornerInd < ind2 )
2151 const int ind3 = v->GetNodeIndex( n3 );
2152 if ( ind3 < 0 || lastCornerInd < ind3 )
2159 //=======================================================================
2161 * \brief A key of a face of volume
2163 //=======================================================================
2165 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2167 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2169 TIDSortedNodeSet sortedNodes;
2170 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2171 int nbNodes = vol.NbFaceNodes( iF );
2172 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2173 for ( int i = 0; i < nbNodes; i += iQ )
2174 sortedNodes.insert( fNodes[i] );
2175 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2176 first.first = (*(n++))->GetID();
2177 first.second = (*(n++))->GetID();
2178 second.first = (*(n++))->GetID();
2179 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2184 //=======================================================================
2185 //function : SplitVolumes
2186 //purpose : Split volume elements into tetrahedra or prisms.
2187 // If facet ID < 0, element is split into tetrahedra,
2188 // else a hexahedron is split into prisms so that the given facet is
2189 // split into triangles
2190 //=======================================================================
2192 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2193 const int theMethodFlags)
2195 SMDS_VolumeTool volTool;
2196 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2197 fHelper.ToFixNodeParameters( true );
2199 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2200 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2202 SMESH_SequenceOfElemPtr newNodes, newElems;
2204 // map face of volume to it's baricenrtic node
2205 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2207 vector<const SMDS_MeshElement* > splitVols;
2209 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2210 for ( ; elem2facet != theElems.end(); ++elem2facet )
2212 const SMDS_MeshElement* elem = elem2facet->first;
2213 const int facetToSplit = elem2facet->second;
2214 if ( elem->GetType() != SMDSAbs_Volume )
2216 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2217 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2220 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2222 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2223 getTetraSplitMethod( volTool, theMethodFlags ) :
2224 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2225 if ( splitMethod._nbSplits < 1 ) continue;
2227 // find submesh to add new tetras to
2228 if ( !subMesh || !subMesh->Contains( elem ))
2230 int shapeID = FindShape( elem );
2231 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2232 subMesh = GetMeshDS()->MeshElements( shapeID );
2235 if ( elem->IsQuadratic() )
2238 // add quadratic links to the helper
2239 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2241 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2242 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2243 for ( int iN = 0; iN < nbN; iN += iQ )
2244 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2246 helper.SetIsQuadratic( true );
2251 helper.SetIsQuadratic( false );
2253 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2254 volTool.GetNodes() + elem->NbNodes() );
2255 helper.SetElementsOnShape( true );
2256 if ( splitMethod._baryNode )
2258 // make a node at barycenter
2259 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2260 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2261 nodes.push_back( gcNode );
2262 newNodes.push_back( gcNode );
2264 if ( !splitMethod._faceBaryNode.empty() )
2266 // make or find baricentric nodes of faces
2267 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2268 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2270 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2271 volFace2BaryNode.insert
2272 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2275 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2276 newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2278 nodes.push_back( iF_n->second = f_n->second );
2283 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2284 const int* volConn = splitMethod._connectivity;
2285 if ( splitMethod._nbCorners == 4 ) // tetra
2286 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2287 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2288 nodes[ volConn[1] ],
2289 nodes[ volConn[2] ],
2290 nodes[ volConn[3] ]));
2292 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2293 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2294 nodes[ volConn[1] ],
2295 nodes[ volConn[2] ],
2296 nodes[ volConn[3] ],
2297 nodes[ volConn[4] ],
2298 nodes[ volConn[5] ]));
2300 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2302 // Split faces on sides of the split volume
2304 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2305 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2307 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2308 if ( nbNodes < 4 ) continue;
2310 // find an existing face
2311 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2312 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2313 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2314 /*noMedium=*/false))
2317 helper.SetElementsOnShape( false );
2318 vector< const SMDS_MeshElement* > triangles;
2320 // find submesh to add new triangles in
2321 if ( !fSubMesh || !fSubMesh->Contains( face ))
2323 int shapeID = FindShape( face );
2324 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2326 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2327 if ( iF_n != splitMethod._faceBaryNode.end() )
2329 const SMDS_MeshNode *baryNode = iF_n->second;
2330 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2332 const SMDS_MeshNode* n1 = fNodes[iN];
2333 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2334 const SMDS_MeshNode *n3 = baryNode;
2335 if ( !volTool.IsFaceExternal( iF ))
2337 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2339 if ( fSubMesh ) // update position of the bary node on geometry
2342 subMesh->RemoveNode( baryNode );
2343 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2344 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2345 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2347 fHelper.SetSubShape( s );
2348 gp_XY uv( 1e100, 1e100 );
2350 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2351 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2354 // node is too far from the surface
2355 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2356 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2357 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2364 // among possible triangles create ones described by split method
2365 const int* nInd = volTool.GetFaceNodesIndices( iF );
2366 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2367 int iCom = 0; // common node of triangle faces to split into
2368 list< TTriangleFacet > facets;
2369 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2371 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2372 nInd[ iQ * ( (iCom+1)%nbNodes )],
2373 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2374 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2375 nInd[ iQ * ( (iCom+2)%nbNodes )],
2376 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2377 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2379 facets.push_back( t012 );
2380 facets.push_back( t023 );
2381 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2382 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2383 nInd[ iQ * ((iLast-1)%nbNodes )],
2384 nInd[ iQ * ((iLast )%nbNodes )]));
2388 list< TTriangleFacet >::iterator facet = facets.begin();
2389 if ( facet == facets.end() )
2391 for ( ; facet != facets.end(); ++facet )
2393 if ( !volTool.IsFaceExternal( iF ))
2394 swap( facet->_n2, facet->_n3 );
2395 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2396 volNodes[ facet->_n2 ],
2397 volNodes[ facet->_n3 ]));
2400 for ( size_t i = 0; i < triangles.size(); ++i )
2402 if ( !triangles[ i ]) continue;
2404 fSubMesh->AddElement( triangles[ i ]);
2405 newElems.push_back( triangles[ i ]);
2407 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2408 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2410 } // while a face based on facet nodes exists
2411 } // loop on volume faces to split them into triangles
2413 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2415 if ( geomType == SMDSEntity_TriQuad_Hexa )
2417 // remove medium nodes that could become free
2418 for ( int i = 20; i < volTool.NbNodes(); ++i )
2419 if ( volNodes[i]->NbInverseElements() == 0 )
2420 GetMeshDS()->RemoveNode( volNodes[i] );
2422 } // loop on volumes to split
2424 myLastCreatedNodes = newNodes;
2425 myLastCreatedElems = newElems;
2428 //=======================================================================
2429 //function : GetHexaFacetsToSplit
2430 //purpose : For hexahedra that will be split into prisms, finds facets to
2431 // split into triangles. Only hexahedra adjacent to the one closest
2432 // to theFacetNormal.Location() are returned.
2433 //param [in,out] theHexas - the hexahedra
2434 //param [in] theFacetNormal - facet normal
2435 //param [out] theFacets - the hexahedra and found facet IDs
2436 //=======================================================================
2438 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2439 const gp_Ax1& theFacetNormal,
2440 TFacetOfElem & theFacets)
2442 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2444 // Find a hexa closest to the location of theFacetNormal
2446 const SMDS_MeshElement* startHex;
2448 // get SMDS_ElemIteratorPtr on theHexas
2449 typedef const SMDS_MeshElement* TValue;
2450 typedef TIDSortedElemSet::iterator TSetIterator;
2451 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2452 typedef SMDS_MeshElement::GeomFilter TFilter;
2453 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2454 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2455 ( new TElemSetIter( theHexas.begin(),
2457 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2459 SMESH_ElementSearcher* searcher =
2460 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2462 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2467 throw SALOME_Exception( THIS_METHOD "startHex not found");
2470 // Select a facet of startHex by theFacetNormal
2472 SMDS_VolumeTool vTool( startHex );
2473 double norm[3], dot, maxDot = 0;
2475 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2476 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2478 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2486 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2488 // Fill theFacets starting from facetID of startHex
2490 // facets used for searching of volumes adjacent to already treated ones
2491 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2492 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2493 TFacetMap facetsToCheck;
2495 set<const SMDS_MeshNode*> facetNodes;
2496 const SMDS_MeshElement* curHex;
2498 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2502 // move in two directions from startHex via facetID
2503 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2506 int curFacet = facetID;
2507 if ( is2nd ) // do not treat startHex twice
2509 vTool.Set( curHex );
2510 if ( vTool.IsFreeFace( curFacet, &curHex ))
2516 vTool.GetFaceNodes( curFacet, facetNodes );
2517 vTool.Set( curHex );
2518 curFacet = vTool.GetFaceIndex( facetNodes );
2523 // store a facet to split
2524 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2526 theFacets.insert( make_pair( curHex, -1 ));
2529 if ( !allHex && !theHexas.count( curHex ))
2532 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2533 theFacets.insert( make_pair( curHex, curFacet ));
2534 if ( !facetIt2isNew.second )
2537 // remember not-to-split facets in facetsToCheck
2538 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2539 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2541 if ( iF == curFacet && iF == oppFacet )
2543 TVolumeFaceKey facetKey ( vTool, iF );
2544 TElemFacets elemFacet( facetIt2isNew.first, iF );
2545 pair< TFacetMap::iterator, bool > it2isnew =
2546 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2547 if ( !it2isnew.second )
2548 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2550 // pass to a volume adjacent via oppFacet
2551 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2557 // get a new curFacet
2558 vTool.GetFaceNodes( oppFacet, facetNodes );
2559 vTool.Set( curHex );
2560 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2563 } // move in two directions from startHex via facetID
2565 // Find a new startHex by facetsToCheck
2569 TFacetMap::iterator fIt = facetsToCheck.begin();
2570 while ( !startHex && fIt != facetsToCheck.end() )
2572 const TElemFacets& elemFacets = fIt->second;
2573 const SMDS_MeshElement* hex = elemFacets.first->first;
2574 int splitFacet = elemFacets.first->second;
2575 int lateralFacet = elemFacets.second;
2576 facetsToCheck.erase( fIt );
2577 fIt = facetsToCheck.begin();
2580 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2581 curHex->GetGeomType() != SMDSGeom_HEXA )
2583 if ( !allHex && !theHexas.count( curHex ))
2588 // find a facet of startHex to split
2590 set<const SMDS_MeshNode*> lateralNodes;
2591 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2592 vTool.GetFaceNodes( splitFacet, facetNodes );
2593 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2594 vTool.Set( startHex );
2595 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2597 // look for a facet of startHex having common nodes with facetNodes
2598 // but not lateralFacet
2599 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2601 if ( iF == lateralFacet )
2603 int nbCommonNodes = 0;
2604 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2605 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2606 nbCommonNodes += facetNodes.count( nn[ iN ]);
2608 if ( nbCommonNodes >= 2 )
2615 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2617 } // while ( startHex )
2624 //================================================================================
2626 * \brief Selects nodes of several elements according to a given interlace
2627 * \param [in] srcNodes - nodes to select from
2628 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2629 * \param [in] interlace - indices of nodes for all elements
2630 * \param [in] nbElems - nb of elements
2631 * \param [in] nbNodes - nb of nodes in each element
2632 * \param [in] mesh - the mesh
2633 * \param [out] elemQueue - a list to push elements found by the selected nodes
2634 * \param [in] type - type of elements to look for
2636 //================================================================================
2638 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2639 vector< const SMDS_MeshNode* >* tgtNodesVec,
2640 const int* interlace,
2643 SMESHDS_Mesh* mesh = 0,
2644 list< const SMDS_MeshElement* >* elemQueue=0,
2645 SMDSAbs_ElementType type=SMDSAbs_All)
2647 for ( int iE = 0; iE < nbElems; ++iE )
2649 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2650 const int* select = & interlace[iE*nbNodes];
2651 elemNodes.resize( nbNodes );
2652 for ( int iN = 0; iN < nbNodes; ++iN )
2653 elemNodes[iN] = srcNodes[ select[ iN ]];
2655 const SMDS_MeshElement* e;
2657 for ( int iE = 0; iE < nbElems; ++iE )
2658 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2659 elemQueue->push_back( e );
2663 //=======================================================================
2665 * Split bi-quadratic elements into linear ones without creation of additional nodes
2666 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2667 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2668 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2669 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2670 * will be split in order to keep the mesh conformal.
2671 * \param elems - elements to split
2673 //=======================================================================
2675 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2677 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2678 vector<const SMDS_MeshElement* > splitElems;
2679 list< const SMDS_MeshElement* > elemQueue;
2680 list< const SMDS_MeshElement* >::iterator elemIt;
2682 SMESHDS_Mesh * mesh = GetMeshDS();
2683 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2684 int nbElems, nbNodes;
2686 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2687 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2690 elemQueue.push_back( *elemSetIt );
2691 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2693 const SMDS_MeshElement* elem = *elemIt;
2694 switch( elem->GetEntityType() )
2696 case SMDSEntity_TriQuad_Hexa: // HEX27
2698 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2699 nbElems = nbNodes = 8;
2700 elemType = & hexaType;
2702 // get nodes for new elements
2703 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2704 { 1,9,20,8, 17,22,26,21 },
2705 { 2,10,20,9, 18,23,26,22 },
2706 { 3,11,20,10, 19,24,26,23 },
2707 { 16,21,26,24, 4,12,25,15 },
2708 { 17,22,26,21, 5,13,25,12 },
2709 { 18,23,26,22, 6,14,25,13 },
2710 { 19,24,26,23, 7,15,25,14 }};
2711 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2713 // add boundary faces to elemQueue
2714 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2715 { 4,5,6,7, 12,13,14,15, 25 },
2716 { 0,1,5,4, 8,17,12,16, 21 },
2717 { 1,2,6,5, 9,18,13,17, 22 },
2718 { 2,3,7,6, 10,19,14,18, 23 },
2719 { 3,0,4,7, 11,16,15,19, 24 }};
2720 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2722 // add boundary segments to elemQueue
2723 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2724 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2725 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2726 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2729 case SMDSEntity_BiQuad_Triangle: // TRIA7
2731 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2734 elemType = & quadType;
2736 // get nodes for new elements
2737 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2738 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2740 // add boundary segments to elemQueue
2741 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2742 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2745 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2747 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2750 elemType = & quadType;
2752 // get nodes for new elements
2753 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2754 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2756 // add boundary segments to elemQueue
2757 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2758 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2761 case SMDSEntity_Quad_Edge:
2763 if ( elemIt == elemQueue.begin() )
2764 continue; // an elem is in theElems
2765 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2768 elemType = & segType;
2770 // get nodes for new elements
2771 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2772 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2776 } // switch( elem->GetEntityType() )
2778 // Create new elements
2780 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2784 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2785 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2786 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2787 //elemType->SetID( -1 );
2789 for ( int iE = 0; iE < nbElems; ++iE )
2790 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2793 ReplaceElemInGroups( elem, splitElems, mesh );
2796 for ( size_t i = 0; i < splitElems.size(); ++i )
2797 subMesh->AddElement( splitElems[i] );
2802 //=======================================================================
2803 //function : AddToSameGroups
2804 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2805 //=======================================================================
2807 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2808 const SMDS_MeshElement* elemInGroups,
2809 SMESHDS_Mesh * aMesh)
2811 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2812 if (!groups.empty()) {
2813 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2814 for ( ; grIt != groups.end(); grIt++ ) {
2815 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2816 if ( group && group->Contains( elemInGroups ))
2817 group->SMDSGroup().Add( elemToAdd );
2823 //=======================================================================
2824 //function : RemoveElemFromGroups
2825 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2826 //=======================================================================
2827 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2828 SMESHDS_Mesh * aMesh)
2830 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2831 if (!groups.empty())
2833 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2834 for (; GrIt != groups.end(); GrIt++)
2836 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2837 if (!grp || grp->IsEmpty()) continue;
2838 grp->SMDSGroup().Remove(removeelem);
2843 //================================================================================
2845 * \brief Replace elemToRm by elemToAdd in the all groups
2847 //================================================================================
2849 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2850 const SMDS_MeshElement* elemToAdd,
2851 SMESHDS_Mesh * aMesh)
2853 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2854 if (!groups.empty()) {
2855 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2856 for ( ; grIt != groups.end(); grIt++ ) {
2857 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2858 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2859 group->SMDSGroup().Add( elemToAdd );
2864 //================================================================================
2866 * \brief Replace elemToRm by elemToAdd in the all groups
2868 //================================================================================
2870 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2871 const vector<const SMDS_MeshElement*>& elemToAdd,
2872 SMESHDS_Mesh * aMesh)
2874 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2875 if (!groups.empty())
2877 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2878 for ( ; grIt != groups.end(); grIt++ ) {
2879 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2880 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2881 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2882 group->SMDSGroup().Add( elemToAdd[ i ] );
2887 //=======================================================================
2888 //function : QuadToTri
2889 //purpose : Cut quadrangles into triangles.
2890 // theCrit is used to select a diagonal to cut
2891 //=======================================================================
2893 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2894 const bool the13Diag)
2897 myLastCreatedElems.reserve( theElems.size() * 2 );
2899 SMESHDS_Mesh * aMesh = GetMeshDS();
2900 Handle(Geom_Surface) surface;
2901 SMESH_MesherHelper helper( *GetMesh() );
2903 TIDSortedElemSet::iterator itElem;
2904 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2906 const SMDS_MeshElement* elem = *itElem;
2907 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2910 if ( elem->NbNodes() == 4 ) {
2911 // retrieve element nodes
2912 const SMDS_MeshNode* aNodes [4];
2913 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2915 while ( itN->more() )
2916 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2918 int aShapeId = FindShape( elem );
2919 const SMDS_MeshElement* newElem1 = 0;
2920 const SMDS_MeshElement* newElem2 = 0;
2922 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2923 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2926 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2927 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2929 myLastCreatedElems.push_back(newElem1);
2930 myLastCreatedElems.push_back(newElem2);
2931 // put a new triangle on the same shape and add to the same groups
2934 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2935 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2937 AddToSameGroups( newElem1, elem, aMesh );
2938 AddToSameGroups( newElem2, elem, aMesh );
2939 aMesh->RemoveElement( elem );
2942 // Quadratic quadrangle
2944 else if ( elem->NbNodes() >= 8 )
2946 // get surface elem is on
2947 int aShapeId = FindShape( elem );
2948 if ( aShapeId != helper.GetSubShapeID() ) {
2952 shape = aMesh->IndexToShape( aShapeId );
2953 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2954 TopoDS_Face face = TopoDS::Face( shape );
2955 surface = BRep_Tool::Surface( face );
2956 if ( !surface.IsNull() )
2957 helper.SetSubShape( shape );
2961 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
2962 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2963 for ( int i = 0; itN->more(); ++i )
2964 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2966 const SMDS_MeshNode* centrNode = aNodes[8];
2967 if ( centrNode == 0 )
2969 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2970 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
2972 myLastCreatedNodes.push_back(centrNode);
2975 // create a new element
2976 const SMDS_MeshElement* newElem1 = 0;
2977 const SMDS_MeshElement* newElem2 = 0;
2979 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2980 aNodes[6], aNodes[7], centrNode );
2981 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2982 centrNode, aNodes[4], aNodes[5] );
2985 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2986 aNodes[7], aNodes[4], centrNode );
2987 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2988 centrNode, aNodes[5], aNodes[6] );
2990 myLastCreatedElems.push_back(newElem1);
2991 myLastCreatedElems.push_back(newElem2);
2992 // put a new triangle on the same shape and add to the same groups
2995 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2996 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2998 AddToSameGroups( newElem1, elem, aMesh );
2999 AddToSameGroups( newElem2, elem, aMesh );
3000 aMesh->RemoveElement( elem );
3007 //=======================================================================
3008 //function : getAngle
3010 //=======================================================================
3012 double getAngle(const SMDS_MeshElement * tr1,
3013 const SMDS_MeshElement * tr2,
3014 const SMDS_MeshNode * n1,
3015 const SMDS_MeshNode * n2)
3017 double angle = 2. * M_PI; // bad angle
3020 SMESH::Controls::TSequenceOfXYZ P1, P2;
3021 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3022 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3025 if(!tr1->IsQuadratic())
3026 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3028 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3029 if ( N1.SquareMagnitude() <= gp::Resolution() )
3031 if(!tr2->IsQuadratic())
3032 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3034 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3035 if ( N2.SquareMagnitude() <= gp::Resolution() )
3038 // find the first diagonal node n1 in the triangles:
3039 // take in account a diagonal link orientation
3040 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3041 for ( int t = 0; t < 2; t++ ) {
3042 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3043 int i = 0, iDiag = -1;
3044 while ( it->more()) {
3045 const SMDS_MeshElement *n = it->next();
3046 if ( n == n1 || n == n2 ) {
3050 if ( i - iDiag == 1 )
3051 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3060 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3063 angle = N1.Angle( N2 );
3068 // =================================================
3069 // class generating a unique ID for a pair of nodes
3070 // and able to return nodes by that ID
3071 // =================================================
3075 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3076 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3079 long GetLinkID (const SMDS_MeshNode * n1,
3080 const SMDS_MeshNode * n2) const
3082 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3085 bool GetNodes (const long theLinkID,
3086 const SMDS_MeshNode* & theNode1,
3087 const SMDS_MeshNode* & theNode2) const
3089 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3090 if ( !theNode1 ) return false;
3091 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3092 if ( !theNode2 ) return false;
3098 const SMESHDS_Mesh* myMesh;
3103 //=======================================================================
3104 //function : TriToQuad
3105 //purpose : Fuse neighbour triangles into quadrangles.
3106 // theCrit is used to select a neighbour to fuse with.
3107 // theMaxAngle is a max angle between element normals at which
3108 // fusion is still performed.
3109 //=======================================================================
3111 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3112 SMESH::Controls::NumericalFunctorPtr theCrit,
3113 const double theMaxAngle)
3116 myLastCreatedElems.reserve( theElems.size() / 2 );
3118 if ( !theCrit.get() )
3121 SMESHDS_Mesh * aMesh = GetMeshDS();
3123 // Prepare data for algo: build
3124 // 1. map of elements with their linkIDs
3125 // 2. map of linkIDs with their elements
3127 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3128 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3129 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3130 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3132 TIDSortedElemSet::iterator itElem;
3133 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3135 const SMDS_MeshElement* elem = *itElem;
3136 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3137 bool IsTria = ( elem->NbCornerNodes()==3 );
3138 if (!IsTria) continue;
3140 // retrieve element nodes
3141 const SMDS_MeshNode* aNodes [4];
3142 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3145 aNodes[ i++ ] = itN->next();
3146 aNodes[ 3 ] = aNodes[ 0 ];
3149 for ( i = 0; i < 3; i++ ) {
3150 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3151 // check if elements sharing a link can be fused
3152 itLE = mapLi_listEl.find( link );
3153 if ( itLE != mapLi_listEl.end() ) {
3154 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3156 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3157 //if ( FindShape( elem ) != FindShape( elem2 ))
3158 // continue; // do not fuse triangles laying on different shapes
3159 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3160 continue; // avoid making badly shaped quads
3161 (*itLE).second.push_back( elem );
3164 mapLi_listEl[ link ].push_back( elem );
3166 mapEl_setLi [ elem ].insert( link );
3169 // Clean the maps from the links shared by a sole element, ie
3170 // links to which only one element is bound in mapLi_listEl
3172 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3173 int nbElems = (*itLE).second.size();
3174 if ( nbElems < 2 ) {
3175 const SMDS_MeshElement* elem = (*itLE).second.front();
3176 SMESH_TLink link = (*itLE).first;
3177 mapEl_setLi[ elem ].erase( link );
3178 if ( mapEl_setLi[ elem ].empty() )
3179 mapEl_setLi.erase( elem );
3183 // Algo: fuse triangles into quadrangles
3185 while ( ! mapEl_setLi.empty() ) {
3186 // Look for the start element:
3187 // the element having the least nb of shared links
3188 const SMDS_MeshElement* startElem = 0;
3190 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3191 int nbLinks = (*itEL).second.size();
3192 if ( nbLinks < minNbLinks ) {
3193 startElem = (*itEL).first;
3194 minNbLinks = nbLinks;
3195 if ( minNbLinks == 1 )
3200 // search elements to fuse starting from startElem or links of elements
3201 // fused earlyer - startLinks
3202 list< SMESH_TLink > startLinks;
3203 while ( startElem || !startLinks.empty() ) {
3204 while ( !startElem && !startLinks.empty() ) {
3205 // Get an element to start, by a link
3206 SMESH_TLink linkId = startLinks.front();
3207 startLinks.pop_front();
3208 itLE = mapLi_listEl.find( linkId );
3209 if ( itLE != mapLi_listEl.end() ) {
3210 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3211 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3212 for ( ; itE != listElem.end() ; itE++ )
3213 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3215 mapLi_listEl.erase( itLE );
3220 // Get candidates to be fused
3221 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3222 const SMESH_TLink *link12 = 0, *link13 = 0;
3224 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3225 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3226 ASSERT( !setLi.empty() );
3227 set< SMESH_TLink >::iterator itLi;
3228 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3230 const SMESH_TLink & link = (*itLi);
3231 itLE = mapLi_listEl.find( link );
3232 if ( itLE == mapLi_listEl.end() )
3235 const SMDS_MeshElement* elem = (*itLE).second.front();
3237 elem = (*itLE).second.back();
3238 mapLi_listEl.erase( itLE );
3239 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3250 // add other links of elem to list of links to re-start from
3251 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3252 set< SMESH_TLink >::iterator it;
3253 for ( it = links.begin(); it != links.end(); it++ ) {
3254 const SMESH_TLink& link2 = (*it);
3255 if ( link2 != link )
3256 startLinks.push_back( link2 );
3260 // Get nodes of possible quadrangles
3261 const SMDS_MeshNode *n12 [4], *n13 [4];
3262 bool Ok12 = false, Ok13 = false;
3263 const SMDS_MeshNode *linkNode1, *linkNode2;
3265 linkNode1 = link12->first;
3266 linkNode2 = link12->second;
3267 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3271 linkNode1 = link13->first;
3272 linkNode2 = link13->second;
3273 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3277 // Choose a pair to fuse
3278 if ( Ok12 && Ok13 ) {
3279 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3280 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3281 double aBadRate12 = getBadRate( &quad12, theCrit );
3282 double aBadRate13 = getBadRate( &quad13, theCrit );
3283 if ( aBadRate13 < aBadRate12 )
3290 // and remove fused elems and remove links from the maps
3291 mapEl_setLi.erase( tr1 );
3294 mapEl_setLi.erase( tr2 );
3295 mapLi_listEl.erase( *link12 );
3296 if ( tr1->NbNodes() == 3 )
3298 const SMDS_MeshElement* newElem = 0;
3299 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3300 myLastCreatedElems.push_back(newElem);
3301 AddToSameGroups( newElem, tr1, aMesh );
3302 int aShapeId = tr1->getshapeId();
3304 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3305 aMesh->RemoveElement( tr1 );
3306 aMesh->RemoveElement( tr2 );
3309 vector< const SMDS_MeshNode* > N1;
3310 vector< const SMDS_MeshNode* > N2;
3311 getNodesFromTwoTria(tr1,tr2,N1,N2);
3312 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3313 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3314 // i.e. first nodes from both arrays form a new diagonal
3315 const SMDS_MeshNode* aNodes[8];
3324 const SMDS_MeshElement* newElem = 0;
3325 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3326 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3327 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3329 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3330 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3331 myLastCreatedElems.push_back(newElem);
3332 AddToSameGroups( newElem, tr1, aMesh );
3333 int aShapeId = tr1->getshapeId();
3335 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3336 aMesh->RemoveElement( tr1 );
3337 aMesh->RemoveElement( tr2 );
3338 // remove middle node (9)
3339 if ( N1[4]->NbInverseElements() == 0 )
3340 aMesh->RemoveNode( N1[4] );
3341 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3342 aMesh->RemoveNode( N1[6] );
3343 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3344 aMesh->RemoveNode( N2[6] );
3349 mapEl_setLi.erase( tr3 );
3350 mapLi_listEl.erase( *link13 );
3351 if ( tr1->NbNodes() == 3 ) {
3352 const SMDS_MeshElement* newElem = 0;
3353 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3354 myLastCreatedElems.push_back(newElem);
3355 AddToSameGroups( newElem, tr1, aMesh );
3356 int aShapeId = tr1->getshapeId();
3358 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3359 aMesh->RemoveElement( tr1 );
3360 aMesh->RemoveElement( tr3 );
3363 vector< const SMDS_MeshNode* > N1;
3364 vector< const SMDS_MeshNode* > N2;
3365 getNodesFromTwoTria(tr1,tr3,N1,N2);
3366 // now we receive following N1 and N2 (using numeration as above image)
3367 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3368 // i.e. first nodes from both arrays form a new diagonal
3369 const SMDS_MeshNode* aNodes[8];
3378 const SMDS_MeshElement* newElem = 0;
3379 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3380 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3381 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3383 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3384 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3385 myLastCreatedElems.push_back(newElem);
3386 AddToSameGroups( newElem, tr1, aMesh );
3387 int aShapeId = tr1->getshapeId();
3389 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3390 aMesh->RemoveElement( tr1 );
3391 aMesh->RemoveElement( tr3 );
3392 // remove middle node (9)
3393 if ( N1[4]->NbInverseElements() == 0 )
3394 aMesh->RemoveNode( N1[4] );
3395 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3396 aMesh->RemoveNode( N1[6] );
3397 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3398 aMesh->RemoveNode( N2[6] );
3402 // Next element to fuse: the rejected one
3404 startElem = Ok12 ? tr3 : tr2;
3406 } // if ( startElem )
3407 } // while ( startElem || !startLinks.empty() )
3408 } // while ( ! mapEl_setLi.empty() )
3413 //================================================================================
3415 * \brief Return nodes linked to the given one
3416 * \param theNode - the node
3417 * \param linkedNodes - the found nodes
3418 * \param type - the type of elements to check
3420 * Medium nodes are ignored
3422 //================================================================================
3424 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3425 TIDSortedElemSet & linkedNodes,
3426 SMDSAbs_ElementType type )
3428 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3429 while ( elemIt->more() )
3431 const SMDS_MeshElement* elem = elemIt->next();
3432 if(elem->GetType() == SMDSAbs_0DElement)
3435 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3436 if ( elem->GetType() == SMDSAbs_Volume )
3438 SMDS_VolumeTool vol( elem );
3439 while ( nodeIt->more() ) {
3440 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3441 if ( theNode != n && vol.IsLinked( theNode, n ))
3442 linkedNodes.insert( n );
3447 for ( int i = 0; nodeIt->more(); ++i ) {
3448 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3449 if ( n == theNode ) {
3450 int iBefore = i - 1;
3452 if ( elem->IsQuadratic() ) {
3453 int nb = elem->NbNodes() / 2;
3454 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3455 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3457 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3458 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3465 //=======================================================================
3466 //function : laplacianSmooth
3467 //purpose : pulls theNode toward the center of surrounding nodes directly
3468 // connected to that node along an element edge
3469 //=======================================================================
3471 void laplacianSmooth(const SMDS_MeshNode* theNode,
3472 const Handle(Geom_Surface)& theSurface,
3473 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3475 // find surrounding nodes
3477 TIDSortedElemSet nodeSet;
3478 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3480 // compute new coodrs
3482 double coord[] = { 0., 0., 0. };
3483 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3484 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3485 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3486 if ( theSurface.IsNull() ) { // smooth in 3D
3487 coord[0] += node->X();
3488 coord[1] += node->Y();
3489 coord[2] += node->Z();
3491 else { // smooth in 2D
3492 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3493 gp_XY* uv = theUVMap[ node ];
3494 coord[0] += uv->X();
3495 coord[1] += uv->Y();
3498 int nbNodes = nodeSet.size();
3501 coord[0] /= nbNodes;
3502 coord[1] /= nbNodes;
3504 if ( !theSurface.IsNull() ) {
3505 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3506 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3507 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3513 coord[2] /= nbNodes;
3517 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3520 //=======================================================================
3521 //function : centroidalSmooth
3522 //purpose : pulls theNode toward the element-area-weighted centroid of the
3523 // surrounding elements
3524 //=======================================================================
3526 void centroidalSmooth(const SMDS_MeshNode* theNode,
3527 const Handle(Geom_Surface)& theSurface,
3528 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3530 gp_XYZ aNewXYZ(0.,0.,0.);
3531 SMESH::Controls::Area anAreaFunc;
3532 double totalArea = 0.;
3537 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3538 while ( elemIt->more() )
3540 const SMDS_MeshElement* elem = elemIt->next();
3543 gp_XYZ elemCenter(0.,0.,0.);
3544 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3545 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3546 int nn = elem->NbNodes();
3547 if(elem->IsQuadratic()) nn = nn/2;
3549 //while ( itN->more() ) {
3551 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3553 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3554 aNodePoints.push_back( aP );
3555 if ( !theSurface.IsNull() ) { // smooth in 2D
3556 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3557 gp_XY* uv = theUVMap[ aNode ];
3558 aP.SetCoord( uv->X(), uv->Y(), 0. );
3562 double elemArea = anAreaFunc.GetValue( aNodePoints );
3563 totalArea += elemArea;
3565 aNewXYZ += elemCenter * elemArea;
3567 aNewXYZ /= totalArea;
3568 if ( !theSurface.IsNull() ) {
3569 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3570 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3575 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3578 //=======================================================================
3579 //function : getClosestUV
3580 //purpose : return UV of closest projection
3581 //=======================================================================
3583 static bool getClosestUV (Extrema_GenExtPS& projector,
3584 const gp_Pnt& point,
3587 projector.Perform( point );
3588 if ( projector.IsDone() ) {
3589 double u, v, minVal = DBL_MAX;
3590 for ( int i = projector.NbExt(); i > 0; i-- )
3591 if ( projector.SquareDistance( i ) < minVal ) {
3592 minVal = projector.SquareDistance( i );
3593 projector.Point( i ).Parameter( u, v );
3595 result.SetCoord( u, v );
3601 //=======================================================================
3603 //purpose : Smooth theElements during theNbIterations or until a worst
3604 // element has aspect ratio <= theTgtAspectRatio.
3605 // Aspect Ratio varies in range [1.0, inf].
3606 // If theElements is empty, the whole mesh is smoothed.
3607 // theFixedNodes contains additionally fixed nodes. Nodes built
3608 // on edges and boundary nodes are always fixed.
3609 //=======================================================================
3611 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3612 set<const SMDS_MeshNode*> & theFixedNodes,
3613 const SmoothMethod theSmoothMethod,
3614 const int theNbIterations,
3615 double theTgtAspectRatio,
3620 if ( theTgtAspectRatio < 1.0 )
3621 theTgtAspectRatio = 1.0;
3623 const double disttol = 1.e-16;
3625 SMESH::Controls::AspectRatio aQualityFunc;
3627 SMESHDS_Mesh* aMesh = GetMeshDS();
3629 if ( theElems.empty() ) {
3630 // add all faces to theElems
3631 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3632 while ( fIt->more() ) {
3633 const SMDS_MeshElement* face = fIt->next();
3634 theElems.insert( theElems.end(), face );
3637 // get all face ids theElems are on
3638 set< int > faceIdSet;
3639 TIDSortedElemSet::iterator itElem;
3641 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3642 int fId = FindShape( *itElem );
3643 // check that corresponding submesh exists and a shape is face
3645 faceIdSet.find( fId ) == faceIdSet.end() &&
3646 aMesh->MeshElements( fId )) {
3647 TopoDS_Shape F = aMesh->IndexToShape( fId );
3648 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3649 faceIdSet.insert( fId );
3652 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3654 // ===============================================
3655 // smooth elements on each TopoDS_Face separately
3656 // ===============================================
3658 SMESH_MesherHelper helper( *GetMesh() );
3660 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3661 for ( ; fId != faceIdSet.rend(); ++fId )
3663 // get face surface and submesh
3664 Handle(Geom_Surface) surface;
3665 SMESHDS_SubMesh* faceSubMesh = 0;
3668 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3669 bool isUPeriodic = false, isVPeriodic = false;
3672 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3673 surface = BRep_Tool::Surface( face );
3674 faceSubMesh = aMesh->MeshElements( *fId );
3675 fToler2 = BRep_Tool::Tolerance( face );
3676 fToler2 *= fToler2 * 10.;
3677 isUPeriodic = surface->IsUPeriodic();
3678 // if ( isUPeriodic )
3679 // surface->UPeriod();
3680 isVPeriodic = surface->IsVPeriodic();
3681 // if ( isVPeriodic )
3682 // surface->VPeriod();
3683 surface->Bounds( u1, u2, v1, v2 );
3684 helper.SetSubShape( face );
3686 // ---------------------------------------------------------
3687 // for elements on a face, find movable and fixed nodes and
3688 // compute UV for them
3689 // ---------------------------------------------------------
3690 bool checkBoundaryNodes = false;
3691 bool isQuadratic = false;
3692 set<const SMDS_MeshNode*> setMovableNodes;
3693 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3694 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3695 list< const SMDS_MeshElement* > elemsOnFace;
3697 Extrema_GenExtPS projector;
3698 GeomAdaptor_Surface surfAdaptor;
3699 if ( !surface.IsNull() ) {
3700 surfAdaptor.Load( surface );
3701 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3703 int nbElemOnFace = 0;
3704 itElem = theElems.begin();
3705 // loop on not yet smoothed elements: look for elems on a face
3706 while ( itElem != theElems.end() )
3708 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3709 break; // all elements found
3711 const SMDS_MeshElement* elem = *itElem;
3712 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3713 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3717 elemsOnFace.push_back( elem );
3718 theElems.erase( itElem++ );
3722 isQuadratic = elem->IsQuadratic();
3724 // get movable nodes of elem
3725 const SMDS_MeshNode* node;
3726 SMDS_TypeOfPosition posType;
3727 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3728 int nn = 0, nbn = elem->NbNodes();
3729 if(elem->IsQuadratic())
3731 while ( nn++ < nbn ) {
3732 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3733 const SMDS_PositionPtr& pos = node->GetPosition();
3734 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3735 if (posType != SMDS_TOP_EDGE &&
3736 posType != SMDS_TOP_VERTEX &&
3737 theFixedNodes.find( node ) == theFixedNodes.end())
3739 // check if all faces around the node are on faceSubMesh
3740 // because a node on edge may be bound to face
3742 if ( faceSubMesh ) {
3743 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3744 while ( eIt->more() && all ) {
3745 const SMDS_MeshElement* e = eIt->next();
3746 all = faceSubMesh->Contains( e );
3750 setMovableNodes.insert( node );
3752 checkBoundaryNodes = true;
3754 if ( posType == SMDS_TOP_3DSPACE )
3755 checkBoundaryNodes = true;
3758 if ( surface.IsNull() )
3761 // get nodes to check UV
3762 list< const SMDS_MeshNode* > uvCheckNodes;
3763 const SMDS_MeshNode* nodeInFace = 0;
3764 itN = elem->nodesIterator();
3765 nn = 0; nbn = elem->NbNodes();
3766 if(elem->IsQuadratic())
3768 while ( nn++ < nbn ) {
3769 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3770 if ( node->GetPosition()->GetDim() == 2 )
3772 if ( uvMap.find( node ) == uvMap.end() )
3773 uvCheckNodes.push_back( node );
3774 // add nodes of elems sharing node
3775 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3776 // while ( eIt->more() ) {
3777 // const SMDS_MeshElement* e = eIt->next();
3778 // if ( e != elem ) {
3779 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3780 // while ( nIt->more() ) {
3781 // const SMDS_MeshNode* n =
3782 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3783 // if ( uvMap.find( n ) == uvMap.end() )
3784 // uvCheckNodes.push_back( n );
3790 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3791 for ( ; n != uvCheckNodes.end(); ++n ) {
3794 const SMDS_PositionPtr& pos = node->GetPosition();
3795 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3799 bool toCheck = true;
3800 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3802 // compute not existing UV
3803 bool project = ( posType == SMDS_TOP_3DSPACE );
3804 // double dist1 = DBL_MAX, dist2 = 0;
3805 // if ( posType != SMDS_TOP_3DSPACE ) {
3806 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3807 // project = dist1 > fToler2;
3809 if ( project ) { // compute new UV
3811 gp_Pnt pNode = SMESH_NodeXYZ( node );
3812 if ( !getClosestUV( projector, pNode, newUV )) {
3813 MESSAGE("Node Projection Failed " << node);
3817 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3819 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3821 // if ( posType != SMDS_TOP_3DSPACE )
3822 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3823 // if ( dist2 < dist1 )
3827 // store UV in the map
3828 listUV.push_back( uv );
3829 uvMap.insert( make_pair( node, &listUV.back() ));
3831 } // loop on not yet smoothed elements
3833 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3834 checkBoundaryNodes = true;
3836 // fix nodes on mesh boundary
3838 if ( checkBoundaryNodes ) {
3839 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3840 map< SMESH_TLink, int >::iterator link_nb;
3841 // put all elements links to linkNbMap
3842 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3843 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3844 const SMDS_MeshElement* elem = (*elemIt);
3845 int nbn = elem->NbCornerNodes();
3846 // loop on elem links: insert them in linkNbMap
3847 for ( int iN = 0; iN < nbn; ++iN ) {
3848 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3849 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3850 SMESH_TLink link( n1, n2 );
3851 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3855 // remove nodes that are in links encountered only once from setMovableNodes
3856 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3857 if ( link_nb->second == 1 ) {
3858 setMovableNodes.erase( link_nb->first.node1() );
3859 setMovableNodes.erase( link_nb->first.node2() );
3864 // -----------------------------------------------------
3865 // for nodes on seam edge, compute one more UV ( uvMap2 );
3866 // find movable nodes linked to nodes on seam and which
3867 // are to be smoothed using the second UV ( uvMap2 )
3868 // -----------------------------------------------------
3870 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3871 if ( !surface.IsNull() ) {
3872 TopExp_Explorer eExp( face, TopAbs_EDGE );
3873 for ( ; eExp.More(); eExp.Next() ) {
3874 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3875 if ( !BRep_Tool::IsClosed( edge, face ))
3877 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3878 if ( !sm ) continue;
3879 // find out which parameter varies for a node on seam
3882 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3883 if ( pcurve.IsNull() ) continue;
3884 uv1 = pcurve->Value( f );
3886 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3887 if ( pcurve.IsNull() ) continue;
3888 uv2 = pcurve->Value( f );
3889 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3891 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3892 std::swap( uv1, uv2 );
3893 // get nodes on seam and its vertices
3894 list< const SMDS_MeshNode* > seamNodes;
3895 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3896 while ( nSeamIt->more() ) {
3897 const SMDS_MeshNode* node = nSeamIt->next();
3898 if ( !isQuadratic || !IsMedium( node ))
3899 seamNodes.push_back( node );
3901 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3902 for ( ; vExp.More(); vExp.Next() ) {
3903 sm = aMesh->MeshElements( vExp.Current() );
3905 nSeamIt = sm->GetNodes();
3906 while ( nSeamIt->more() )
3907 seamNodes.push_back( nSeamIt->next() );
3910 // loop on nodes on seam
3911 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3912 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3913 const SMDS_MeshNode* nSeam = *noSeIt;
3914 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3915 if ( n_uv == uvMap.end() )
3918 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3919 // set the second UV
3920 listUV.push_back( *n_uv->second );
3921 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3922 if ( uvMap2.empty() )
3923 uvMap2 = uvMap; // copy the uvMap contents
3924 uvMap2[ nSeam ] = &listUV.back();
3926 // collect movable nodes linked to ones on seam in nodesNearSeam
3927 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3928 while ( eIt->more() ) {
3929 const SMDS_MeshElement* e = eIt->next();
3930 int nbUseMap1 = 0, nbUseMap2 = 0;
3931 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3932 int nn = 0, nbn = e->NbNodes();
3933 if(e->IsQuadratic()) nbn = nbn/2;
3934 while ( nn++ < nbn )
3936 const SMDS_MeshNode* n =
3937 static_cast<const SMDS_MeshNode*>( nIt->next() );
3939 setMovableNodes.find( n ) == setMovableNodes.end() )
3941 // add only nodes being closer to uv2 than to uv1
3942 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3943 // 0.5 * ( n->Y() + nSeam->Y() ),
3944 // 0.5 * ( n->Z() + nSeam->Z() ));
3946 // getClosestUV( projector, pMid, uv );
3947 double x = uvMap[ n ]->Coord( iPar );
3948 if ( Abs( uv1.Coord( iPar ) - x ) >
3949 Abs( uv2.Coord( iPar ) - x )) {
3950 nodesNearSeam.insert( n );
3956 // for centroidalSmooth all element nodes must
3957 // be on one side of a seam
3958 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3959 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3961 while ( nn++ < nbn ) {
3962 const SMDS_MeshNode* n =
3963 static_cast<const SMDS_MeshNode*>( nIt->next() );
3964 setMovableNodes.erase( n );
3968 } // loop on nodes on seam
3969 } // loop on edge of a face
3970 } // if ( !face.IsNull() )
3972 if ( setMovableNodes.empty() ) {
3973 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3974 continue; // goto next face
3982 double maxRatio = -1., maxDisplacement = -1.;
3983 set<const SMDS_MeshNode*>::iterator nodeToMove;
3984 for ( it = 0; it < theNbIterations; it++ ) {
3985 maxDisplacement = 0.;
3986 nodeToMove = setMovableNodes.begin();
3987 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3988 const SMDS_MeshNode* node = (*nodeToMove);
3989 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3992 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3993 if ( theSmoothMethod == LAPLACIAN )
3994 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3996 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3998 // node displacement
3999 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4000 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4001 if ( aDispl > maxDisplacement )
4002 maxDisplacement = aDispl;
4004 // no node movement => exit
4005 //if ( maxDisplacement < 1.e-16 ) {
4006 if ( maxDisplacement < disttol ) {
4007 MESSAGE("-- no node movement --");
4011 // check elements quality
4013 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4014 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4015 const SMDS_MeshElement* elem = (*elemIt);
4016 if ( !elem || elem->GetType() != SMDSAbs_Face )
4018 SMESH::Controls::TSequenceOfXYZ aPoints;
4019 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4020 double aValue = aQualityFunc.GetValue( aPoints );
4021 if ( aValue > maxRatio )
4025 if ( maxRatio <= theTgtAspectRatio ) {
4026 //MESSAGE("-- quality achieved --");
4029 if (it+1 == theNbIterations) {
4030 //MESSAGE("-- Iteration limit exceeded --");
4032 } // smoothing iterations
4034 // MESSAGE(" Face id: " << *fId <<
4035 // " Nb iterstions: " << it <<
4036 // " Displacement: " << maxDisplacement <<
4037 // " Aspect Ratio " << maxRatio);
4039 // ---------------------------------------
4040 // new nodes positions are computed,
4041 // record movement in DS and set new UV
4042 // ---------------------------------------
4043 nodeToMove = setMovableNodes.begin();
4044 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4045 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4046 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4047 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4048 if ( node_uv != uvMap.end() ) {
4049 gp_XY* uv = node_uv->second;
4051 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4055 // move medium nodes of quadratic elements
4058 vector<const SMDS_MeshNode*> nodes;
4060 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4061 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4063 const SMDS_MeshElement* QF = *elemIt;
4064 if ( QF->IsQuadratic() )
4066 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4067 SMDS_MeshElement::iterator() );
4068 nodes.push_back( nodes[0] );
4070 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4072 if ( !surface.IsNull() )
4074 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4075 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4076 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4077 xyz = surface->Value( uv.X(), uv.Y() );
4080 xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4082 if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4083 // we have to move a medium node
4084 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4090 } // loop on face ids
4096 //=======================================================================
4097 //function : isReverse
4098 //purpose : Return true if normal of prevNodes is not co-directied with
4099 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4100 // iNotSame is where prevNodes and nextNodes are different.
4101 // If result is true then future volume orientation is OK
4102 //=======================================================================
4104 bool isReverse(const SMDS_MeshElement* face,
4105 const vector<const SMDS_MeshNode*>& prevNodes,
4106 const vector<const SMDS_MeshNode*>& nextNodes,
4110 SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4111 SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4112 gp_XYZ extrDir( pN - pP ), faceNorm;
4113 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4115 return faceNorm * extrDir < 0.0;
4118 //================================================================================
4120 * \brief Assure that theElemSets[0] holds elements, not nodes
4122 //================================================================================
4124 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4126 if ( !theElemSets[0].empty() &&
4127 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4129 std::swap( theElemSets[0], theElemSets[1] );
4131 else if ( !theElemSets[1].empty() &&
4132 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4134 std::swap( theElemSets[0], theElemSets[1] );
4139 //=======================================================================
4141 * \brief Create elements by sweeping an element
4142 * \param elem - element to sweep
4143 * \param newNodesItVec - nodes generated from each node of the element
4144 * \param newElems - generated elements
4145 * \param nbSteps - number of sweeping steps
4146 * \param srcElements - to append elem for each generated element
4148 //=======================================================================
4150 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4151 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4152 list<const SMDS_MeshElement*>& newElems,
4153 const size_t nbSteps,
4154 SMESH_SequenceOfElemPtr& srcElements)
4156 SMESHDS_Mesh* aMesh = GetMeshDS();
4158 const int nbNodes = elem->NbNodes();
4159 const int nbCorners = elem->NbCornerNodes();
4160 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4161 polyhedron creation !!! */
4162 // Loop on elem nodes:
4163 // find new nodes and detect same nodes indices
4164 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4165 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4166 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4167 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4169 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4170 vector<int> sames(nbNodes);
4171 vector<bool> isSingleNode(nbNodes);
4173 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4174 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4175 const SMDS_MeshNode* node = nnIt->first;
4176 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4177 if ( listNewNodes.empty() )
4180 itNN [ iNode ] = listNewNodes.begin();
4181 prevNod[ iNode ] = node;
4182 nextNod[ iNode ] = listNewNodes.front();
4184 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4185 corner node of linear */
4186 if ( prevNod[ iNode ] != nextNod [ iNode ])
4187 nbDouble += !isSingleNode[iNode];
4189 if( iNode < nbCorners ) { // check corners only
4190 if ( prevNod[ iNode ] == nextNod [ iNode ])
4191 sames[nbSame++] = iNode;
4193 iNotSameNode = iNode;
4197 if ( nbSame == nbNodes || nbSame > 2) {
4198 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4202 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4204 // fix nodes order to have bottom normal external
4205 if ( baseType == SMDSEntity_Polygon )
4207 std::reverse( itNN.begin(), itNN.end() );
4208 std::reverse( prevNod.begin(), prevNod.end() );
4209 std::reverse( midlNod.begin(), midlNod.end() );
4210 std::reverse( nextNod.begin(), nextNod.end() );
4211 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4215 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4216 SMDS_MeshCell::applyInterlace( ind, itNN );
4217 SMDS_MeshCell::applyInterlace( ind, prevNod );
4218 SMDS_MeshCell::applyInterlace( ind, nextNod );
4219 SMDS_MeshCell::applyInterlace( ind, midlNod );
4220 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4223 sames[nbSame] = iNotSameNode;
4224 for ( int j = 0; j <= nbSame; ++j )
4225 for ( size_t i = 0; i < ind.size(); ++i )
4226 if ( ind[i] == sames[j] )
4231 iNotSameNode = sames[nbSame];
4235 else if ( elem->GetType() == SMDSAbs_Edge )
4237 // orient a new face same as adjacent one
4239 const SMDS_MeshElement* e;
4240 TIDSortedElemSet dummy;
4241 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4242 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4243 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4245 // there is an adjacent face, check order of nodes in it
4246 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4249 std::swap( itNN[0], itNN[1] );
4250 std::swap( prevNod[0], prevNod[1] );
4251 std::swap( nextNod[0], nextNod[1] );
4252 std::swap( isSingleNode[0], isSingleNode[1] );
4254 sames[0] = 1 - sames[0];
4255 iNotSameNode = 1 - iNotSameNode;
4260 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4262 iSameNode = sames[ nbSame-1 ];
4263 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4264 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4265 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4268 if ( baseType == SMDSEntity_Polygon )
4270 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4271 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4273 else if ( baseType == SMDSEntity_Quad_Polygon )
4275 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4276 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4279 // make new elements
4280 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4283 for ( iNode = 0; iNode < nbNodes; iNode++ )
4285 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4286 nextNod[ iNode ] = *itNN[ iNode ]++;
4289 SMDS_MeshElement* aNewElem = 0;
4290 /*if(!elem->IsPoly())*/ {
4291 switch ( baseType ) {
4293 case SMDSEntity_Node: { // sweep NODE
4294 if ( nbSame == 0 ) {
4295 if ( isSingleNode[0] )
4296 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4298 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4304 case SMDSEntity_Edge: { // sweep EDGE
4305 if ( nbDouble == 0 )
4307 if ( nbSame == 0 ) // ---> quadrangle
4308 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4309 nextNod[ 1 ], nextNod[ 0 ] );
4310 else // ---> triangle
4311 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4312 nextNod[ iNotSameNode ] );
4314 else // ---> polygon
4316 vector<const SMDS_MeshNode*> poly_nodes;
4317 poly_nodes.push_back( prevNod[0] );
4318 poly_nodes.push_back( prevNod[1] );
4319 if ( prevNod[1] != nextNod[1] )
4321 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4322 poly_nodes.push_back( nextNod[1] );
4324 if ( prevNod[0] != nextNod[0] )
4326 poly_nodes.push_back( nextNod[0] );
4327 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4329 switch ( poly_nodes.size() ) {
4331 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4334 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4335 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4338 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4343 case SMDSEntity_Triangle: // TRIANGLE --->
4345 if ( nbDouble > 0 ) break;
4346 if ( nbSame == 0 ) // ---> pentahedron
4347 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4348 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4350 else if ( nbSame == 1 ) // ---> pyramid
4351 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4352 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4353 nextNod[ iSameNode ]);
4355 else // 2 same nodes: ---> tetrahedron
4356 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4357 nextNod[ iNotSameNode ]);
4360 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4364 if ( nbDouble+nbSame == 2 )
4366 if(nbSame==0) { // ---> quadratic quadrangle
4367 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4368 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4370 else { //(nbSame==1) // ---> quadratic triangle
4372 return; // medium node on axis
4374 else if(sames[0]==0)
4375 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4376 prevNod[2], midlNod[1], nextNod[2] );
4378 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4379 prevNod[2], nextNod[2], midlNod[0]);
4382 else if ( nbDouble == 3 )
4384 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4385 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4386 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4393 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4394 if ( nbDouble > 0 ) break;
4396 if ( nbSame == 0 ) // ---> hexahedron
4397 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4398 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4400 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4401 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4402 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4403 nextNod[ iSameNode ]);
4404 newElems.push_back( aNewElem );
4405 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4406 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4407 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4409 else if ( nbSame == 2 ) { // ---> pentahedron
4410 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4411 // iBeforeSame is same too
4412 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4413 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4414 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4416 // iAfterSame is same too
4417 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4418 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4419 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4423 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4424 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4425 if ( nbDouble+nbSame != 3 ) break;
4427 // ---> pentahedron with 15 nodes
4428 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4429 nextNod[0], nextNod[1], nextNod[2],
4430 prevNod[3], prevNod[4], prevNod[5],
4431 nextNod[3], nextNod[4], nextNod[5],
4432 midlNod[0], midlNod[1], midlNod[2]);
4434 else if(nbSame==1) {
4435 // ---> 2d order pyramid of 13 nodes
4436 int apex = iSameNode;
4437 int i0 = ( apex + 1 ) % nbCorners;
4438 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4442 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4443 nextNod[i0], nextNod[i1], prevNod[apex],
4444 prevNod[i01], midlNod[i0],
4445 nextNod[i01], midlNod[i1],
4446 prevNod[i1a], prevNod[i0a],
4447 nextNod[i0a], nextNod[i1a]);
4449 else if(nbSame==2) {
4450 // ---> 2d order tetrahedron of 10 nodes
4451 int n1 = iNotSameNode;
4452 int n2 = ( n1 + 1 ) % nbCorners;
4453 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4457 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4458 prevNod[n12], prevNod[n23], prevNod[n31],
4459 midlNod[n1], nextNod[n12], nextNod[n31]);
4463 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4465 if ( nbDouble != 4 ) break;
4466 // ---> hexahedron with 20 nodes
4467 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4468 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4469 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4470 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4471 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4473 else if(nbSame==1) {
4474 // ---> pyramid + pentahedron - can not be created since it is needed
4475 // additional middle node at the center of face
4476 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4479 else if( nbSame == 2 ) {
4480 if ( nbDouble != 2 ) break;
4481 // ---> 2d order Pentahedron with 15 nodes
4483 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4484 // iBeforeSame is same too
4491 // iAfterSame is same too
4501 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4502 prevNod[n4], prevNod[n5], nextNod[n5],
4503 prevNod[n12], midlNod[n2], nextNod[n12],
4504 prevNod[n45], midlNod[n5], nextNod[n45],
4505 prevNod[n14], prevNod[n25], nextNod[n25]);
4509 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4511 if( nbSame == 0 && nbDouble == 9 ) {
4512 // ---> tri-quadratic hexahedron with 27 nodes
4513 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4514 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4515 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4516 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4517 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4518 prevNod[8], // bottom center
4519 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4520 nextNod[8], // top center
4521 midlNod[8]);// elem center
4529 case SMDSEntity_Polygon: { // sweep POLYGON
4531 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4532 // ---> hexagonal prism
4533 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4534 prevNod[3], prevNod[4], prevNod[5],
4535 nextNod[0], nextNod[1], nextNod[2],
4536 nextNod[3], nextNod[4], nextNod[5]);
4540 case SMDSEntity_Ball:
4545 } // switch ( baseType )
4548 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4550 if ( baseType != SMDSEntity_Polygon )
4552 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4553 SMDS_MeshCell::applyInterlace( ind, prevNod );
4554 SMDS_MeshCell::applyInterlace( ind, nextNod );
4555 SMDS_MeshCell::applyInterlace( ind, midlNod );
4556 SMDS_MeshCell::applyInterlace( ind, itNN );
4557 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4558 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4560 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4561 vector<int> quantities (nbNodes + 2);
4562 polyedre_nodes.clear();
4566 for (int inode = 0; inode < nbNodes; inode++)
4567 polyedre_nodes.push_back( prevNod[inode] );
4568 quantities.push_back( nbNodes );
4571 polyedre_nodes.push_back( nextNod[0] );
4572 for (int inode = nbNodes; inode-1; --inode )
4573 polyedre_nodes.push_back( nextNod[inode-1] );
4574 quantities.push_back( nbNodes );
4582 const int iQuad = elem->IsQuadratic();
4583 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4585 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4586 int inextface = (iface+1+iQuad) % nbNodes;
4587 int imid = (iface+1) % nbNodes;
4588 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4589 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4590 polyedre_nodes.push_back( prevNod[iface] ); // 1
4591 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4593 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4594 polyedre_nodes.push_back( nextNod[iface] ); // 2
4596 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4597 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4599 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4600 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4602 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4603 if ( nbFaceNodes > 2 )
4604 quantities.push_back( nbFaceNodes );
4605 else // degenerated face
4606 polyedre_nodes.resize( prevNbNodes );
4608 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4610 } // try to create a polyherdal prism
4613 newElems.push_back( aNewElem );
4614 myLastCreatedElems.push_back(aNewElem);
4615 srcElements.push_back( elem );
4618 // set new prev nodes
4619 for ( iNode = 0; iNode < nbNodes; iNode++ )
4620 prevNod[ iNode ] = nextNod[ iNode ];
4625 //=======================================================================
4627 * \brief Create 1D and 2D elements around swept elements
4628 * \param mapNewNodes - source nodes and ones generated from them
4629 * \param newElemsMap - source elements and ones generated from them
4630 * \param elemNewNodesMap - nodes generated from each node of each element
4631 * \param elemSet - all swept elements
4632 * \param nbSteps - number of sweeping steps
4633 * \param srcElements - to append elem for each generated element
4635 //=======================================================================
4637 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4638 TTElemOfElemListMap & newElemsMap,
4639 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4640 TIDSortedElemSet& elemSet,
4642 SMESH_SequenceOfElemPtr& srcElements)
4644 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4645 SMESHDS_Mesh* aMesh = GetMeshDS();
4647 // Find nodes belonging to only one initial element - sweep them into edges.
4649 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4650 for ( ; nList != mapNewNodes.end(); nList++ )
4652 const SMDS_MeshNode* node =
4653 static_cast<const SMDS_MeshNode*>( nList->first );
4654 if ( newElemsMap.count( node ))
4655 continue; // node was extruded into edge
4656 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4657 int nbInitElems = 0;
4658 const SMDS_MeshElement* el = 0;
4659 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4660 while ( eIt->more() && nbInitElems < 2 ) {
4661 const SMDS_MeshElement* e = eIt->next();
4662 SMDSAbs_ElementType type = e->GetType();
4663 if ( type == SMDSAbs_Volume ||
4667 if ( type > highType ) {
4674 if ( nbInitElems == 1 ) {
4675 bool NotCreateEdge = el && el->IsMediumNode(node);
4676 if(!NotCreateEdge) {
4677 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4678 list<const SMDS_MeshElement*> newEdges;
4679 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4684 // Make a ceiling for each element ie an equal element of last new nodes.
4685 // Find free links of faces - make edges and sweep them into faces.
4687 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4689 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4690 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4691 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4693 const SMDS_MeshElement* elem = itElem->first;
4694 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4696 if(itElem->second.size()==0) continue;
4698 const bool isQuadratic = elem->IsQuadratic();
4700 if ( elem->GetType() == SMDSAbs_Edge ) {
4701 // create a ceiling edge
4702 if ( !isQuadratic ) {
4703 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4704 vecNewNodes[ 1 ]->second.back())) {
4705 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4706 vecNewNodes[ 1 ]->second.back()));
4707 srcElements.push_back( elem );
4711 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4712 vecNewNodes[ 1 ]->second.back(),
4713 vecNewNodes[ 2 ]->second.back())) {
4714 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4715 vecNewNodes[ 1 ]->second.back(),
4716 vecNewNodes[ 2 ]->second.back()));
4717 srcElements.push_back( elem );
4721 if ( elem->GetType() != SMDSAbs_Face )
4724 bool hasFreeLinks = false;
4726 TIDSortedElemSet avoidSet;
4727 avoidSet.insert( elem );
4729 set<const SMDS_MeshNode*> aFaceLastNodes;
4730 int iNode, nbNodes = vecNewNodes.size();
4731 if ( !isQuadratic ) {
4732 // loop on the face nodes
4733 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4734 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4735 // look for free links of the face
4736 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4737 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4738 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4739 // check if a link n1-n2 is free
4740 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4741 hasFreeLinks = true;
4742 // make a new edge and a ceiling for a new edge
4743 const SMDS_MeshElement* edge;
4744 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4745 myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4746 srcElements.push_back( myLastCreatedElems.back() );
4748 n1 = vecNewNodes[ iNode ]->second.back();
4749 n2 = vecNewNodes[ iNext ]->second.back();
4750 if ( !aMesh->FindEdge( n1, n2 )) {
4751 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4752 srcElements.push_back( edge );
4757 else { // elem is quadratic face
4758 int nbn = nbNodes/2;
4759 for ( iNode = 0; iNode < nbn; iNode++ ) {
4760 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4761 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4762 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4763 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4764 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4765 // check if a link is free
4766 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4767 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4768 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4769 hasFreeLinks = true;
4770 // make an edge and a ceiling for a new edge
4772 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4773 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4774 srcElements.push_back( elem );
4776 n1 = vecNewNodes[ iNode ]->second.back();
4777 n2 = vecNewNodes[ iNext ]->second.back();
4778 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4779 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4780 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4781 srcElements.push_back( elem );
4785 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4786 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4790 // sweep free links into faces
4792 if ( hasFreeLinks ) {
4793 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4794 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4796 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4797 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4798 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4799 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4800 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4802 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4803 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4804 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4806 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4807 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4808 std::advance( v, volNb );
4809 // find indices of free faces of a volume and their source edges
4810 list< int > freeInd;
4811 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4812 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4813 int iF, nbF = vTool.NbFaces();
4814 for ( iF = 0; iF < nbF; iF ++ ) {
4815 if ( vTool.IsFreeFace( iF ) &&
4816 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4817 initNodeSet != faceNodeSet) // except an initial face
4819 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4821 if ( faceNodeSet == initNodeSetNoCenter )
4823 freeInd.push_back( iF );
4824 // find source edge of a free face iF
4825 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4826 vector<const SMDS_MeshNode*>::iterator lastCommom;
4827 commonNodes.resize( nbNodes, 0 );
4828 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4829 initNodeSet.begin(), initNodeSet.end(),
4830 commonNodes.begin());
4831 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4832 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4834 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4836 if ( !srcEdges.back() )
4838 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4839 << iF << " of volume #" << vTool.ID() << endl;
4844 if ( freeInd.empty() )
4847 // create wall faces for all steps;
4848 // if such a face has been already created by sweep of edge,
4849 // assure that its orientation is OK
4850 for ( int iStep = 0; iStep < nbSteps; iStep++ )
4852 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4853 vTool.SetExternalNormal();
4854 const int nextShift = vTool.IsForward() ? +1 : -1;
4855 list< int >::iterator ind = freeInd.begin();
4856 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4857 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4859 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4860 int nbn = vTool.NbFaceNodes( *ind );
4861 const SMDS_MeshElement * f = 0;
4862 if ( nbn == 3 ) ///// triangle
4864 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4866 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4868 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4870 nodes[ 1 + nextShift ] };
4872 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4874 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4878 else if ( nbn == 4 ) ///// quadrangle
4880 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4882 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4884 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4885 nodes[ 2 ], nodes[ 2+nextShift ] };
4887 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4889 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4890 newOrder[ 2 ], newOrder[ 3 ]));
4893 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4895 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4897 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4899 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4901 nodes[2 + 2*nextShift],
4902 nodes[3 - 2*nextShift],
4904 nodes[3 + 2*nextShift]};
4906 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4908 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4916 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4918 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4919 nodes[1], nodes[3], nodes[5], nodes[7] );
4921 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4923 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4924 nodes[4 - 2*nextShift],
4926 nodes[4 + 2*nextShift],
4928 nodes[5 - 2*nextShift],
4930 nodes[5 + 2*nextShift] };
4932 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4934 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4935 newOrder[ 2 ], newOrder[ 3 ],
4936 newOrder[ 4 ], newOrder[ 5 ],
4937 newOrder[ 6 ], newOrder[ 7 ]));
4940 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4942 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4943 SMDSAbs_Face, /*noMedium=*/false);
4945 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4947 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4948 nodes[4 - 2*nextShift],
4950 nodes[4 + 2*nextShift],
4952 nodes[5 - 2*nextShift],
4954 nodes[5 + 2*nextShift],
4957 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4959 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4960 newOrder[ 2 ], newOrder[ 3 ],
4961 newOrder[ 4 ], newOrder[ 5 ],
4962 newOrder[ 6 ], newOrder[ 7 ],
4966 else //////// polygon
4968 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4969 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4971 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4973 if ( !vTool.IsForward() )
4974 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4976 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4978 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
4982 while ( srcElements.size() < myLastCreatedElems.size() )
4983 srcElements.push_back( *srcEdge );
4985 } // loop on free faces
4987 // go to the next volume
4989 while ( iVol++ < nbVolumesByStep ) v++;
4992 } // loop on volumes of one step
4993 } // sweep free links into faces
4995 // Make a ceiling face with a normal external to a volume
4997 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
4998 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4999 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5001 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5002 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5003 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5007 lastVol.SetExternalNormal();
5008 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5009 const int nbn = lastVol.NbFaceNodes( iF );
5010 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5011 if ( !hasFreeLinks ||
5012 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5014 const vector<int>& interlace =
5015 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5016 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5018 AddElement( nodeVec, anyFace.Init( elem ));
5020 while ( srcElements.size() < myLastCreatedElems.size() )
5021 srcElements.push_back( elem );
5024 } // loop on swept elements
5027 //=======================================================================
5028 //function : RotationSweep
5030 //=======================================================================
5032 SMESH_MeshEditor::PGroupIDs
5033 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5034 const gp_Ax1& theAxis,
5035 const double theAngle,
5036 const int theNbSteps,
5037 const double theTol,
5038 const bool theMakeGroups,
5039 const bool theMakeWalls)
5043 setElemsFirst( theElemSets );
5044 myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5045 myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5047 // source elements for each generated one
5048 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5049 srcElems.reserve( theElemSets[0].size() );
5050 srcNodes.reserve( theElemSets[1].size() );
5053 aTrsf.SetRotation( theAxis, theAngle );
5055 aTrsf2.SetRotation( theAxis, theAngle/2. );
5057 gp_Lin aLine( theAxis );
5058 double aSqTol = theTol * theTol;
5060 SMESHDS_Mesh* aMesh = GetMeshDS();
5062 TNodeOfNodeListMap mapNewNodes;
5063 TElemOfVecOfNnlmiMap mapElemNewNodes;
5064 TTElemOfElemListMap newElemsMap;
5066 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5067 myMesh->NbFaces(ORDER_QUADRATIC) +
5068 myMesh->NbVolumes(ORDER_QUADRATIC) );
5069 // loop on theElemSets
5070 TIDSortedElemSet::iterator itElem;
5071 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5073 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5074 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5075 const SMDS_MeshElement* elem = *itElem;
5076 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5078 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5079 newNodesItVec.reserve( elem->NbNodes() );
5081 // loop on elem nodes
5082 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5083 while ( itN->more() )
5085 const SMDS_MeshNode* node = cast2Node( itN->next() );
5087 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5089 aXYZ.Coord( coord[0], coord[1], coord[2] );
5090 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5092 // check if a node has been already sweeped
5093 TNodeOfNodeListMapItr nIt =
5094 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5095 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5096 if ( listNewNodes.empty() )
5098 // check if we are to create medium nodes between corner ones
5099 bool needMediumNodes = false;
5100 if ( isQuadraticMesh )
5102 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5103 while (it->more() && !needMediumNodes )
5105 const SMDS_MeshElement* invElem = it->next();
5106 if ( invElem != elem && !theElems.count( invElem )) continue;
5107 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5108 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5109 needMediumNodes = true;
5114 const SMDS_MeshNode * newNode = node;
5115 for ( int i = 0; i < theNbSteps; i++ ) {
5117 if ( needMediumNodes ) // create a medium node
5119 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5120 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5121 myLastCreatedNodes.push_back(newNode);
5122 srcNodes.push_back( node );
5123 listNewNodes.push_back( newNode );
5124 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5127 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5129 // create a corner node
5130 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5131 myLastCreatedNodes.push_back(newNode);
5132 srcNodes.push_back( node );
5133 listNewNodes.push_back( newNode );
5136 listNewNodes.push_back( newNode );
5137 // if ( needMediumNodes )
5138 // listNewNodes.push_back( newNode );
5142 newNodesItVec.push_back( nIt );
5144 // make new elements
5145 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5150 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5152 PGroupIDs newGroupIDs;
5153 if ( theMakeGroups )
5154 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5159 //=======================================================================
5160 //function : ExtrusParam
5161 //purpose : standard construction
5162 //=======================================================================
5164 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5165 const int theNbSteps,
5166 const std::list<double>& theScales,
5167 const std::list<double>& theAngles,
5168 const gp_XYZ* theBasePoint,
5170 const double theTolerance):
5172 myBaseP( Precision::Infinite(), 0, 0 ),
5173 myFlags( theFlags ),
5174 myTolerance( theTolerance ),
5175 myElemsToUse( NULL )
5177 mySteps = new TColStd_HSequenceOfReal;
5178 const double stepSize = theStep.Magnitude();
5179 for (int i=1; i<=theNbSteps; i++ )
5180 mySteps->Append( stepSize );
5182 if ( !theScales.empty() )
5184 if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5185 linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5187 // add medium scales
5188 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5189 myScales.reserve( theNbSteps * 2 );
5190 myScales.push_back( 0.5 * ( *s1 + 1. ));
5191 myScales.push_back( *s1 );
5192 for ( ; s2 != theScales.end(); s1 = s2++ )
5194 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5195 myScales.push_back( *s2 );
5199 if ( !theAngles.empty() )
5201 std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5202 if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5203 linearAngleVariation( theNbSteps, angles );
5205 // accumulate angles
5208 std::list<double>::iterator a1 = angles.begin(), a2;
5209 for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5214 while ( nbAngles++ < theNbSteps )
5215 angles.push_back( angles.back() );
5217 // add medium angles
5218 a2 = angles.begin(), a1 = a2++;
5219 myAngles.push_back( 0.5 * *a1 );
5220 myAngles.push_back( *a1 );
5221 for ( ; a2 != angles.end(); a1 = a2++ )
5223 myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5224 myAngles.push_back( *a2 );
5230 myBaseP = *theBasePoint;
5233 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5234 ( theTolerance > 0 ))
5236 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5240 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5244 //=======================================================================
5245 //function : ExtrusParam
5246 //purpose : steps are given explicitly
5247 //=======================================================================
5249 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5250 Handle(TColStd_HSequenceOfReal) theSteps,
5252 const double theTolerance):
5254 mySteps( theSteps ),
5255 myFlags( theFlags ),
5256 myTolerance( theTolerance ),
5257 myElemsToUse( NULL )
5259 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5260 ( theTolerance > 0 ))
5262 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5266 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5270 //=======================================================================
5271 //function : ExtrusParam
5272 //purpose : for extrusion by normal
5273 //=======================================================================
5275 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5276 const int theNbSteps,
5280 mySteps( new TColStd_HSequenceOfReal ),
5281 myFlags( theFlags ),
5283 myElemsToUse( NULL )
5285 for (int i = 0; i < theNbSteps; i++ )
5286 mySteps->Append( theStepSize );
5290 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5294 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5298 //=======================================================================
5299 //function : ExtrusParam
5300 //purpose : for extrusion along path
5301 //=======================================================================
5303 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5304 const gp_Pnt* theBasePoint,
5305 const std::list<double>& theScales,
5306 const bool theMakeGroups )
5307 : myBaseP( Precision::Infinite(), 0, 0 ),
5308 myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5309 myPathPoints( thePoints )
5313 myBaseP = theBasePoint->XYZ();
5316 if ( !theScales.empty() )
5318 // add medium scales
5319 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5320 myScales.reserve( thePoints.size() * 2 );
5321 myScales.push_back( 0.5 * ( 1. + *s1 ));
5322 myScales.push_back( *s1 );
5323 for ( ; s2 != theScales.end(); s1 = s2++ )
5325 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5326 myScales.push_back( *s2 );
5330 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5333 //=======================================================================
5334 //function : ExtrusParam::SetElementsToUse
5335 //purpose : stores elements to use for extrusion by normal, depending on
5336 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5337 // define myBaseP for scaling
5338 //=======================================================================
5340 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5341 const TIDSortedElemSet& nodes )
5343 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5345 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5347 myBaseP.SetCoord( 0.,0.,0. );
5348 TIDSortedElemSet newNodes;
5350 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5351 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5353 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5354 TIDSortedElemSet::const_iterator itElem = elements.begin();
5355 for ( ; itElem != elements.end(); itElem++ )
5357 const SMDS_MeshElement* elem = *itElem;
5358 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5359 while ( itN->more() ) {
5360 const SMDS_MeshElement* node = itN->next();
5361 if ( newNodes.insert( node ).second )
5362 myBaseP += SMESH_NodeXYZ( node );
5366 myBaseP /= newNodes.size();
5370 //=======================================================================
5371 //function : ExtrusParam::beginStepIter
5372 //purpose : prepare iteration on steps
5373 //=======================================================================
5375 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5377 myWithMediumNodes = withMediumNodes;
5381 //=======================================================================
5382 //function : ExtrusParam::moreSteps
5383 //purpose : are there more steps?
5384 //=======================================================================
5386 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5388 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5390 //=======================================================================
5391 //function : ExtrusParam::nextStep
5392 //purpose : returns the next step
5393 //=======================================================================
5395 double SMESH_MeshEditor::ExtrusParam::nextStep()
5398 if ( !myCurSteps.empty() )
5400 res = myCurSteps.back();
5401 myCurSteps.pop_back();
5403 else if ( myNextStep <= mySteps->Length() )
5405 myCurSteps.push_back( mySteps->Value( myNextStep ));
5407 if ( myWithMediumNodes )
5409 myCurSteps.back() /= 2.;
5410 myCurSteps.push_back( myCurSteps.back() );
5417 //=======================================================================
5418 //function : ExtrusParam::makeNodesByDir
5419 //purpose : create nodes for standard extrusion
5420 //=======================================================================
5422 int SMESH_MeshEditor::ExtrusParam::
5423 makeNodesByDir( SMESHDS_Mesh* mesh,
5424 const SMDS_MeshNode* srcNode,
5425 std::list<const SMDS_MeshNode*> & newNodes,
5426 const bool makeMediumNodes)
5428 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5431 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5433 p += myDir.XYZ() * nextStep();
5434 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5435 newNodes.push_back( newNode );
5438 if ( !myScales.empty() || !myAngles.empty() )
5440 gp_XYZ center = myBaseP;
5441 gp_Ax1 ratationAxis( center, myDir );
5444 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5445 size_t i = !makeMediumNodes;
5446 for ( beginStepIter( makeMediumNodes );
5448 ++nIt, i += 1 + !makeMediumNodes )
5450 center += myDir.XYZ() * nextStep();
5452 gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5454 if ( i < myScales.size() )
5456 xyz = ( myScales[i] * ( xyz - center )) + center;
5459 if ( !myAngles.empty() )
5461 rotation.SetRotation( ratationAxis, myAngles[i] );
5462 rotation.Transforms( xyz );
5466 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5474 //=======================================================================
5475 //function : ExtrusParam::makeNodesByDirAndSew
5476 //purpose : create nodes for standard extrusion with sewing
5477 //=======================================================================
5479 int SMESH_MeshEditor::ExtrusParam::
5480 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5481 const SMDS_MeshNode* srcNode,
5482 std::list<const SMDS_MeshNode*> & newNodes,
5483 const bool makeMediumNodes)
5485 gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5488 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5490 P1 += myDir.XYZ() * nextStep();
5492 // try to search in sequence of existing nodes
5493 // if myNodes.size()>0 we 'nave to use given sequence
5494 // else - use all nodes of mesh
5495 const SMDS_MeshNode * node = 0;
5496 if ( myNodes.Length() > 0 )
5498 for ( int i = 1; i <= myNodes.Length(); i++ )
5500 SMESH_NodeXYZ P2 = myNodes.Value(i);
5501 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5503 node = myNodes.Value(i);
5510 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5513 SMESH_NodeXYZ P2 = itn->next();
5514 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5523 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5525 newNodes.push_back( node );
5532 //=======================================================================
5533 //function : ExtrusParam::makeNodesByNormal2D
5534 //purpose : create nodes for extrusion using normals of faces
5535 //=======================================================================
5537 int SMESH_MeshEditor::ExtrusParam::
5538 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5539 const SMDS_MeshNode* srcNode,
5540 std::list<const SMDS_MeshNode*> & newNodes,
5541 const bool makeMediumNodes)
5543 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5545 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5547 // get normals to faces sharing srcNode
5548 vector< gp_XYZ > norms, baryCenters;
5549 gp_XYZ norm, avgNorm( 0,0,0 );
5550 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5551 while ( faceIt->more() )
5553 const SMDS_MeshElement* face = faceIt->next();
5554 if ( myElemsToUse && !myElemsToUse->count( face ))
5556 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5558 norms.push_back( norm );
5560 if ( !alongAvgNorm )
5564 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5565 bc += SMESH_NodeXYZ( nIt->next() );
5566 baryCenters.push_back( bc / nbN );
5571 if ( norms.empty() ) return 0;
5573 double normSize = avgNorm.Modulus();
5574 if ( normSize < std::numeric_limits<double>::min() )
5577 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5580 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5583 avgNorm /= normSize;
5586 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5589 double stepSize = nextStep();
5591 if ( norms.size() > 1 )
5593 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5595 // translate plane of a face
5596 baryCenters[ iF ] += norms[ iF ] * stepSize;
5598 // find point of intersection of the face plane located at baryCenters[ iF ]
5599 // and avgNorm located at pNew
5600 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5601 double dot = ( norms[ iF ] * avgNorm );
5602 if ( dot < std::numeric_limits<double>::min() )
5603 dot = stepSize * 1e-3;
5604 double step = -( norms[ iF ] * pNew + d ) / dot;
5605 pNew += step * avgNorm;
5610 pNew += stepSize * avgNorm;
5614 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5615 newNodes.push_back( newNode );
5620 //=======================================================================
5621 //function : ExtrusParam::makeNodesByNormal1D
5622 //purpose : create nodes for extrusion using normals of edges
5623 //=======================================================================
5625 int SMESH_MeshEditor::ExtrusParam::
5626 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5627 const SMDS_MeshNode* srcNode,
5628 std::list<const SMDS_MeshNode*> & newNodes,
5629 const bool makeMediumNodes)
5631 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5635 //=======================================================================
5636 //function : ExtrusParam::makeNodesAlongTrack
5637 //purpose : create nodes for extrusion along path
5638 //=======================================================================
5640 int SMESH_MeshEditor::ExtrusParam::
5641 makeNodesAlongTrack( SMESHDS_Mesh* mesh,
5642 const SMDS_MeshNode* srcNode,
5643 std::list<const SMDS_MeshNode*> & newNodes,
5644 const bool makeMediumNodes)
5646 const Standard_Real aTolAng=1.e-4;
5648 gp_Pnt aV0x = myBaseP;
5649 gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
5651 const PathPoint& aPP0 = myPathPoints[0];
5652 gp_Pnt aP0x = aPP0.myPnt;
5653 gp_Dir aDT0x= aPP0.myTgt;
5655 std::vector< gp_Pnt > centers;
5656 centers.reserve( NbSteps() * 2 );
5658 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5660 for ( size_t j = 1; j < myPathPoints.size(); ++j )
5662 const PathPoint& aPP = myPathPoints[j];
5663 const gp_Pnt& aP1x = aPP.myPnt;
5664 const gp_Dir& aDT1x = aPP.myTgt;
5667 gp_Vec aV01x( aP0x, aP1x );
5668 aTrsf.SetTranslation( aV01x );
5669 gp_Pnt aV1x = aV0x.Transformed( aTrsf );
5670 gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
5672 // rotation 1 [ T1,T0 ]
5673 Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
5674 if ( fabs( aAngleT1T0 ) > aTolAng )
5676 gp_Dir aDT1T0 = aDT1x ^ aDT0x;
5677 aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
5679 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5683 if ( aPP.myAngle != 0. )
5685 aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
5686 aPN1 = aPN1.Transformed( aTrsfRot );
5690 if ( makeMediumNodes )
5692 // create additional node
5693 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
5694 const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
5695 newNodes.push_back( newNode );
5698 const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5699 newNodes.push_back( newNode );
5701 centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
5702 centers.push_back( aV1x );
5711 if ( !myScales.empty() )
5714 std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
5715 for ( size_t i = !makeMediumNodes;
5716 i < myScales.size() && node != newNodes.end();
5717 i += ( 1 + !makeMediumNodes ), ++node )
5719 aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
5720 gp_Pnt aN = SMESH_NodeXYZ( *node );
5721 gp_Pnt aP = aN.Transformed( aTrsfScale );
5722 mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
5726 return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
5729 //=======================================================================
5730 //function : ExtrusionSweep
5732 //=======================================================================
5734 SMESH_MeshEditor::PGroupIDs
5735 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5736 const gp_Vec& theStep,
5737 const int theNbSteps,
5738 TTElemOfElemListMap& newElemsMap,
5740 const double theTolerance)
5742 std::list<double> dummy;
5743 ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
5744 theFlags, theTolerance );
5745 return ExtrusionSweep( theElems, aParams, newElemsMap );
5751 //=======================================================================
5752 //function : getOriFactor
5753 //purpose : Return -1 or 1 depending on if order of given nodes corresponds to
5754 // edge curve orientation
5755 //=======================================================================
5757 double getOriFactor( const TopoDS_Edge& edge,
5758 const SMDS_MeshNode* n1,
5759 const SMDS_MeshNode* n2,
5760 SMESH_MesherHelper& helper)
5762 double u1 = helper.GetNodeU( edge, n1, n2 );
5763 double u2 = helper.GetNodeU( edge, n2, n1 );
5764 return u1 < u2 ? 1. : -1.;
5768 //=======================================================================
5769 //function : ExtrusionSweep
5771 //=======================================================================
5773 SMESH_MeshEditor::PGroupIDs
5774 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5775 ExtrusParam& theParams,
5776 TTElemOfElemListMap& newElemsMap)
5780 setElemsFirst( theElemSets );
5781 myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5782 myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5784 // source elements for each generated one
5785 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5786 srcElems.reserve( theElemSets[0].size() );
5787 srcNodes.reserve( theElemSets[1].size() );
5789 const int nbSteps = theParams.NbSteps();
5790 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5792 TNodeOfNodeListMap mapNewNodes;
5793 TElemOfVecOfNnlmiMap mapElemNewNodes;
5795 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5796 myMesh->NbFaces(ORDER_QUADRATIC) +
5797 myMesh->NbVolumes(ORDER_QUADRATIC) );
5799 TIDSortedElemSet::iterator itElem;
5800 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5802 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5803 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5805 // check element type
5806 const SMDS_MeshElement* elem = *itElem;
5807 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5810 const size_t nbNodes = elem->NbNodes();
5811 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5812 newNodesItVec.reserve( nbNodes );
5814 // loop on elem nodes
5815 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
5816 while ( itN->more() )
5818 // check if a node has been already sweeped
5819 const SMDS_MeshNode* node = itN->next();
5820 TNodeOfNodeListMap::iterator nIt =
5821 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5822 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5823 if ( listNewNodes.empty() )
5827 // check if we are to create medium nodes between corner ones
5828 bool needMediumNodes = false;
5829 if ( isQuadraticMesh )
5831 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5832 while (it->more() && !needMediumNodes )
5834 const SMDS_MeshElement* invElem = it->next();
5835 if ( invElem != elem && !theElems.count( invElem )) continue;
5836 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5837 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5838 needMediumNodes = true;
5841 // create nodes for all steps
5842 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5844 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5845 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5847 myLastCreatedNodes.push_back( *newNodesIt );
5848 srcNodes.push_back( node );
5853 if ( theParams.ToMakeBoundary() )
5855 GetMeshDS()->Modified();
5856 throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
5858 break; // newNodesItVec will be shorter than nbNodes
5861 newNodesItVec.push_back( nIt );
5863 // make new elements
5864 if ( newNodesItVec.size() == nbNodes )
5865 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5869 if ( theParams.ToMakeBoundary() ) {
5870 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5872 PGroupIDs newGroupIDs;
5873 if ( theParams.ToMakeGroups() )
5874 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5879 //=======================================================================
5880 //function : ExtrusionAlongTrack
5882 //=======================================================================
5883 SMESH_MeshEditor::Extrusion_Error
5884 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5885 SMESH_Mesh* theTrackMesh,
5886 SMDS_ElemIteratorPtr theTrackIterator,
5887 const SMDS_MeshNode* theN1,
5888 std::list<double>& theAngles,
5889 const bool theAngleVariation,
5890 std::list<double>& theScales,
5891 const bool theScaleVariation,
5892 const gp_Pnt* theRefPoint,
5893 const bool theMakeGroups)
5898 if ( theElements[0].empty() && theElements[1].empty() )
5899 return EXTR_NO_ELEMENTS;
5901 ASSERT( theTrackMesh );
5902 if ( ! theTrackIterator || !theTrackIterator->more() )
5903 return EXTR_NO_ELEMENTS;
5905 // 2. Get ordered nodes
5906 SMESH_MeshAlgos::TElemGroupVector branchEdges;
5907 SMESH_MeshAlgos::TNodeGroupVector branchNods;
5908 SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
5909 if ( branchEdges.empty() )
5910 return EXTR_PATH_NOT_EDGE;
5912 if ( branchEdges.size() > 1 )
5913 return EXTR_BAD_PATH_SHAPE;
5915 std::vector< const SMDS_MeshNode* >& pathNodes = branchNods[0];
5916 std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
5917 if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
5918 return EXTR_BAD_STARTING_NODE;
5920 if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
5922 // add medium nodes to pathNodes
5923 std::vector< const SMDS_MeshNode* > pathNodes2;
5924 std::vector< const SMDS_MeshElement* > pathEdges2;
5925 pathNodes2.reserve( pathNodes.size() * 2 );
5926 pathEdges2.reserve( pathEdges.size() * 2 );
5927 for ( size_t i = 0; i < pathEdges.size(); ++i )
5929 pathNodes2.push_back( pathNodes[i] );
5930 pathEdges2.push_back( pathEdges[i] );
5931 if ( pathEdges[i]->IsQuadratic() )
5933 pathNodes2.push_back( pathEdges[i]->GetNode(2) );
5934 pathEdges2.push_back( pathEdges[i] );
5937 pathNodes2.push_back( pathNodes.back() );
5938 pathEdges.swap( pathEdges2 );
5939 pathNodes.swap( pathNodes2 );
5942 // 3. Get path data at pathNodes
5944 std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
5946 if ( theAngleVariation )
5947 linearAngleVariation( points.size()-1, theAngles );
5948 if ( theScaleVariation )
5949 linearScaleVariation( points.size()-1, theScales );
5951 theAngles.push_front( 0 ); // for the 1st point that is not transformed
5952 std::list<double>::iterator angle = theAngles.begin();
5954 SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
5956 std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
5957 std::map< int, double >::iterator id2factor;
5958 SMESH_MesherHelper pathHelper( *theTrackMesh );
5959 gp_Pnt p; gp_Vec tangent;
5960 const double tol2 = gp::Resolution() * gp::Resolution();
5962 for ( size_t i = 0; i < pathNodes.size(); ++i )
5964 ExtrusParam::PathPoint & point = points[ i ];
5966 point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
5968 if ( angle != theAngles.end() )
5969 point.myAngle = *angle++;
5971 tangent.SetCoord( 0,0,0 );
5972 const int shapeID = pathNodes[ i ]->GetShapeID();
5973 const TopoDS_Shape& shape = pathMeshDS->IndexToShape( shapeID );
5974 TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
5975 switch ( shapeType )
5979 TopoDS_Edge edge = TopoDS::Edge( shape );
5980 id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
5981 if ( id2factor->second == 0 )
5983 if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
5984 else id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
5986 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
5987 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
5988 curve->D1( u, p, tangent );
5989 tangent *= id2factor->second;
5995 PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
5996 while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
5998 int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
5999 for ( int di = -1; di <= 0; ++di )
6002 if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6004 TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6005 id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6006 if ( id2factor->second == 0 )
6009 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6011 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6013 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6014 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6016 curve->D1( u, p, du );
6017 double size2 = du.SquareMagnitude();
6018 if ( du.SquareMagnitude() > tol2 )
6020 tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6032 for ( int di = -1; di <= 1; di += 2 )
6035 if ( j < pathNodes.size() )
6037 gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6038 double size2 = dir.SquareMagnitude();
6040 tangent += dir.Divided( Sqrt( size2 )) * di;
6044 } // switch ( shapeType )
6046 if ( tangent.SquareMagnitude() < tol2 )
6047 return EXTR_CANT_GET_TANGENT;
6049 point.myTgt = tangent;
6051 } // loop on pathNodes
6054 ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6055 TTElemOfElemListMap newElemsMap;
6057 ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6062 //=======================================================================
6063 //function : linearAngleVariation
6064 //purpose : spread values over nbSteps
6065 //=======================================================================
6067 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6068 list<double>& Angles)
6070 int nbAngles = Angles.size();
6071 if( nbSteps > nbAngles && nbAngles > 0 )
6073 vector<double> theAngles(nbAngles);
6074 theAngles.assign( Angles.begin(), Angles.end() );
6077 double rAn2St = double( nbAngles ) / double( nbSteps );
6078 double angPrev = 0, angle;
6079 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6081 double angCur = rAn2St * ( iSt+1 );
6082 double angCurFloor = floor( angCur );
6083 double angPrevFloor = floor( angPrev );
6084 if ( angPrevFloor == angCurFloor )
6085 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6087 int iP = int( angPrevFloor );
6088 double angPrevCeil = ceil(angPrev);
6089 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6091 int iC = int( angCurFloor );
6092 if ( iC < nbAngles )
6093 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6095 iP = int( angPrevCeil );
6097 angle += theAngles[ iC ];
6099 res.push_back(angle);
6106 //=======================================================================
6107 //function : linearScaleVariation
6108 //purpose : spread values over nbSteps
6109 //=======================================================================
6111 void SMESH_MeshEditor::linearScaleVariation(const int theNbSteps,
6112 std::list<double>& theScales)
6114 int nbScales = theScales.size();
6115 std::vector<double> myScales;
6116 myScales.reserve( theNbSteps );
6117 std::list<double>::const_iterator scale = theScales.begin();
6118 double prevScale = 1.0;
6119 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6121 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6122 int stDelta = Max( 1, iStep - myScales.size());
6123 double scDelta = ( *scale - prevScale ) / stDelta;
6124 for ( int iStep = 0; iStep < stDelta; ++iStep )
6126 myScales.push_back( prevScale + scDelta );
6127 prevScale = myScales.back();
6131 theScales.assign( myScales.begin(), myScales.end() );
6134 //================================================================================
6136 * \brief Move or copy theElements applying theTrsf to their nodes
6137 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6138 * \param theTrsf - transformation to apply
6139 * \param theCopy - if true, create translated copies of theElems
6140 * \param theMakeGroups - if true and theCopy, create translated groups
6141 * \param theTargetMesh - mesh to copy translated elements into
6142 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6144 //================================================================================
6146 SMESH_MeshEditor::PGroupIDs
6147 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6148 const gp_Trsf& theTrsf,
6150 const bool theMakeGroups,
6151 SMESH_Mesh* theTargetMesh)
6154 myLastCreatedElems.reserve( theElems.size() );
6156 bool needReverse = false;
6157 string groupPostfix;
6158 switch ( theTrsf.Form() ) {
6161 groupPostfix = "mirrored";
6164 groupPostfix = "mirrored";
6168 groupPostfix = "mirrored";
6171 groupPostfix = "rotated";
6173 case gp_Translation:
6174 groupPostfix = "translated";
6177 groupPostfix = "scaled";
6179 case gp_CompoundTrsf: // different scale by axis
6180 groupPostfix = "scaled";
6183 needReverse = false;
6184 groupPostfix = "transformed";
6187 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6188 SMESHDS_Mesh* aMesh = GetMeshDS();
6190 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6191 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6192 SMESH_MeshEditor::ElemFeatures elemType;
6194 // map old node to new one
6195 TNodeNodeMap nodeMap;
6197 // elements sharing moved nodes; those of them which have all
6198 // nodes mirrored but are not in theElems are to be reversed
6199 TIDSortedElemSet inverseElemSet;
6201 // source elements for each generated one
6202 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6204 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6205 TIDSortedElemSet orphanNode;
6207 if ( theElems.empty() ) // transform the whole mesh
6210 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6211 while ( eIt->more() ) theElems.insert( eIt->next() );
6213 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6214 while ( nIt->more() )
6216 const SMDS_MeshNode* node = nIt->next();
6217 if ( node->NbInverseElements() == 0)
6218 orphanNode.insert( node );
6222 // loop on elements to transform nodes : first orphan nodes then elems
6223 TIDSortedElemSet::iterator itElem;
6224 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6225 for (int i=0; i<2; i++)
6226 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6228 const SMDS_MeshElement* elem = *itElem;
6232 // loop on elem nodes
6234 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6235 while ( itN->more() )
6237 const SMDS_MeshNode* node = cast2Node( itN->next() );
6238 // check if a node has been already transformed
6239 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6240 nodeMap.insert( make_pair ( node, node ));
6241 if ( !n2n_isnew.second )
6244 node->GetXYZ( coord );
6245 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6246 if ( theTargetMesh ) {
6247 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6248 n2n_isnew.first->second = newNode;
6249 myLastCreatedNodes.push_back(newNode);
6250 srcNodes.push_back( node );
6252 else if ( theCopy ) {
6253 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6254 n2n_isnew.first->second = newNode;
6255 myLastCreatedNodes.push_back(newNode);
6256 srcNodes.push_back( node );
6259 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6260 // node position on shape becomes invalid
6261 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6262 ( SMDS_SpacePosition::originSpacePosition() );
6265 // keep inverse elements
6266 if ( !theCopy && !theTargetMesh && needReverse ) {
6267 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6268 while ( invElemIt->more() ) {
6269 const SMDS_MeshElement* iel = invElemIt->next();
6270 inverseElemSet.insert( iel );
6274 } // loop on elems in { &orphanNode, &theElems };
6276 // either create new elements or reverse mirrored ones
6277 if ( !theCopy && !needReverse && !theTargetMesh )
6280 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6282 // Replicate or reverse elements
6284 std::vector<int> iForw;
6285 vector<const SMDS_MeshNode*> nodes;
6286 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6288 const SMDS_MeshElement* elem = *itElem;
6289 if ( !elem ) continue;
6291 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6292 size_t nbNodes = elem->NbNodes();
6293 if ( geomType == SMDSGeom_NONE ) continue; // node
6295 nodes.resize( nbNodes );
6297 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6299 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6303 bool allTransformed = true;
6304 int nbFaces = aPolyedre->NbFaces();
6305 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6307 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6308 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6310 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6311 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6312 if ( nodeMapIt == nodeMap.end() )
6313 allTransformed = false; // not all nodes transformed
6315 nodes.push_back((*nodeMapIt).second);
6317 if ( needReverse && allTransformed )
6318 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6320 if ( !allTransformed )
6321 continue; // not all nodes transformed
6323 else // ----------------------- the rest element types
6325 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6326 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6327 const vector<int>& i = needReverse ? iRev : iForw;
6329 // find transformed nodes
6331 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6332 while ( itN->more() ) {
6333 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6334 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6335 if ( nodeMapIt == nodeMap.end() )
6336 break; // not all nodes transformed
6337 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6339 if ( iNode != nbNodes )
6340 continue; // not all nodes transformed
6344 // copy in this or a new mesh
6345 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6346 srcElems.push_back( elem );
6349 // reverse element as it was reversed by transformation
6351 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6354 } // loop on elements
6356 if ( editor && editor != this )
6357 myLastCreatedElems.swap( editor->myLastCreatedElems );
6359 PGroupIDs newGroupIDs;
6361 if ( ( theMakeGroups && theCopy ) ||
6362 ( theMakeGroups && theTargetMesh ) )
6363 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6368 //================================================================================
6370 * \brief Make an offset mesh from a source 2D mesh
6371 * \param [in] theElements - source faces
6372 * \param [in] theValue - offset value
6373 * \param [out] theTgtMesh - a mesh to add offset elements to
6374 * \param [in] theMakeGroups - to generate groups
6375 * \return PGroupIDs - IDs of created groups. NULL means failure
6377 //================================================================================
6379 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6380 const double theValue,
6381 SMESH_Mesh* theTgtMesh,
6382 const bool theMakeGroups,
6383 const bool theCopyElements,
6384 const bool theFixSelfIntersection)
6386 SMESHDS_Mesh* meshDS = GetMeshDS();
6387 SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6388 SMESH_MeshEditor tgtEditor( theTgtMesh );
6390 SMDS_ElemIteratorPtr eIt;
6391 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6392 else eIt = SMESHUtils::elemSetIterator( theElements );
6394 SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6395 SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6396 std::unique_ptr< SMDS_Mesh > offsetMesh
6397 ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6398 theFixSelfIntersection,
6399 new2OldFaces, new2OldNodes ));
6400 if ( offsetMesh->NbElements() == 0 )
6401 return PGroupIDs(); // MakeOffset() failed
6404 if ( theTgtMesh == myMesh && !theCopyElements )
6406 // clear the source elements
6407 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6408 else eIt = SMESHUtils::elemSetIterator( theElements );
6409 while ( eIt->more() )
6410 meshDS->RemoveFreeElement( eIt->next(), 0 );
6413 // offsetMesh->Modified();
6414 // offsetMesh->CompactMesh(); // make IDs start from 1
6416 // source elements for each generated one
6417 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6418 srcElems.reserve( new2OldFaces.size() );
6419 srcNodes.reserve( new2OldNodes.size() );
6422 myLastCreatedElems.reserve( new2OldFaces.size() );
6423 myLastCreatedNodes.reserve( new2OldNodes.size() );
6425 // copy offsetMesh to theTgtMesh
6427 int idShift = meshDS->MaxNodeID();
6428 for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6429 if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6432 if ( n->NbInverseElements() > 0 )
6435 const SMDS_MeshNode* n2 =
6436 tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6437 myLastCreatedNodes.push_back( n2 );
6438 srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6442 ElemFeatures elemType;
6443 for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6444 if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6447 elemType.myNodes.clear();
6448 for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6450 const SMDS_MeshNode* n2 = nIt->next();
6451 elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6453 tgtEditor.AddElement( elemType.myNodes, elemType );
6454 srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6457 myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6459 PGroupIDs newGroupIDs;
6460 if ( theMakeGroups )
6461 newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6463 newGroupIDs.reset( new std::list< int > );
6468 //=======================================================================
6470 * \brief Create groups of elements made during transformation
6471 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6472 * \param elemGens - elements making corresponding myLastCreatedElems
6473 * \param postfix - to push_back to names of new groups
6474 * \param targetMesh - mesh to create groups in
6475 * \param topPresent - is there are "top" elements that are created by sweeping
6477 //=======================================================================
6479 SMESH_MeshEditor::PGroupIDs
6480 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6481 const SMESH_SequenceOfElemPtr& elemGens,
6482 const std::string& postfix,
6483 SMESH_Mesh* targetMesh,
6484 const bool topPresent)
6486 PGroupIDs newGroupIDs( new list<int> );
6487 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6489 // Sort existing groups by types and collect their names
6491 // containers to store an old group and generated new ones;
6492 // 1st new group is for result elems of different type than a source one;
6493 // 2nd new group is for same type result elems ("top" group at extrusion)
6495 using boost::make_tuple;
6496 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6497 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6498 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6500 set< string > groupNames;
6502 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6503 if ( !groupIt->more() ) return newGroupIDs;
6505 int newGroupID = mesh->GetGroupIds().back()+1;
6506 while ( groupIt->more() )
6508 SMESH_Group * group = groupIt->next();
6509 if ( !group ) continue;
6510 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6511 if ( !groupDS || groupDS->IsEmpty() ) continue;
6512 groupNames.insert ( group->GetName() );
6513 groupDS->SetStoreName( group->GetName() );
6514 const SMDSAbs_ElementType type = groupDS->GetType();
6515 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6516 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6517 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6518 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6521 // Loop on nodes and elements to add them in new groups
6523 vector< const SMDS_MeshElement* > resultElems;
6524 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6526 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6527 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6528 if ( gens.size() != elems.size() )
6529 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6531 // loop on created elements
6532 for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6534 const SMDS_MeshElement* sourceElem = gens[ iElem ];
6535 if ( !sourceElem ) {
6536 MESSAGE("generateGroups(): NULL source element");
6539 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6540 if ( groupsOldNew.empty() ) { // no groups of this type at all
6541 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6542 ++iElem; // skip all elements made by sourceElem
6545 // collect all elements made by the iElem-th sourceElem
6546 resultElems.clear();
6547 if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6548 if ( resElem != sourceElem )
6549 resultElems.push_back( resElem );
6550 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6551 if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6552 if ( resElem != sourceElem )
6553 resultElems.push_back( resElem );
6555 const SMDS_MeshElement* topElem = 0;
6556 if ( isNodes ) // there must be a top element
6558 topElem = resultElems.back();
6559 resultElems.pop_back();
6563 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6564 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6565 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6567 topElem = *resElemIt;
6568 *resElemIt = 0; // erase *resElemIt
6572 // add resultElems to groups originted from ones the sourceElem belongs to
6573 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6574 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6576 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6577 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6579 // fill in a new group
6580 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6581 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6582 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6584 newGroup.Add( *resElemIt );
6586 // fill a "top" group
6589 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6590 newTopGroup.Add( topElem );
6594 } // loop on created elements
6595 }// loop on nodes and elements
6597 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6599 list<int> topGrouIds;
6600 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6602 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6603 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6604 orderedOldNewGroups[i]->get<2>() };
6605 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6607 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6608 if ( newGroupDS->IsEmpty() )
6610 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6615 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6618 const bool isTop = ( topPresent &&
6619 newGroupDS->GetType() == oldGroupDS->GetType() &&
6622 string name = oldGroupDS->GetStoreName();
6623 { // remove trailing whitespaces (issue 22599)
6624 size_t size = name.size();
6625 while ( size > 1 && isspace( name[ size-1 ]))
6627 if ( size != name.size() )
6629 name.resize( size );
6630 oldGroupDS->SetStoreName( name.c_str() );
6633 if ( !targetMesh ) {
6634 string suffix = ( isTop ? "top": postfix.c_str() );
6638 while ( !groupNames.insert( name ).second ) // name exists
6639 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6644 newGroupDS->SetStoreName( name.c_str() );
6646 // make a SMESH_Groups
6647 mesh->AddGroup( newGroupDS );
6649 topGrouIds.push_back( newGroupDS->GetID() );
6651 newGroupIDs->push_back( newGroupDS->GetID() );
6655 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6660 //================================================================================
6662 * * \brief Return list of group of nodes close to each other within theTolerance
6663 * * Search among theNodes or in the whole mesh if theNodes is empty using
6664 * * an Octree algorithm
6665 * \param [in,out] theNodes - the nodes to treat
6666 * \param [in] theTolerance - the tolerance
6667 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
6668 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
6669 * corner and medium nodes in separate groups
6671 //================================================================================
6673 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6674 const double theTolerance,
6675 TListOfListOfNodes & theGroupsOfNodes,
6676 bool theSeparateCornersAndMedium)
6680 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
6681 myMesh->NbFaces ( ORDER_QUADRATIC ) +
6682 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
6683 theSeparateCornersAndMedium = false;
6685 TIDSortedNodeSet& corners = theNodes;
6686 TIDSortedNodeSet medium;
6688 if ( theNodes.empty() ) // get all nodes in the mesh
6690 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
6691 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
6692 if ( theSeparateCornersAndMedium )
6693 while ( nIt->more() )
6695 const SMDS_MeshNode* n = nIt->next();
6696 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
6697 nodeSet->insert( nodeSet->end(), n );
6700 while ( nIt->more() )
6701 theNodes.insert( theNodes.end(), nIt->next() );
6703 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
6705 TIDSortedNodeSet::iterator nIt = corners.begin();
6706 while ( nIt != corners.end() )
6707 if ( SMESH_MesherHelper::IsMedium( *nIt ))
6709 medium.insert( medium.end(), *nIt );
6710 corners.erase( nIt++ );
6718 if ( !corners.empty() )
6719 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
6720 if ( !medium.empty() )
6721 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
6724 //=======================================================================
6725 //function : SimplifyFace
6726 //purpose : split a chain of nodes into several closed chains
6727 //=======================================================================
6729 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6730 vector<const SMDS_MeshNode *>& poly_nodes,
6731 vector<int>& quantities) const
6733 int nbNodes = faceNodes.size();
6734 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
6738 size_t prevNbQuant = quantities.size();
6740 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
6741 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
6742 map< const SMDS_MeshNode*, int >::iterator nInd;
6744 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
6745 simpleNodes.push_back( faceNodes[0] );
6746 for ( int iCur = 1; iCur < nbNodes; iCur++ )
6748 if ( faceNodes[ iCur ] != simpleNodes.back() )
6750 int index = simpleNodes.size();
6751 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
6752 int prevIndex = nInd->second;
6753 if ( prevIndex < index )
6756 int loopLen = index - prevIndex;
6759 // store the sub-loop
6760 quantities.push_back( loopLen );
6761 for ( int i = prevIndex; i < index; i++ )
6762 poly_nodes.push_back( simpleNodes[ i ]);
6764 simpleNodes.resize( prevIndex+1 );
6768 simpleNodes.push_back( faceNodes[ iCur ]);
6773 if ( simpleNodes.size() > 2 )
6775 quantities.push_back( simpleNodes.size() );
6776 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
6779 return quantities.size() - prevNbQuant;
6782 //=======================================================================
6783 //function : MergeNodes
6784 //purpose : In each group, the cdr of nodes are substituted by the first one
6786 //=======================================================================
6788 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
6789 const bool theAvoidMakingHoles)
6793 SMESHDS_Mesh* mesh = GetMeshDS();
6795 TNodeNodeMap nodeNodeMap; // node to replace - new node
6796 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6797 list< int > rmElemIds, rmNodeIds;
6798 vector< ElemFeatures > newElemDefs;
6800 // Fill nodeNodeMap and elems
6802 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6803 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
6805 list<const SMDS_MeshNode*>& nodes = *grIt;
6806 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6807 const SMDS_MeshNode* nToKeep = *nIt;
6808 for ( ++nIt; nIt != nodes.end(); nIt++ )
6810 const SMDS_MeshNode* nToRemove = *nIt;
6811 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
6812 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6813 while ( invElemIt->more() ) {
6814 const SMDS_MeshElement* elem = invElemIt->next();
6820 // Apply recursive replacements (BUG 0020185)
6821 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
6822 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
6824 const SMDS_MeshNode* nToKeep = nnIt->second;
6825 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
6826 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
6828 nToKeep = nnIt_i->second;
6829 nnIt->second = nToKeep;
6830 nnIt_i = nodeNodeMap.find( nToKeep );
6834 if ( theAvoidMakingHoles )
6836 // find elements whose topology changes
6838 vector<const SMDS_MeshElement*> pbElems;
6839 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6840 for ( ; eIt != elems.end(); ++eIt )
6842 const SMDS_MeshElement* elem = *eIt;
6843 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6844 while ( itN->more() )
6846 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6847 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6848 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
6850 // several nodes of elem stick
6851 pbElems.push_back( elem );
6856 // exclude from merge nodes causing spoiling element
6857 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
6859 bool nodesExcluded = false;
6860 for ( size_t i = 0; i < pbElems.size(); ++i )
6862 size_t prevNbMergeNodes = nodeNodeMap.size();
6863 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
6864 prevNbMergeNodes < nodeNodeMap.size() )
6865 nodesExcluded = true;
6867 if ( !nodesExcluded )
6872 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
6874 const SMDS_MeshNode* nToRemove = nnIt->first;
6875 const SMDS_MeshNode* nToKeep = nnIt->second;
6876 if ( nToRemove != nToKeep )
6878 rmNodeIds.push_back( nToRemove->GetID() );
6879 AddToSameGroups( nToKeep, nToRemove, mesh );
6880 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
6881 // w/o creating node in place of merged ones.
6882 SMDS_PositionPtr pos = nToRemove->GetPosition();
6883 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6884 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6885 sm->SetIsAlwaysComputed( true );
6889 // Change element nodes or remove an element
6891 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6892 for ( ; eIt != elems.end(); eIt++ )
6894 const SMDS_MeshElement* elem = *eIt;
6895 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
6897 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
6899 rmElemIds.push_back( elem->GetID() );
6901 for ( size_t i = 0; i < newElemDefs.size(); ++i )
6903 if ( i > 0 || !mesh->ChangeElementNodes( elem,
6904 & newElemDefs[i].myNodes[0],
6905 newElemDefs[i].myNodes.size() ))
6909 newElemDefs[i].SetID( elem->GetID() );
6910 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6911 if ( !keepElem ) rmElemIds.pop_back();
6915 newElemDefs[i].SetID( -1 );
6917 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
6918 if ( sm && newElem )
6919 sm->AddElement( newElem );
6920 if ( elem != newElem )
6921 ReplaceElemInGroups( elem, newElem, mesh );
6926 // Remove bad elements, then equal nodes (order important)
6927 Remove( rmElemIds, /*isNodes=*/false );
6928 Remove( rmNodeIds, /*isNodes=*/true );
6933 //=======================================================================
6934 //function : applyMerge
6935 //purpose : Compute new connectivity of an element after merging nodes
6936 // \param [in] elems - the element
6937 // \param [out] newElemDefs - definition(s) of result element(s)
6938 // \param [inout] nodeNodeMap - nodes to merge
6939 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
6940 // after merging (but not degenerated), removes nodes causing
6941 // the invalidity from \a nodeNodeMap.
6942 // \return bool - true if the element should be removed
6943 //=======================================================================
6945 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
6946 vector< ElemFeatures >& newElemDefs,
6947 TNodeNodeMap& nodeNodeMap,
6948 const bool avoidMakingHoles )
6950 bool toRemove = false; // to remove elem
6951 int nbResElems = 1; // nb new elements
6953 newElemDefs.resize(nbResElems);
6954 newElemDefs[0].Init( elem );
6955 newElemDefs[0].myNodes.clear();
6957 set<const SMDS_MeshNode*> nodeSet;
6958 vector< const SMDS_MeshNode*> curNodes;
6959 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
6962 const int nbNodes = elem->NbNodes();
6963 SMDSAbs_EntityType entity = elem->GetEntityType();
6965 curNodes.resize( nbNodes );
6966 uniqueNodes.resize( nbNodes );
6967 iRepl.resize( nbNodes );
6968 int iUnique = 0, iCur = 0, nbRepl = 0;
6970 // Get new seq of nodes
6972 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6973 while ( itN->more() )
6975 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6977 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6978 if ( nnIt != nodeNodeMap.end() ) {
6981 curNodes[ iCur ] = n;
6982 bool isUnique = nodeSet.insert( n ).second;
6984 uniqueNodes[ iUnique++ ] = n;
6986 iRepl[ nbRepl++ ] = iCur;
6990 // Analyse element topology after replacement
6992 int nbUniqueNodes = nodeSet.size();
6993 if ( nbNodes != nbUniqueNodes ) // some nodes stick
6998 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7000 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7001 int nbCorners = nbNodes / 2;
7002 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7004 int iNext = ( iCur + 1 ) % nbCorners;
7005 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7007 int iMedium = iCur + nbCorners;
7008 vector< const SMDS_MeshNode* >::iterator i =
7009 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7011 curNodes[ iMedium ]);
7012 if ( i != uniqueNodes.end() )
7015 for ( ; i+1 != uniqueNodes.end(); ++i )
7024 case SMDSEntity_Polygon:
7025 case SMDSEntity_Quad_Polygon: // Polygon
7027 ElemFeatures* elemType = & newElemDefs[0];
7028 const bool isQuad = elemType->myIsQuad;
7030 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7031 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7033 // a polygon can divide into several elements
7034 vector<const SMDS_MeshNode *> polygons_nodes;
7035 vector<int> quantities;
7036 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7037 newElemDefs.resize( nbResElems );
7038 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7040 ElemFeatures* elemType = & newElemDefs[iface];
7041 if ( iface ) elemType->Init( elem );
7043 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7044 int nbNewNodes = quantities[iface];
7045 face_nodes.assign( polygons_nodes.begin() + inode,
7046 polygons_nodes.begin() + inode + nbNewNodes );
7047 inode += nbNewNodes;
7048 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7050 bool isValid = ( nbNewNodes % 2 == 0 );
7051 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7052 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7053 elemType->SetQuad( isValid );
7054 if ( isValid ) // put medium nodes after corners
7055 SMDS_MeshCell::applyInterlaceRev
7056 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7057 nbNewNodes ), face_nodes );
7059 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7061 nbUniqueNodes = newElemDefs[0].myNodes.size();
7065 case SMDSEntity_Polyhedra: // Polyhedral volume
7067 if ( nbUniqueNodes >= 4 )
7069 // each face has to be analyzed in order to check volume validity
7070 if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7072 int nbFaces = aPolyedre->NbFaces();
7074 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7075 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7076 vector<const SMDS_MeshNode *> faceNodes;
7080 for (int iface = 1; iface <= nbFaces; iface++)
7082 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7083 faceNodes.resize( nbFaceNodes );
7084 for (int inode = 1; inode <= nbFaceNodes; inode++)
7086 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7087 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7088 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7089 faceNode = (*nnIt).second;
7090 faceNodes[inode - 1] = faceNode;
7092 SimplifyFace(faceNodes, poly_nodes, quantities);
7095 if ( quantities.size() > 3 )
7097 // TODO: remove coincident faces
7099 nbUniqueNodes = newElemDefs[0].myNodes.size();
7107 // TODO not all the possible cases are solved. Find something more generic?
7108 case SMDSEntity_Edge: //////// EDGE
7109 case SMDSEntity_Triangle: //// TRIANGLE
7110 case SMDSEntity_Quad_Triangle:
7111 case SMDSEntity_Tetra:
7112 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7116 case SMDSEntity_Quad_Edge:
7120 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7122 if ( nbUniqueNodes < 3 )
7124 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7125 toRemove = true; // opposite nodes stick
7130 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7139 if ( nbUniqueNodes == 6 &&
7141 ( nbRepl == 1 || iRepl[1] >= 4 ))
7147 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7156 if ( nbUniqueNodes == 7 &&
7158 ( nbRepl == 1 || iRepl[1] != 8 ))
7164 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7166 if ( nbUniqueNodes == 4 ) {
7167 // ---------------------------------> tetrahedron
7168 if ( curNodes[3] == curNodes[4] &&
7169 curNodes[3] == curNodes[5] ) {
7173 else if ( curNodes[0] == curNodes[1] &&
7174 curNodes[0] == curNodes[2] ) {
7175 // bottom nodes stick: set a top before
7176 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7177 uniqueNodes[ 0 ] = curNodes [ 5 ];
7178 uniqueNodes[ 1 ] = curNodes [ 4 ];
7179 uniqueNodes[ 2 ] = curNodes [ 3 ];
7182 else if (( curNodes[0] == curNodes[3] ) +
7183 ( curNodes[1] == curNodes[4] ) +
7184 ( curNodes[2] == curNodes[5] ) == 2 ) {
7185 // a lateral face turns into a line
7189 else if ( nbUniqueNodes == 5 ) {
7190 // PENTAHEDRON --------------------> pyramid
7191 if ( curNodes[0] == curNodes[3] )
7193 uniqueNodes[ 0 ] = curNodes[ 1 ];
7194 uniqueNodes[ 1 ] = curNodes[ 4 ];
7195 uniqueNodes[ 2 ] = curNodes[ 5 ];
7196 uniqueNodes[ 3 ] = curNodes[ 2 ];
7197 uniqueNodes[ 4 ] = curNodes[ 0 ];
7200 if ( curNodes[1] == curNodes[4] )
7202 uniqueNodes[ 0 ] = curNodes[ 0 ];
7203 uniqueNodes[ 1 ] = curNodes[ 2 ];
7204 uniqueNodes[ 2 ] = curNodes[ 5 ];
7205 uniqueNodes[ 3 ] = curNodes[ 3 ];
7206 uniqueNodes[ 4 ] = curNodes[ 1 ];
7209 if ( curNodes[2] == curNodes[5] )
7211 uniqueNodes[ 0 ] = curNodes[ 0 ];
7212 uniqueNodes[ 1 ] = curNodes[ 3 ];
7213 uniqueNodes[ 2 ] = curNodes[ 4 ];
7214 uniqueNodes[ 3 ] = curNodes[ 1 ];
7215 uniqueNodes[ 4 ] = curNodes[ 2 ];
7221 case SMDSEntity_Hexa:
7223 //////////////////////////////////// HEXAHEDRON
7224 SMDS_VolumeTool hexa (elem);
7225 hexa.SetExternalNormal();
7226 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7227 //////////////////////// HEX ---> tetrahedron
7228 for ( int iFace = 0; iFace < 6; iFace++ ) {
7229 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7230 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7231 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7232 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7233 // one face turns into a point ...
7234 int pickInd = ind[ 0 ];
7235 int iOppFace = hexa.GetOppFaceIndex( iFace );
7236 ind = hexa.GetFaceNodesIndices( iOppFace );
7238 uniqueNodes.clear();
7239 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7240 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7243 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7245 if ( nbStick == 1 ) {
7246 // ... and the opposite one - into a triangle.
7248 uniqueNodes.push_back( curNodes[ pickInd ]);
7255 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7256 //////////////////////// HEX ---> prism
7257 int nbTria = 0, iTria[3];
7258 const int *ind; // indices of face nodes
7259 // look for triangular faces
7260 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7261 ind = hexa.GetFaceNodesIndices( iFace );
7262 TIDSortedNodeSet faceNodes;
7263 for ( iCur = 0; iCur < 4; iCur++ )
7264 faceNodes.insert( curNodes[ind[iCur]] );
7265 if ( faceNodes.size() == 3 )
7266 iTria[ nbTria++ ] = iFace;
7268 // check if triangles are opposite
7269 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7271 // set nodes of the bottom triangle
7272 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7274 for ( iCur = 0; iCur < 4; iCur++ )
7275 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7276 indB.push_back( ind[iCur] );
7277 if ( !hexa.IsForward() )
7278 std::swap( indB[0], indB[2] );
7279 for ( iCur = 0; iCur < 3; iCur++ )
7280 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7281 // set nodes of the top triangle
7282 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7283 for ( iCur = 0; iCur < 3; ++iCur )
7284 for ( int j = 0; j < 4; ++j )
7285 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7287 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7294 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7295 //////////////////// HEXAHEDRON ---> pyramid
7296 for ( int iFace = 0; iFace < 6; iFace++ ) {
7297 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7298 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7299 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7300 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7301 // one face turns into a point ...
7302 int iOppFace = hexa.GetOppFaceIndex( iFace );
7303 ind = hexa.GetFaceNodesIndices( iOppFace );
7304 uniqueNodes.clear();
7305 for ( iCur = 0; iCur < 4; iCur++ ) {
7306 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7309 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7311 if ( uniqueNodes.size() == 4 ) {
7312 // ... and the opposite one is a quadrangle
7314 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7315 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7323 if ( toRemove && nbUniqueNodes > 4 ) {
7324 ////////////////// HEXAHEDRON ---> polyhedron
7325 hexa.SetExternalNormal();
7326 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7327 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7328 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7329 quantities.reserve( 6 ); quantities.clear();
7330 for ( int iFace = 0; iFace < 6; iFace++ )
7332 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7333 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7334 curNodes[ind[1]] == curNodes[ind[3]] )
7337 break; // opposite nodes stick
7340 for ( iCur = 0; iCur < 4; iCur++ )
7342 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7343 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7345 if ( nodeSet.size() < 3 )
7346 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7348 quantities.push_back( nodeSet.size() );
7350 if ( quantities.size() >= 4 )
7353 nbUniqueNodes = poly_nodes.size();
7354 newElemDefs[0].SetPoly(true);
7358 } // case HEXAHEDRON
7363 } // switch ( entity )
7365 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7367 // erase from nodeNodeMap nodes whose merge spoils elem
7368 vector< const SMDS_MeshNode* > noMergeNodes;
7369 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7370 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7371 nodeNodeMap.erase( noMergeNodes[i] );
7374 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7376 uniqueNodes.resize( nbUniqueNodes );
7378 if ( !toRemove && nbResElems == 0 )
7381 newElemDefs.resize( nbResElems );
7387 // ========================================================
7388 // class : ComparableElement
7389 // purpose : allow comparing elements basing on their nodes
7390 // ========================================================
7392 class ComparableElement : public boost::container::flat_set< int >
7394 typedef boost::container::flat_set< int > int_set;
7396 const SMDS_MeshElement* myElem;
7398 mutable int myGroupID;
7402 ComparableElement( const SMDS_MeshElement* theElem ):
7403 myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7405 this->reserve( theElem->NbNodes() );
7406 for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7408 int id = nodeIt->next()->GetID();
7414 const SMDS_MeshElement* GetElem() const { return myElem; }
7416 int& GroupID() const { return myGroupID; }
7417 //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7419 ComparableElement( const ComparableElement& theSource ) // move copy
7421 ComparableElement& src = const_cast< ComparableElement& >( theSource );
7422 (int_set&) (*this ) = boost::move( src );
7423 myElem = src.myElem;
7424 mySumID = src.mySumID;
7425 myGroupID = src.myGroupID;
7428 static int HashCode(const ComparableElement& se, int limit )
7430 return ::HashCode( se.mySumID, limit );
7432 static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7434 return ( se1 == se2 );
7439 //=======================================================================
7440 //function : FindEqualElements
7441 //purpose : Return list of group of elements built on the same nodes.
7442 // Search among theElements or in the whole mesh if theElements is empty
7443 //=======================================================================
7445 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet & theElements,
7446 TListOfListOfElementsID & theGroupsOfElementsID )
7450 SMDS_ElemIteratorPtr elemIt;
7451 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7452 else elemIt = SMESHUtils::elemSetIterator( theElements );
7454 typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7455 typedef std::list<int> TGroupOfElems;
7456 TMapOfElements mapOfElements;
7457 std::vector< TGroupOfElems > arrayOfGroups;
7458 TGroupOfElems groupOfElems;
7460 while ( elemIt->more() )
7462 const SMDS_MeshElement* curElem = elemIt->next();
7463 ComparableElement compElem = curElem;
7465 const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7466 if ( elemInSet.GetElem() != curElem ) // coincident elem
7468 int& iG = elemInSet.GroupID();
7471 iG = arrayOfGroups.size();
7472 arrayOfGroups.push_back( groupOfElems );
7473 arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7475 arrayOfGroups[ iG ].push_back( curElem->GetID() );
7479 groupOfElems.clear();
7480 std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7481 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7483 if ( groupIt->size() > 1 ) {
7484 //groupOfElems.sort(); -- theElements are sorted already
7485 theGroupsOfElementsID.emplace_back( *groupIt );
7490 //=======================================================================
7491 //function : MergeElements
7492 //purpose : In each given group, substitute all elements by the first one.
7493 //=======================================================================
7495 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7499 typedef list<int> TListOfIDs;
7500 TListOfIDs rmElemIds; // IDs of elems to remove
7502 SMESHDS_Mesh* aMesh = GetMeshDS();
7504 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7505 while ( groupsIt != theGroupsOfElementsID.end() ) {
7506 TListOfIDs& aGroupOfElemID = *groupsIt;
7507 aGroupOfElemID.sort();
7508 int elemIDToKeep = aGroupOfElemID.front();
7509 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7510 aGroupOfElemID.pop_front();
7511 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7512 while ( idIt != aGroupOfElemID.end() ) {
7513 int elemIDToRemove = *idIt;
7514 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7515 // add the kept element in groups of removed one (PAL15188)
7516 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7517 rmElemIds.push_back( elemIDToRemove );
7523 Remove( rmElemIds, false );
7526 //=======================================================================
7527 //function : MergeEqualElements
7528 //purpose : Remove all but one of elements built on the same nodes.
7529 //=======================================================================
7531 void SMESH_MeshEditor::MergeEqualElements()
7533 TIDSortedElemSet aMeshElements; /* empty input ==
7534 to merge equal elements in the whole mesh */
7535 TListOfListOfElementsID aGroupsOfElementsID;
7536 FindEqualElements( aMeshElements, aGroupsOfElementsID );
7537 MergeElements( aGroupsOfElementsID );
7540 //=======================================================================
7541 //function : findAdjacentFace
7543 //=======================================================================
7545 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7546 const SMDS_MeshNode* n2,
7547 const SMDS_MeshElement* elem)
7549 TIDSortedElemSet elemSet, avoidSet;
7551 avoidSet.insert ( elem );
7552 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7555 //=======================================================================
7556 //function : findSegment
7557 //purpose : Return a mesh segment by two nodes one of which can be medium
7558 //=======================================================================
7560 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7561 const SMDS_MeshNode* n2)
7563 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7564 while ( it->more() )
7566 const SMDS_MeshElement* seg = it->next();
7567 if ( seg->GetNodeIndex( n2 ) >= 0 )
7573 //=======================================================================
7574 //function : FindFreeBorder
7576 //=======================================================================
7578 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7580 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7581 const SMDS_MeshNode* theSecondNode,
7582 const SMDS_MeshNode* theLastNode,
7583 list< const SMDS_MeshNode* > & theNodes,
7584 list< const SMDS_MeshElement* >& theFaces)
7586 if ( !theFirstNode || !theSecondNode )
7588 // find border face between theFirstNode and theSecondNode
7589 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7593 theFaces.push_back( curElem );
7594 theNodes.push_back( theFirstNode );
7595 theNodes.push_back( theSecondNode );
7597 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7598 //TIDSortedElemSet foundElems;
7599 bool needTheLast = ( theLastNode != 0 );
7601 vector<const SMDS_MeshNode*> nodes;
7603 while ( nStart != theLastNode ) {
7604 if ( nStart == theFirstNode )
7605 return !needTheLast;
7607 // find all free border faces sharing nStart
7609 list< const SMDS_MeshElement* > curElemList;
7610 list< const SMDS_MeshNode* > nStartList;
7611 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7612 while ( invElemIt->more() ) {
7613 const SMDS_MeshElement* e = invElemIt->next();
7614 //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
7617 nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7618 SMDS_MeshElement::iterator() );
7619 nodes.push_back( nodes[ 0 ]);
7622 int iNode = 0, nbNodes = nodes.size() - 1;
7623 for ( iNode = 0; iNode < nbNodes; iNode++ )
7624 if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7625 ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7626 ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
7628 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
7629 curElemList.push_back( e );
7633 // analyse the found
7635 int nbNewBorders = curElemList.size();
7636 if ( nbNewBorders == 0 ) {
7637 // no free border furthermore
7638 return !needTheLast;
7640 else if ( nbNewBorders == 1 ) {
7641 // one more element found
7643 nStart = nStartList.front();
7644 curElem = curElemList.front();
7645 theFaces.push_back( curElem );
7646 theNodes.push_back( nStart );
7649 // several continuations found
7650 list< const SMDS_MeshElement* >::iterator curElemIt;
7651 list< const SMDS_MeshNode* >::iterator nStartIt;
7652 // check if one of them reached the last node
7653 if ( needTheLast ) {
7654 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7655 curElemIt!= curElemList.end();
7656 curElemIt++, nStartIt++ )
7657 if ( *nStartIt == theLastNode ) {
7658 theFaces.push_back( *curElemIt );
7659 theNodes.push_back( *nStartIt );
7663 // find the best free border by the continuations
7664 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7665 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7666 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7667 curElemIt!= curElemList.end();
7668 curElemIt++, nStartIt++ )
7670 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7671 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7672 // find one more free border
7673 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7677 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7678 // choice: clear a worse one
7679 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7680 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7681 contNodes[ iWorse ].clear();
7682 contFaces[ iWorse ].clear();
7685 if ( contNodes[0].empty() && contNodes[1].empty() )
7688 // push_back the best free border
7689 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7690 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7691 //theNodes.pop_back(); // remove nIgnore
7692 theNodes.pop_back(); // remove nStart
7693 //theFaces.pop_back(); // remove curElem
7694 theNodes.splice( theNodes.end(), *cNL );
7695 theFaces.splice( theFaces.end(), *cFL );
7698 } // several continuations found
7699 } // while ( nStart != theLastNode )
7704 //=======================================================================
7705 //function : CheckFreeBorderNodes
7706 //purpose : Return true if the tree nodes are on a free border
7707 //=======================================================================
7709 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7710 const SMDS_MeshNode* theNode2,
7711 const SMDS_MeshNode* theNode3)
7713 list< const SMDS_MeshNode* > nodes;
7714 list< const SMDS_MeshElement* > faces;
7715 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7718 //=======================================================================
7719 //function : SewFreeBorder
7721 //warning : for border-to-side sewing theSideSecondNode is considered as
7722 // the last side node and theSideThirdNode is not used
7723 //=======================================================================
7725 SMESH_MeshEditor::Sew_Error
7726 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7727 const SMDS_MeshNode* theBordSecondNode,
7728 const SMDS_MeshNode* theBordLastNode,
7729 const SMDS_MeshNode* theSideFirstNode,
7730 const SMDS_MeshNode* theSideSecondNode,
7731 const SMDS_MeshNode* theSideThirdNode,
7732 const bool theSideIsFreeBorder,
7733 const bool toCreatePolygons,
7734 const bool toCreatePolyedrs)
7738 Sew_Error aResult = SEW_OK;
7740 // ====================================
7741 // find side nodes and elements
7742 // ====================================
7744 list< const SMDS_MeshNode* > nSide[ 2 ];
7745 list< const SMDS_MeshElement* > eSide[ 2 ];
7746 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7747 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7751 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7752 nSide[0], eSide[0])) {
7753 MESSAGE(" Free Border 1 not found " );
7754 aResult = SEW_BORDER1_NOT_FOUND;
7756 if (theSideIsFreeBorder) {
7759 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7760 nSide[1], eSide[1])) {
7761 MESSAGE(" Free Border 2 not found " );
7762 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7765 if ( aResult != SEW_OK )
7768 if (!theSideIsFreeBorder) {
7772 // -------------------------------------------------------------------------
7774 // 1. If nodes to merge are not coincident, move nodes of the free border
7775 // from the coord sys defined by the direction from the first to last
7776 // nodes of the border to the correspondent sys of the side 2
7777 // 2. On the side 2, find the links most co-directed with the correspondent
7778 // links of the free border
7779 // -------------------------------------------------------------------------
7781 // 1. Since sewing may break if there are volumes to split on the side 2,
7782 // we won't move nodes but just compute new coordinates for them
7783 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7784 TNodeXYZMap nBordXYZ;
7785 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7786 list< const SMDS_MeshNode* >::iterator nBordIt;
7788 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7789 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7790 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7791 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7792 double tol2 = 1.e-8;
7793 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7794 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7795 // Need node movement.
7797 // find X and Z axes to create trsf
7798 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7800 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7802 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7805 gp_Ax3 toBordAx( Pb1, Zb, X );
7806 gp_Ax3 fromSideAx( Ps1, Zs, X );
7807 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7809 gp_Trsf toBordSys, fromSide2Sys;
7810 toBordSys.SetTransformation( toBordAx );
7811 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7812 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7815 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7816 const SMDS_MeshNode* n = *nBordIt;
7817 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7818 toBordSys.Transforms( xyz );
7819 fromSide2Sys.Transforms( xyz );
7820 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7824 // just insert nodes XYZ in the nBordXYZ map
7825 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7826 const SMDS_MeshNode* n = *nBordIt;
7827 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7831 // 2. On the side 2, find the links most co-directed with the correspondent
7832 // links of the free border
7834 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7835 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7836 sideNodes.push_back( theSideFirstNode );
7838 bool hasVolumes = false;
7839 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7840 set<long> foundSideLinkIDs, checkedLinkIDs;
7841 SMDS_VolumeTool volume;
7842 //const SMDS_MeshNode* faceNodes[ 4 ];
7844 const SMDS_MeshNode* sideNode;
7845 const SMDS_MeshElement* sideElem = 0;
7846 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7847 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7848 nBordIt = bordNodes.begin();
7850 // border node position and border link direction to compare with
7851 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7852 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7853 // choose next side node by link direction or by closeness to
7854 // the current border node:
7855 bool searchByDir = ( *nBordIt != theBordLastNode );
7857 // find the next node on the Side 2
7859 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7861 checkedLinkIDs.clear();
7862 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7864 // loop on inverse elements of current node (prevSideNode) on the Side 2
7865 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7866 while ( invElemIt->more() )
7868 const SMDS_MeshElement* elem = invElemIt->next();
7869 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7870 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7871 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7872 bool isVolume = volume.Set( elem );
7873 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7874 if ( isVolume ) // --volume
7876 else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7877 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7878 SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
7879 while ( nIt->more() ) {
7880 nodes[ iNode ] = cast2Node( nIt->next() );
7881 if ( nodes[ iNode++ ] == prevSideNode )
7882 iPrevNode = iNode - 1;
7884 // there are 2 links to check
7889 // loop on links, to be precise, on the second node of links
7890 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7891 const SMDS_MeshNode* n = nodes[ iNode ];
7893 if ( !volume.IsLinked( n, prevSideNode ))
7897 if ( iNode ) // a node before prevSideNode
7898 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7899 else // a node after prevSideNode
7900 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7902 // check if this link was already used
7903 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7904 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7905 if (!isJustChecked &&
7906 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7908 // test a link geometrically
7909 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7910 bool linkIsBetter = false;
7911 double dot = 0.0, dist = 0.0;
7912 if ( searchByDir ) { // choose most co-directed link
7913 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7914 linkIsBetter = ( dot > maxDot );
7916 else { // choose link with the node closest to bordPos
7917 dist = ( nextXYZ - bordPos ).SquareModulus();
7918 linkIsBetter = ( dist < minDist );
7920 if ( linkIsBetter ) {
7929 } // loop on inverse elements of prevSideNode
7932 MESSAGE(" Can't find path by links of the Side 2 ");
7933 return SEW_BAD_SIDE_NODES;
7935 sideNodes.push_back( sideNode );
7936 sideElems.push_back( sideElem );
7937 foundSideLinkIDs.insert ( linkID );
7938 prevSideNode = sideNode;
7940 if ( *nBordIt == theBordLastNode )
7941 searchByDir = false;
7943 // find the next border link to compare with
7944 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7945 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7946 // move to next border node if sideNode is before forward border node (bordPos)
7947 while ( *nBordIt != theBordLastNode && !searchByDir ) {
7948 prevBordNode = *nBordIt;
7950 bordPos = nBordXYZ[ *nBordIt ];
7951 bordDir = bordPos - nBordXYZ[ prevBordNode ];
7952 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7956 while ( sideNode != theSideSecondNode );
7958 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7959 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7960 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7962 } // end nodes search on the side 2
7964 // ============================
7965 // sew the border to the side 2
7966 // ============================
7968 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
7969 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
7971 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
7972 if ( toMergeConformal && toCreatePolygons )
7974 // do not merge quadrangles if polygons are OK (IPAL0052824)
7975 eIt[0] = eSide[0].begin();
7976 eIt[1] = eSide[1].begin();
7977 bool allQuads[2] = { true, true };
7978 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7979 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
7980 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
7982 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
7985 TListOfListOfNodes nodeGroupsToMerge;
7986 if (( toMergeConformal ) ||
7987 ( theSideIsFreeBorder && !theSideThirdNode )) {
7989 // all nodes are to be merged
7991 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
7992 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
7993 nIt[0]++, nIt[1]++ )
7995 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
7996 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
7997 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8002 // insert new nodes into the border and the side to get equal nb of segments
8004 // get normalized parameters of nodes on the borders
8005 vector< double > param[ 2 ];
8006 param[0].resize( maxNbNodes );
8007 param[1].resize( maxNbNodes );
8009 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8010 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8011 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8012 const SMDS_MeshNode* nPrev = *nIt;
8013 double bordLength = 0;
8014 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8015 const SMDS_MeshNode* nCur = *nIt;
8016 gp_XYZ segment (nCur->X() - nPrev->X(),
8017 nCur->Y() - nPrev->Y(),
8018 nCur->Z() - nPrev->Z());
8019 double segmentLen = segment.Modulus();
8020 bordLength += segmentLen;
8021 param[ iBord ][ iNode ] = bordLength;
8024 // normalize within [0,1]
8025 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8026 param[ iBord ][ iNode ] /= bordLength;
8030 // loop on border segments
8031 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8032 int i[ 2 ] = { 0, 0 };
8033 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8034 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8036 // element can be split while iterating on border if it has two edges in the border
8037 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8038 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8040 TElemOfNodeListMap insertMap;
8041 TElemOfNodeListMap::iterator insertMapIt;
8043 // key: elem to insert nodes into
8044 // value: 2 nodes to insert between + nodes to be inserted
8046 bool next[ 2 ] = { false, false };
8048 // find min adjacent segment length after sewing
8049 double nextParam = 10., prevParam = 0;
8050 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8051 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8052 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8053 if ( i[ iBord ] > 0 )
8054 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8056 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8057 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8058 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8060 // choose to insert or to merge nodes
8061 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8062 if ( Abs( du ) <= minSegLen * 0.2 ) {
8065 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8066 const SMDS_MeshNode* n0 = *nIt[0];
8067 const SMDS_MeshNode* n1 = *nIt[1];
8068 nodeGroupsToMerge.back().push_back( n1 );
8069 nodeGroupsToMerge.back().push_back( n0 );
8070 // position of node of the border changes due to merge
8071 param[ 0 ][ i[0] ] += du;
8072 // move n1 for the sake of elem shape evaluation during insertion.
8073 // n1 will be removed by MergeNodes() anyway
8074 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8075 next[0] = next[1] = true;
8080 int intoBord = ( du < 0 ) ? 0 : 1;
8081 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8082 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8083 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8084 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8085 if ( intoBord == 1 ) {
8086 // move node of the border to be on a link of elem of the side
8087 SMESH_NodeXYZ p1( n1 ), p2( n2 );
8088 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8089 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8090 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8092 elemReplaceMapIt = elemReplaceMap.find( elem );
8093 if ( elemReplaceMapIt != elemReplaceMap.end() )
8094 elem = elemReplaceMapIt->second;
8096 insertMapIt = insertMap.find( elem );
8097 bool notFound = ( insertMapIt == insertMap.end() );
8098 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8100 // insert into another link of the same element:
8101 // 1. perform insertion into the other link of the elem
8102 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8103 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8104 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8105 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8106 // 2. perform insertion into the link of adjacent faces
8107 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8108 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8110 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8111 InsertNodesIntoLink( seg, n12, n22, nodeList );
8113 if (toCreatePolyedrs) {
8114 // perform insertion into the links of adjacent volumes
8115 UpdateVolumes(n12, n22, nodeList);
8117 // 3. find an element appeared on n1 and n2 after the insertion
8118 insertMap.erase( insertMapIt );
8119 const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8120 elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8123 if ( notFound || otherLink ) {
8124 // add element and nodes of the side into the insertMap
8125 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8126 (*insertMapIt).second.push_back( n1 );
8127 (*insertMapIt).second.push_back( n2 );
8129 // add node to be inserted into elem
8130 (*insertMapIt).second.push_back( nIns );
8131 next[ 1 - intoBord ] = true;
8134 // go to the next segment
8135 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8136 if ( next[ iBord ] ) {
8137 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8139 nPrev[ iBord ] = *nIt[ iBord ];
8140 nIt[ iBord ]++; i[ iBord ]++;
8144 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8146 // perform insertion of nodes into elements
8148 for (insertMapIt = insertMap.begin();
8149 insertMapIt != insertMap.end();
8152 const SMDS_MeshElement* elem = (*insertMapIt).first;
8153 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8154 if ( nodeList.size() < 3 ) continue;
8155 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8156 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8158 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8160 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8161 InsertNodesIntoLink( seg, n1, n2, nodeList );
8164 if ( !theSideIsFreeBorder ) {
8165 // look for and insert nodes into the faces adjacent to elem
8166 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8167 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8170 if (toCreatePolyedrs) {
8171 // perform insertion into the links of adjacent volumes
8172 UpdateVolumes(n1, n2, nodeList);
8175 } // end: insert new nodes
8177 MergeNodes ( nodeGroupsToMerge );
8180 // Remove coincident segments
8183 TIDSortedElemSet segments;
8184 SMESH_SequenceOfElemPtr newFaces;
8185 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8187 if ( !myLastCreatedElems[i] ) continue;
8188 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8189 segments.insert( segments.end(), myLastCreatedElems[i] );
8191 newFaces.push_back( myLastCreatedElems[i] );
8193 // get segments adjacent to merged nodes
8194 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8195 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8197 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8198 if ( nodes.front()->IsNull() ) continue;
8199 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8200 while ( segIt->more() )
8201 segments.insert( segIt->next() );
8205 TListOfListOfElementsID equalGroups;
8206 if ( !segments.empty() )
8207 FindEqualElements( segments, equalGroups );
8208 if ( !equalGroups.empty() )
8210 // remove from segments those that will be removed
8211 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8212 for ( ; itGroups != equalGroups.end(); ++itGroups )
8214 list< int >& group = *itGroups;
8215 list< int >::iterator id = group.begin();
8216 for ( ++id; id != group.end(); ++id )
8217 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8218 segments.erase( seg );
8220 // remove equal segments
8221 MergeElements( equalGroups );
8223 // restore myLastCreatedElems
8224 myLastCreatedElems = newFaces;
8225 TIDSortedElemSet::iterator seg = segments.begin();
8226 for ( ; seg != segments.end(); ++seg )
8227 myLastCreatedElems.push_back( *seg );
8233 //=======================================================================
8234 //function : InsertNodesIntoLink
8235 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8236 // and theBetweenNode2 and split theElement
8237 //=======================================================================
8239 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8240 const SMDS_MeshNode* theBetweenNode1,
8241 const SMDS_MeshNode* theBetweenNode2,
8242 list<const SMDS_MeshNode*>& theNodesToInsert,
8243 const bool toCreatePoly)
8245 if ( !theElement ) return;
8247 SMESHDS_Mesh *aMesh = GetMeshDS();
8248 vector<const SMDS_MeshElement*> newElems;
8250 if ( theElement->GetType() == SMDSAbs_Edge )
8252 theNodesToInsert.push_front( theBetweenNode1 );
8253 theNodesToInsert.push_back ( theBetweenNode2 );
8254 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8255 const SMDS_MeshNode* n1 = *n;
8256 for ( ++n; n != theNodesToInsert.end(); ++n )
8258 const SMDS_MeshNode* n2 = *n;
8259 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8260 AddToSameGroups( seg, theElement, aMesh );
8262 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8265 theNodesToInsert.pop_front();
8266 theNodesToInsert.pop_back();
8268 if ( theElement->IsQuadratic() ) // add a not split part
8270 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8271 theElement->end_nodes() );
8272 int iOther = 0, nbN = nodes.size();
8273 for ( ; iOther < nbN; ++iOther )
8274 if ( nodes[iOther] != theBetweenNode1 &&
8275 nodes[iOther] != theBetweenNode2 )
8279 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8280 AddToSameGroups( seg, theElement, aMesh );
8282 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8284 else if ( iOther == 2 )
8286 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8287 AddToSameGroups( seg, theElement, aMesh );
8289 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8292 // treat new elements
8293 for ( size_t i = 0; i < newElems.size(); ++i )
8296 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8297 myLastCreatedElems.push_back( newElems[i] );
8299 ReplaceElemInGroups( theElement, newElems, aMesh );
8300 aMesh->RemoveElement( theElement );
8303 } // if ( theElement->GetType() == SMDSAbs_Edge )
8305 const SMDS_MeshElement* theFace = theElement;
8306 if ( theFace->GetType() != SMDSAbs_Face ) return;
8308 // find indices of 2 link nodes and of the rest nodes
8309 int iNode = 0, il1, il2, i3, i4;
8310 il1 = il2 = i3 = i4 = -1;
8311 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8313 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8314 while ( nodeIt->more() ) {
8315 const SMDS_MeshNode* n = nodeIt->next();
8316 if ( n == theBetweenNode1 )
8318 else if ( n == theBetweenNode2 )
8324 nodes[ iNode++ ] = n;
8326 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8329 // arrange link nodes to go one after another regarding the face orientation
8330 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8331 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8336 aNodesToInsert.reverse();
8338 // check that not link nodes of a quadrangles are in good order
8339 int nbFaceNodes = theFace->NbNodes();
8340 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8346 if (toCreatePoly || theFace->IsPoly()) {
8349 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8351 // add nodes of face up to first node of link
8353 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8354 while ( nodeIt->more() && !isFLN ) {
8355 const SMDS_MeshNode* n = nodeIt->next();
8356 poly_nodes[iNode++] = n;
8357 isFLN = ( n == nodes[il1] );
8359 // add nodes to insert
8360 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8361 for (; nIt != aNodesToInsert.end(); nIt++) {
8362 poly_nodes[iNode++] = *nIt;
8364 // add nodes of face starting from last node of link
8365 while ( nodeIt->more() ) {
8366 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8367 poly_nodes[iNode++] = n;
8371 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8374 else if ( !theFace->IsQuadratic() )
8376 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8377 int nbLinkNodes = 2 + aNodesToInsert.size();
8378 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8379 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8380 linkNodes[ 0 ] = nodes[ il1 ];
8381 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8382 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8383 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8384 linkNodes[ iNode++ ] = *nIt;
8386 // decide how to split a quadrangle: compare possible variants
8387 // and choose which of splits to be a quadrangle
8388 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8389 if ( nbFaceNodes == 3 ) {
8390 iBestQuad = nbSplits;
8393 else if ( nbFaceNodes == 4 ) {
8394 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8395 double aBestRate = DBL_MAX;
8396 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8398 double aBadRate = 0;
8399 // evaluate elements quality
8400 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8401 if ( iSplit == iQuad ) {
8402 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8406 aBadRate += getBadRate( &quad, aCrit );
8409 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8411 nodes[ iSplit < iQuad ? i4 : i3 ]);
8412 aBadRate += getBadRate( &tria, aCrit );
8416 if ( aBadRate < aBestRate ) {
8418 aBestRate = aBadRate;
8423 // create new elements
8425 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8427 if ( iSplit == iBestQuad )
8428 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8433 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8435 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8438 const SMDS_MeshNode* newNodes[ 4 ];
8439 newNodes[ 0 ] = linkNodes[ i1 ];
8440 newNodes[ 1 ] = linkNodes[ i2 ];
8441 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8442 newNodes[ 3 ] = nodes[ i4 ];
8443 if (iSplit == iBestQuad)
8444 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8446 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8448 } // end if(!theFace->IsQuadratic())
8450 else { // theFace is quadratic
8451 // we have to split theFace on simple triangles and one simple quadrangle
8453 int nbshift = tmp*2;
8454 // shift nodes in nodes[] by nbshift
8456 for(i=0; i<nbshift; i++) {
8457 const SMDS_MeshNode* n = nodes[0];
8458 for(j=0; j<nbFaceNodes-1; j++) {
8459 nodes[j] = nodes[j+1];
8461 nodes[nbFaceNodes-1] = n;
8463 il1 = il1 - nbshift;
8464 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8465 // n0 n1 n2 n0 n1 n2
8466 // +-----+-----+ +-----+-----+
8475 // create new elements
8477 if ( nbFaceNodes == 6 ) { // quadratic triangle
8478 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8479 if ( theFace->IsMediumNode(nodes[il1]) ) {
8480 // create quadrangle
8481 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8487 // create quadrangle
8488 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8494 else { // nbFaceNodes==8 - quadratic quadrangle
8495 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8496 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8497 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8498 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8499 // create quadrangle
8500 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8506 // create quadrangle
8507 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8513 // create needed triangles using n1,n2,n3 and inserted nodes
8514 int nbn = 2 + aNodesToInsert.size();
8515 vector<const SMDS_MeshNode*> aNodes(nbn);
8516 aNodes[0 ] = nodes[n1];
8517 aNodes[nbn-1] = nodes[n2];
8518 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8519 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8520 aNodes[iNode++] = *nIt;
8522 for ( i = 1; i < nbn; i++ )
8523 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8526 // remove the old face
8527 for ( size_t i = 0; i < newElems.size(); ++i )
8530 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8531 myLastCreatedElems.push_back( newElems[i] );
8533 ReplaceElemInGroups( theFace, newElems, aMesh );
8534 aMesh->RemoveElement(theFace);
8536 } // InsertNodesIntoLink()
8538 //=======================================================================
8539 //function : UpdateVolumes
8541 //=======================================================================
8543 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8544 const SMDS_MeshNode* theBetweenNode2,
8545 list<const SMDS_MeshNode*>& theNodesToInsert)
8549 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8550 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8551 const SMDS_MeshElement* elem = invElemIt->next();
8553 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8554 SMDS_VolumeTool aVolume (elem);
8555 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8558 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8559 int iface, nbFaces = aVolume.NbFaces();
8560 vector<const SMDS_MeshNode *> poly_nodes;
8561 vector<int> quantities (nbFaces);
8563 for (iface = 0; iface < nbFaces; iface++) {
8564 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8565 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8566 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8568 for (int inode = 0; inode < nbFaceNodes; inode++) {
8569 poly_nodes.push_back(faceNodes[inode]);
8571 if (nbInserted == 0) {
8572 if (faceNodes[inode] == theBetweenNode1) {
8573 if (faceNodes[inode + 1] == theBetweenNode2) {
8574 nbInserted = theNodesToInsert.size();
8576 // add nodes to insert
8577 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8578 for (; nIt != theNodesToInsert.end(); nIt++) {
8579 poly_nodes.push_back(*nIt);
8583 else if (faceNodes[inode] == theBetweenNode2) {
8584 if (faceNodes[inode + 1] == theBetweenNode1) {
8585 nbInserted = theNodesToInsert.size();
8587 // add nodes to insert in reversed order
8588 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8590 for (; nIt != theNodesToInsert.begin(); nIt--) {
8591 poly_nodes.push_back(*nIt);
8593 poly_nodes.push_back(*nIt);
8600 quantities[iface] = nbFaceNodes + nbInserted;
8603 // Replace the volume
8604 SMESHDS_Mesh *aMesh = GetMeshDS();
8606 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8608 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8609 myLastCreatedElems.push_back( newElem );
8610 ReplaceElemInGroups( elem, newElem, aMesh );
8612 aMesh->RemoveElement( elem );
8618 //================================================================================
8620 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8622 //================================================================================
8624 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8625 vector<const SMDS_MeshNode *> & nodes,
8626 vector<int> & nbNodeInFaces )
8629 nbNodeInFaces.clear();
8630 SMDS_VolumeTool vTool ( elem );
8631 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8633 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8634 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8635 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8640 //=======================================================================
8642 * \brief Convert elements contained in a sub-mesh to quadratic
8643 * \return int - nb of checked elements
8645 //=======================================================================
8647 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8648 SMESH_MesherHelper& theHelper,
8649 const bool theForce3d)
8651 //MESSAGE("convertElemToQuadratic");
8653 if( !theSm ) return nbElem;
8655 vector<int> nbNodeInFaces;
8656 vector<const SMDS_MeshNode *> nodes;
8657 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8658 while(ElemItr->more())
8661 const SMDS_MeshElement* elem = ElemItr->next();
8662 if( !elem ) continue;
8664 // analyse a necessity of conversion
8665 const SMDSAbs_ElementType aType = elem->GetType();
8666 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8668 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8669 bool hasCentralNodes = false;
8670 if ( elem->IsQuadratic() )
8673 switch ( aGeomType ) {
8674 case SMDSEntity_Quad_Triangle:
8675 case SMDSEntity_Quad_Quadrangle:
8676 case SMDSEntity_Quad_Hexa:
8677 case SMDSEntity_Quad_Penta:
8678 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8680 case SMDSEntity_BiQuad_Triangle:
8681 case SMDSEntity_BiQuad_Quadrangle:
8682 case SMDSEntity_TriQuad_Hexa:
8683 case SMDSEntity_BiQuad_Penta:
8684 alreadyOK = theHelper.GetIsBiQuadratic();
8685 hasCentralNodes = true;
8690 // take into account already present medium nodes
8692 case SMDSAbs_Volume:
8693 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8695 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8697 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8703 // get elem data needed to re-create it
8705 const int id = elem->GetID();
8706 const int nbNodes = elem->NbCornerNodes();
8707 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8708 if ( aGeomType == SMDSEntity_Polyhedra )
8709 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8710 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8711 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8713 // remove a linear element
8714 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8716 // remove central nodes of biquadratic elements (biquad->quad conversion)
8717 if ( hasCentralNodes )
8718 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8719 if ( nodes[i]->NbInverseElements() == 0 )
8720 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8722 const SMDS_MeshElement* NewElem = 0;
8728 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8736 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8739 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8742 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8746 case SMDSAbs_Volume :
8750 case SMDSEntity_Tetra:
8751 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8753 case SMDSEntity_Pyramid:
8754 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8756 case SMDSEntity_Penta:
8757 case SMDSEntity_Quad_Penta:
8758 case SMDSEntity_BiQuad_Penta:
8759 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8761 case SMDSEntity_Hexa:
8762 case SMDSEntity_Quad_Hexa:
8763 case SMDSEntity_TriQuad_Hexa:
8764 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8765 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8767 case SMDSEntity_Hexagonal_Prism:
8769 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8776 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8777 if( NewElem && NewElem->getshapeId() < 1 )
8778 theSm->AddElement( NewElem );
8782 //=======================================================================
8783 //function : ConvertToQuadratic
8785 //=======================================================================
8787 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8789 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8790 SMESHDS_Mesh* meshDS = GetMeshDS();
8792 SMESH_MesherHelper aHelper(*myMesh);
8794 aHelper.SetIsQuadratic( true );
8795 aHelper.SetIsBiQuadratic( theToBiQuad );
8796 aHelper.SetElementsOnShape(true);
8797 aHelper.ToFixNodeParameters( true );
8799 // convert elements assigned to sub-meshes
8800 int nbCheckedElems = 0;
8801 if ( myMesh->HasShapeToMesh() )
8803 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8805 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8806 while ( smIt->more() ) {
8807 SMESH_subMesh* sm = smIt->next();
8808 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8809 aHelper.SetSubShape( sm->GetSubShape() );
8810 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8816 // convert elements NOT assigned to sub-meshes
8817 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8818 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8820 aHelper.SetElementsOnShape(false);
8821 SMESHDS_SubMesh *smDS = 0;
8824 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8825 while( aEdgeItr->more() )
8827 const SMDS_MeshEdge* edge = aEdgeItr->next();
8828 if ( !edge->IsQuadratic() )
8830 int id = edge->GetID();
8831 const SMDS_MeshNode* n1 = edge->GetNode(0);
8832 const SMDS_MeshNode* n2 = edge->GetNode(1);
8834 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8836 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8837 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8841 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8846 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8847 while( aFaceItr->more() )
8849 const SMDS_MeshFace* face = aFaceItr->next();
8850 if ( !face ) continue;
8852 const SMDSAbs_EntityType type = face->GetEntityType();
8856 case SMDSEntity_Quad_Triangle:
8857 case SMDSEntity_Quad_Quadrangle:
8858 alreadyOK = !theToBiQuad;
8859 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8861 case SMDSEntity_BiQuad_Triangle:
8862 case SMDSEntity_BiQuad_Quadrangle:
8863 alreadyOK = theToBiQuad;
8864 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8866 default: alreadyOK = false;
8871 const int id = face->GetID();
8872 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8874 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8876 SMDS_MeshFace * NewFace = 0;
8879 case SMDSEntity_Triangle:
8880 case SMDSEntity_Quad_Triangle:
8881 case SMDSEntity_BiQuad_Triangle:
8882 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8883 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8884 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8887 case SMDSEntity_Quadrangle:
8888 case SMDSEntity_Quad_Quadrangle:
8889 case SMDSEntity_BiQuad_Quadrangle:
8890 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8891 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8892 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8896 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8898 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8902 vector<int> nbNodeInFaces;
8903 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8904 while(aVolumeItr->more())
8906 const SMDS_MeshVolume* volume = aVolumeItr->next();
8907 if ( !volume ) continue;
8909 const SMDSAbs_EntityType type = volume->GetEntityType();
8910 if ( volume->IsQuadratic() )
8915 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8916 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8917 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
8918 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
8919 default: alreadyOK = true;
8923 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8927 const int id = volume->GetID();
8928 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8929 if ( type == SMDSEntity_Polyhedra )
8930 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
8931 else if ( type == SMDSEntity_Hexagonal_Prism )
8932 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8934 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8936 SMDS_MeshVolume * NewVolume = 0;
8939 case SMDSEntity_Tetra:
8940 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8942 case SMDSEntity_Hexa:
8943 case SMDSEntity_Quad_Hexa:
8944 case SMDSEntity_TriQuad_Hexa:
8945 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8946 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8947 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8948 if ( nodes[i]->NbInverseElements() == 0 )
8949 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8951 case SMDSEntity_Pyramid:
8952 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8953 nodes[3], nodes[4], id, theForce3d);
8955 case SMDSEntity_Penta:
8956 case SMDSEntity_Quad_Penta:
8957 case SMDSEntity_BiQuad_Penta:
8958 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8959 nodes[3], nodes[4], nodes[5], id, theForce3d);
8960 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
8961 if ( nodes[i]->NbInverseElements() == 0 )
8962 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8964 case SMDSEntity_Hexagonal_Prism:
8966 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8968 ReplaceElemInGroups(volume, NewVolume, meshDS);
8973 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8974 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8975 // aHelper.FixQuadraticElements(myError);
8976 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8980 //================================================================================
8982 * \brief Makes given elements quadratic
8983 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
8984 * \param theElements - elements to make quadratic
8986 //================================================================================
8988 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
8989 TIDSortedElemSet& theElements,
8990 const bool theToBiQuad)
8992 if ( theElements.empty() ) return;
8994 // we believe that all theElements are of the same type
8995 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
8997 // get all nodes shared by theElements
8998 TIDSortedNodeSet allNodes;
8999 TIDSortedElemSet::iterator eIt = theElements.begin();
9000 for ( ; eIt != theElements.end(); ++eIt )
9001 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9003 // complete theElements with elements of lower dim whose all nodes are in allNodes
9005 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9006 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9007 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9008 for ( ; nIt != allNodes.end(); ++nIt )
9010 const SMDS_MeshNode* n = *nIt;
9011 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9012 while ( invIt->more() )
9014 const SMDS_MeshElement* e = invIt->next();
9015 const SMDSAbs_ElementType type = e->GetType();
9016 if ( e->IsQuadratic() )
9018 quadAdjacentElems[ type ].insert( e );
9021 switch ( e->GetEntityType() ) {
9022 case SMDSEntity_Quad_Triangle:
9023 case SMDSEntity_Quad_Quadrangle:
9024 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9025 case SMDSEntity_BiQuad_Triangle:
9026 case SMDSEntity_BiQuad_Quadrangle:
9027 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9028 default: alreadyOK = true;
9033 if ( type >= elemType )
9034 continue; // same type or more complex linear element
9036 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9037 continue; // e is already checked
9041 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9042 while ( nodeIt->more() && allIn )
9043 allIn = allNodes.count( nodeIt->next() );
9045 theElements.insert(e );
9049 SMESH_MesherHelper helper(*myMesh);
9050 helper.SetIsQuadratic( true );
9051 helper.SetIsBiQuadratic( theToBiQuad );
9053 // add links of quadratic adjacent elements to the helper
9055 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9056 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9057 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9059 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9061 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9062 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9063 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9065 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9067 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9068 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9069 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9071 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9074 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9076 SMESHDS_Mesh* meshDS = GetMeshDS();
9077 SMESHDS_SubMesh* smDS = 0;
9078 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9080 const SMDS_MeshElement* elem = *eIt;
9083 int nbCentralNodes = 0;
9084 switch ( elem->GetEntityType() ) {
9085 // linear convertible
9086 case SMDSEntity_Edge:
9087 case SMDSEntity_Triangle:
9088 case SMDSEntity_Quadrangle:
9089 case SMDSEntity_Tetra:
9090 case SMDSEntity_Pyramid:
9091 case SMDSEntity_Hexa:
9092 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9093 // quadratic that can become bi-quadratic
9094 case SMDSEntity_Quad_Triangle:
9095 case SMDSEntity_Quad_Quadrangle:
9096 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9098 case SMDSEntity_BiQuad_Triangle:
9099 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9100 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9102 default: alreadyOK = true;
9104 if ( alreadyOK ) continue;
9106 const SMDSAbs_ElementType type = elem->GetType();
9107 const int id = elem->GetID();
9108 const int nbNodes = elem->NbCornerNodes();
9109 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9111 helper.SetSubShape( elem->getshapeId() );
9113 if ( !smDS || !smDS->Contains( elem ))
9114 smDS = meshDS->MeshElements( elem->getshapeId() );
9115 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9117 SMDS_MeshElement * newElem = 0;
9120 case 4: // cases for most frequently used element types go first (for optimization)
9121 if ( type == SMDSAbs_Volume )
9122 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9124 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9127 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9128 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9131 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9134 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9137 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9138 nodes[4], id, theForce3d);
9141 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9142 nodes[4], nodes[5], id, theForce3d);
9146 ReplaceElemInGroups( elem, newElem, meshDS);
9147 if( newElem && smDS )
9148 smDS->AddElement( newElem );
9150 // remove central nodes
9151 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9152 if ( nodes[i]->NbInverseElements() == 0 )
9153 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9155 } // loop on theElements
9158 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9159 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9160 // helper.FixQuadraticElements( myError );
9161 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9165 //=======================================================================
9167 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9168 * \return int - nb of checked elements
9170 //=======================================================================
9172 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9173 SMDS_ElemIteratorPtr theItr,
9174 const int theShapeID)
9177 SMESHDS_Mesh* meshDS = GetMeshDS();
9178 ElemFeatures elemType;
9179 vector<const SMDS_MeshNode *> nodes;
9181 while( theItr->more() )
9183 const SMDS_MeshElement* elem = theItr->next();
9185 if( elem && elem->IsQuadratic())
9188 int nbCornerNodes = elem->NbCornerNodes();
9189 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9191 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9193 //remove a quadratic element
9194 if ( !theSm || !theSm->Contains( elem ))
9195 theSm = meshDS->MeshElements( elem->getshapeId() );
9196 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9198 // remove medium nodes
9199 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9200 if ( nodes[i]->NbInverseElements() == 0 )
9201 meshDS->RemoveFreeNode( nodes[i], theSm );
9203 // add a linear element
9204 nodes.resize( nbCornerNodes );
9205 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9206 ReplaceElemInGroups(elem, newElem, meshDS);
9207 if( theSm && newElem )
9208 theSm->AddElement( newElem );
9214 //=======================================================================
9215 //function : ConvertFromQuadratic
9217 //=======================================================================
9219 bool SMESH_MeshEditor::ConvertFromQuadratic()
9221 int nbCheckedElems = 0;
9222 if ( myMesh->HasShapeToMesh() )
9224 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9226 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9227 while ( smIt->more() ) {
9228 SMESH_subMesh* sm = smIt->next();
9229 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9230 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9236 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9237 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9239 SMESHDS_SubMesh *aSM = 0;
9240 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9248 //================================================================================
9250 * \brief Return true if all medium nodes of the element are in the node set
9252 //================================================================================
9254 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9256 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9257 if ( !nodeSet.count( elem->GetNode(i) ))
9263 //================================================================================
9265 * \brief Makes given elements linear
9267 //================================================================================
9269 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9271 if ( theElements.empty() ) return;
9273 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9274 set<int> mediumNodeIDs;
9275 TIDSortedElemSet::iterator eIt = theElements.begin();
9276 for ( ; eIt != theElements.end(); ++eIt )
9278 const SMDS_MeshElement* e = *eIt;
9279 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9280 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9283 // replace given elements by linear ones
9284 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9285 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9287 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9288 // except those elements sharing medium nodes of quadratic element whose medium nodes
9289 // are not all in mediumNodeIDs
9291 // get remaining medium nodes
9292 TIDSortedNodeSet mediumNodes;
9293 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9294 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9295 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9296 mediumNodes.insert( mediumNodes.end(), n );
9298 // find more quadratic elements to convert
9299 TIDSortedElemSet moreElemsToConvert;
9300 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9301 for ( ; nIt != mediumNodes.end(); ++nIt )
9303 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9304 while ( invIt->more() )
9306 const SMDS_MeshElement* e = invIt->next();
9307 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9309 // find a more complex element including e and
9310 // whose medium nodes are not in mediumNodes
9311 bool complexFound = false;
9312 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9314 SMDS_ElemIteratorPtr invIt2 =
9315 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9316 while ( invIt2->more() )
9318 const SMDS_MeshElement* eComplex = invIt2->next();
9319 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9321 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9322 if ( nbCommonNodes == e->NbNodes())
9324 complexFound = true;
9325 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9331 if ( !complexFound )
9332 moreElemsToConvert.insert( e );
9336 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9337 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9340 //=======================================================================
9341 //function : SewSideElements
9343 //=======================================================================
9345 SMESH_MeshEditor::Sew_Error
9346 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9347 TIDSortedElemSet& theSide2,
9348 const SMDS_MeshNode* theFirstNode1,
9349 const SMDS_MeshNode* theFirstNode2,
9350 const SMDS_MeshNode* theSecondNode1,
9351 const SMDS_MeshNode* theSecondNode2)
9355 if ( theSide1.size() != theSide2.size() )
9356 return SEW_DIFF_NB_OF_ELEMENTS;
9358 Sew_Error aResult = SEW_OK;
9360 // 1. Build set of faces representing each side
9361 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9362 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9364 // =======================================================================
9365 // 1. Build set of faces representing each side:
9366 // =======================================================================
9367 // a. build set of nodes belonging to faces
9368 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9369 // c. create temporary faces representing side of volumes if correspondent
9370 // face does not exist
9372 SMESHDS_Mesh* aMesh = GetMeshDS();
9373 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9374 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9375 TIDSortedElemSet faceSet1, faceSet2;
9376 set<const SMDS_MeshElement*> volSet1, volSet2;
9377 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9378 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9379 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9380 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9381 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9382 int iSide, iFace, iNode;
9384 list<const SMDS_MeshElement* > tempFaceList;
9385 for ( iSide = 0; iSide < 2; iSide++ ) {
9386 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9387 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9388 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9389 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9390 set<const SMDS_MeshElement*>::iterator vIt;
9391 TIDSortedElemSet::iterator eIt;
9392 set<const SMDS_MeshNode*>::iterator nIt;
9394 // check that given nodes belong to given elements
9395 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9396 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9397 int firstIndex = -1, secondIndex = -1;
9398 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9399 const SMDS_MeshElement* elem = *eIt;
9400 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9401 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9402 if ( firstIndex > -1 && secondIndex > -1 ) break;
9404 if ( firstIndex < 0 || secondIndex < 0 ) {
9405 // we can simply return until temporary faces created
9406 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9409 // -----------------------------------------------------------
9410 // 1a. Collect nodes of existing faces
9411 // and build set of face nodes in order to detect missing
9412 // faces corresponding to sides of volumes
9413 // -----------------------------------------------------------
9415 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9417 // loop on the given element of a side
9418 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9419 //const SMDS_MeshElement* elem = *eIt;
9420 const SMDS_MeshElement* elem = *eIt;
9421 if ( elem->GetType() == SMDSAbs_Face ) {
9422 faceSet->insert( elem );
9423 set <const SMDS_MeshNode*> faceNodeSet;
9424 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9425 while ( nodeIt->more() ) {
9426 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9427 nodeSet->insert( n );
9428 faceNodeSet.insert( n );
9430 setOfFaceNodeSet.insert( faceNodeSet );
9432 else if ( elem->GetType() == SMDSAbs_Volume )
9433 volSet->insert( elem );
9435 // ------------------------------------------------------------------------------
9436 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9437 // ------------------------------------------------------------------------------
9439 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9440 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9441 while ( fIt->more() ) { // loop on faces sharing a node
9442 const SMDS_MeshElement* f = fIt->next();
9443 if ( faceSet->find( f ) == faceSet->end() ) {
9444 // check if all nodes are in nodeSet and
9445 // complete setOfFaceNodeSet if they are
9446 set <const SMDS_MeshNode*> faceNodeSet;
9447 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9448 bool allInSet = true;
9449 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9450 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9451 if ( nodeSet->find( n ) == nodeSet->end() )
9454 faceNodeSet.insert( n );
9457 faceSet->insert( f );
9458 setOfFaceNodeSet.insert( faceNodeSet );
9464 // -------------------------------------------------------------------------
9465 // 1c. Create temporary faces representing sides of volumes if correspondent
9466 // face does not exist
9467 // -------------------------------------------------------------------------
9469 if ( !volSet->empty() ) {
9470 //int nodeSetSize = nodeSet->size();
9472 // loop on given volumes
9473 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9474 SMDS_VolumeTool vol (*vIt);
9475 // loop on volume faces: find free faces
9476 // --------------------------------------
9477 list<const SMDS_MeshElement* > freeFaceList;
9478 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9479 if ( !vol.IsFreeFace( iFace ))
9481 // check if there is already a face with same nodes in a face set
9482 const SMDS_MeshElement* aFreeFace = 0;
9483 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9484 int nbNodes = vol.NbFaceNodes( iFace );
9485 set <const SMDS_MeshNode*> faceNodeSet;
9486 vol.GetFaceNodes( iFace, faceNodeSet );
9487 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9489 // no such a face is given but it still can exist, check it
9490 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9491 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9494 // create a temporary face
9495 if ( nbNodes == 3 ) {
9496 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9497 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9499 else if ( nbNodes == 4 ) {
9500 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9501 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9504 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9505 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9506 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9509 tempFaceList.push_back( aFreeFace );
9513 freeFaceList.push_back( aFreeFace );
9515 } // loop on faces of a volume
9517 // choose one of several free faces of a volume
9518 // --------------------------------------------
9519 if ( freeFaceList.size() > 1 ) {
9520 // choose a face having max nb of nodes shared by other elems of a side
9521 int maxNbNodes = -1;
9522 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9523 while ( fIt != freeFaceList.end() ) { // loop on free faces
9524 int nbSharedNodes = 0;
9525 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9526 while ( nodeIt->more() ) { // loop on free face nodes
9527 const SMDS_MeshNode* n =
9528 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9529 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9530 while ( invElemIt->more() ) {
9531 const SMDS_MeshElement* e = invElemIt->next();
9532 nbSharedNodes += faceSet->count( e );
9533 nbSharedNodes += elemSet->count( e );
9536 if ( nbSharedNodes > maxNbNodes ) {
9537 maxNbNodes = nbSharedNodes;
9538 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9540 else if ( nbSharedNodes == maxNbNodes ) {
9544 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9547 if ( freeFaceList.size() > 1 )
9549 // could not choose one face, use another way
9550 // choose a face most close to the bary center of the opposite side
9551 gp_XYZ aBC( 0., 0., 0. );
9552 set <const SMDS_MeshNode*> addedNodes;
9553 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9554 eIt = elemSet2->begin();
9555 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9556 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9557 while ( nodeIt->more() ) { // loop on free face nodes
9558 const SMDS_MeshNode* n =
9559 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9560 if ( addedNodes.insert( n ).second )
9561 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9564 aBC /= addedNodes.size();
9565 double minDist = DBL_MAX;
9566 fIt = freeFaceList.begin();
9567 while ( fIt != freeFaceList.end() ) { // loop on free faces
9569 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9570 while ( nodeIt->more() ) { // loop on free face nodes
9571 const SMDS_MeshNode* n =
9572 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9573 gp_XYZ p( n->X(),n->Y(),n->Z() );
9574 dist += ( aBC - p ).SquareModulus();
9576 if ( dist < minDist ) {
9578 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9581 fIt = freeFaceList.erase( fIt++ );
9584 } // choose one of several free faces of a volume
9586 if ( freeFaceList.size() == 1 ) {
9587 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9588 faceSet->insert( aFreeFace );
9589 // complete a node set with nodes of a found free face
9590 // for ( iNode = 0; iNode < ; iNode++ )
9591 // nodeSet->insert( fNodes[ iNode ] );
9594 } // loop on volumes of a side
9596 // // complete a set of faces if new nodes in a nodeSet appeared
9597 // // ----------------------------------------------------------
9598 // if ( nodeSetSize != nodeSet->size() ) {
9599 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9600 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9601 // while ( fIt->more() ) { // loop on faces sharing a node
9602 // const SMDS_MeshElement* f = fIt->next();
9603 // if ( faceSet->find( f ) == faceSet->end() ) {
9604 // // check if all nodes are in nodeSet and
9605 // // complete setOfFaceNodeSet if they are
9606 // set <const SMDS_MeshNode*> faceNodeSet;
9607 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9608 // bool allInSet = true;
9609 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9610 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9611 // if ( nodeSet->find( n ) == nodeSet->end() )
9612 // allInSet = false;
9614 // faceNodeSet.insert( n );
9616 // if ( allInSet ) {
9617 // faceSet->insert( f );
9618 // setOfFaceNodeSet.insert( faceNodeSet );
9624 } // Create temporary faces, if there are volumes given
9627 if ( faceSet1.size() != faceSet2.size() ) {
9628 // delete temporary faces: they are in reverseElements of actual nodes
9629 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9630 // while ( tmpFaceIt->more() )
9631 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9632 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9633 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9634 // aMesh->RemoveElement(*tmpFaceIt);
9635 MESSAGE("Diff nb of faces");
9636 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9639 // ============================================================
9640 // 2. Find nodes to merge:
9641 // bind a node to remove to a node to put instead
9642 // ============================================================
9644 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9645 if ( theFirstNode1 != theFirstNode2 )
9646 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9647 if ( theSecondNode1 != theSecondNode2 )
9648 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9650 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9651 set< long > linkIdSet; // links to process
9652 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9654 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9655 list< NLink > linkList[2];
9656 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9657 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9658 // loop on links in linkList; find faces by links and append links
9659 // of the found faces to linkList
9660 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9661 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9663 NLink link[] = { *linkIt[0], *linkIt[1] };
9664 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9665 if ( !linkIdSet.count( linkID ) )
9668 // by links, find faces in the face sets,
9669 // and find indices of link nodes in the found faces;
9670 // in a face set, there is only one or no face sharing a link
9671 // ---------------------------------------------------------------
9673 const SMDS_MeshElement* face[] = { 0, 0 };
9674 vector<const SMDS_MeshNode*> fnodes[2];
9675 int iLinkNode[2][2];
9676 TIDSortedElemSet avoidSet;
9677 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9678 const SMDS_MeshNode* n1 = link[iSide].first;
9679 const SMDS_MeshNode* n2 = link[iSide].second;
9680 //cout << "Side " << iSide << " ";
9681 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9682 // find a face by two link nodes
9683 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9684 *faceSetPtr[ iSide ], avoidSet,
9685 &iLinkNode[iSide][0],
9686 &iLinkNode[iSide][1] );
9689 //cout << " F " << face[ iSide]->GetID() <<endl;
9690 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9691 // put face nodes to fnodes
9692 SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9693 fnodes[ iSide ].assign( nIt, nEnd );
9694 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9698 // check similarity of elements of the sides
9699 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9700 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9701 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9702 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9705 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9707 break; // do not return because it's necessary to remove tmp faces
9710 // set nodes to merge
9711 // -------------------
9713 if ( face[0] && face[1] ) {
9714 const int nbNodes = face[0]->NbNodes();
9715 if ( nbNodes != face[1]->NbNodes() ) {
9716 MESSAGE("Diff nb of face nodes");
9717 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9718 break; // do not return because it s necessary to remove tmp faces
9720 bool reverse[] = { false, false }; // order of nodes in the link
9721 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9722 // analyse link orientation in faces
9723 int i1 = iLinkNode[ iSide ][ 0 ];
9724 int i2 = iLinkNode[ iSide ][ 1 ];
9725 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9727 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9728 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9729 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9731 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9732 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9735 // add other links of the faces to linkList
9736 // -----------------------------------------
9738 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9739 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9740 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9741 if ( !iter_isnew.second ) { // already in a set: no need to process
9742 linkIdSet.erase( iter_isnew.first );
9744 else // new in set == encountered for the first time: add
9746 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9747 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9748 linkList[0].push_back ( NLink( n1, n2 ));
9749 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9754 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9757 } // loop on link lists
9759 if ( aResult == SEW_OK &&
9760 ( //linkIt[0] != linkList[0].end() ||
9761 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9762 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9763 " " << (faceSetPtr[1]->empty()));
9764 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9767 // ====================================================================
9768 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9769 // ====================================================================
9771 // delete temporary faces
9772 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9773 // while ( tmpFaceIt->more() )
9774 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9775 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9776 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9777 aMesh->RemoveElement(*tmpFaceIt);
9779 if ( aResult != SEW_OK)
9782 list< int > nodeIDsToRemove;
9783 vector< const SMDS_MeshNode*> nodes;
9784 ElemFeatures elemType;
9786 // loop on nodes replacement map
9787 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9788 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9789 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9791 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9792 nodeIDsToRemove.push_back( nToRemove->GetID() );
9793 // loop on elements sharing nToRemove
9794 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9795 while ( invElemIt->more() ) {
9796 const SMDS_MeshElement* e = invElemIt->next();
9797 // get a new suite of nodes: make replacement
9798 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9799 nodes.resize( nbNodes );
9800 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9801 while ( nIt->more() ) {
9802 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9803 nnIt = nReplaceMap.find( n );
9804 if ( nnIt != nReplaceMap.end() ) {
9810 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9811 // elemIDsToRemove.push_back( e->GetID() );
9815 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9816 aMesh->RemoveElement( e );
9818 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9820 AddToSameGroups( newElem, e, aMesh );
9821 if ( int aShapeId = e->getshapeId() )
9822 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9828 Remove( nodeIDsToRemove, true );
9833 //================================================================================
9835 * \brief Find corresponding nodes in two sets of faces
9836 * \param theSide1 - first face set
9837 * \param theSide2 - second first face
9838 * \param theFirstNode1 - a boundary node of set 1
9839 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9840 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9841 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9842 * \param nReplaceMap - output map of corresponding nodes
9843 * \return bool - is a success or not
9845 //================================================================================
9848 //#define DEBUG_MATCHING_NODES
9851 SMESH_MeshEditor::Sew_Error
9852 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9853 set<const SMDS_MeshElement*>& theSide2,
9854 const SMDS_MeshNode* theFirstNode1,
9855 const SMDS_MeshNode* theFirstNode2,
9856 const SMDS_MeshNode* theSecondNode1,
9857 const SMDS_MeshNode* theSecondNode2,
9858 TNodeNodeMap & nReplaceMap)
9860 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9862 nReplaceMap.clear();
9863 if ( theFirstNode1 != theFirstNode2 )
9864 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9865 if ( theSecondNode1 != theSecondNode2 )
9866 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9868 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9869 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9871 list< NLink > linkList[2];
9872 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9873 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9875 // loop on links in linkList; find faces by links and append links
9876 // of the found faces to linkList
9877 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9878 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9879 NLink link[] = { *linkIt[0], *linkIt[1] };
9880 if ( linkSet.find( link[0] ) == linkSet.end() )
9883 // by links, find faces in the face sets,
9884 // and find indices of link nodes in the found faces;
9885 // in a face set, there is only one or no face sharing a link
9886 // ---------------------------------------------------------------
9888 const SMDS_MeshElement* face[] = { 0, 0 };
9889 list<const SMDS_MeshNode*> notLinkNodes[2];
9890 //bool reverse[] = { false, false }; // order of notLinkNodes
9892 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9894 const SMDS_MeshNode* n1 = link[iSide].first;
9895 const SMDS_MeshNode* n2 = link[iSide].second;
9896 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9897 set< const SMDS_MeshElement* > facesOfNode1;
9898 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9900 // during a loop of the first node, we find all faces around n1,
9901 // during a loop of the second node, we find one face sharing both n1 and n2
9902 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9903 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9904 while ( fIt->more() ) { // loop on faces sharing a node
9905 const SMDS_MeshElement* f = fIt->next();
9906 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9907 ! facesOfNode1.insert( f ).second ) // f encounters twice
9909 if ( face[ iSide ] ) {
9910 MESSAGE( "2 faces per link " );
9911 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9914 faceSet->erase( f );
9916 // get not link nodes
9917 int nbN = f->NbNodes();
9918 if ( f->IsQuadratic() )
9920 nbNodes[ iSide ] = nbN;
9921 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9922 int i1 = f->GetNodeIndex( n1 );
9923 int i2 = f->GetNodeIndex( n2 );
9924 int iEnd = nbN, iBeg = -1, iDelta = 1;
9925 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9927 std::swap( iEnd, iBeg ); iDelta = -1;
9932 if ( i == iEnd ) i = iBeg + iDelta;
9933 if ( i == i1 ) break;
9934 nodes.push_back ( f->GetNode( i ) );
9940 // check similarity of elements of the sides
9941 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9942 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9943 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9944 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9947 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9951 // set nodes to merge
9952 // -------------------
9954 if ( face[0] && face[1] ) {
9955 if ( nbNodes[0] != nbNodes[1] ) {
9956 MESSAGE("Diff nb of face nodes");
9957 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9959 #ifdef DEBUG_MATCHING_NODES
9960 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9961 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9962 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9964 int nbN = nbNodes[0];
9966 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9967 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9968 for ( int i = 0 ; i < nbN - 2; ++i ) {
9969 #ifdef DEBUG_MATCHING_NODES
9970 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9972 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9976 // add other links of the face 1 to linkList
9977 // -----------------------------------------
9979 const SMDS_MeshElement* f0 = face[0];
9980 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
9981 for ( int i = 0; i < nbN; i++ )
9983 const SMDS_MeshNode* n2 = f0->GetNode( i );
9984 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
9985 linkSet.insert( SMESH_TLink( n1, n2 ));
9986 if ( !iter_isnew.second ) { // already in a set: no need to process
9987 linkSet.erase( iter_isnew.first );
9989 else // new in set == encountered for the first time: add
9991 #ifdef DEBUG_MATCHING_NODES
9992 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
9993 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
9995 linkList[0].push_back ( NLink( n1, n2 ));
9996 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10001 } // loop on link lists
10006 namespace // automatically find theAffectedElems for DoubleNodes()
10008 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10010 //--------------------------------------------------------------------------------
10011 // Nodes shared by adjacent FissureBorder's.
10012 // 1 node if FissureBorder separates faces
10013 // 2 nodes if FissureBorder separates volumes
10016 const SMDS_MeshNode* _nodes[2];
10019 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10023 _nbNodes = bool( n1 ) + bool( n2 );
10024 if ( _nbNodes == 2 && n1 > n2 )
10025 std::swap( _nodes[0], _nodes[1] );
10027 bool operator<( const SubBorder& other ) const
10029 for ( int i = 0; i < _nbNodes; ++i )
10031 if ( _nodes[i] < other._nodes[i] ) return true;
10032 if ( _nodes[i] > other._nodes[i] ) return false;
10038 //--------------------------------------------------------------------------------
10039 // Map a SubBorder to all FissureBorder it bounds
10040 struct FissureBorder;
10041 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10042 typedef TBorderLinks::iterator TMappedSub;
10044 //--------------------------------------------------------------------------------
10046 * \brief Element border (volume facet or face edge) at a fissure
10048 struct FissureBorder
10050 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10051 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10053 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10054 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10056 FissureBorder( FissureBorder && from ) // move constructor
10058 std::swap( _nodes, from._nodes );
10059 std::swap( _sortedNodes, from._sortedNodes );
10060 _elems[0] = from._elems[0];
10061 _elems[1] = from._elems[1];
10064 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10065 std::vector< const SMDS_MeshElement* > & adjElems)
10066 : _nodes( elemToDuplicate->NbCornerNodes() )
10068 for ( size_t i = 0; i < _nodes.size(); ++i )
10069 _nodes[i] = elemToDuplicate->GetNode( i );
10071 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10072 findAdjacent( type, adjElems );
10075 FissureBorder( const SMDS_MeshNode** nodes,
10076 const size_t nbNodes,
10077 const SMDSAbs_ElementType adjElemsType,
10078 std::vector< const SMDS_MeshElement* > & adjElems)
10079 : _nodes( nodes, nodes + nbNodes )
10081 findAdjacent( adjElemsType, adjElems );
10084 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10085 std::vector< const SMDS_MeshElement* > & adjElems)
10087 _elems[0] = _elems[1] = 0;
10089 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10090 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10091 _elems[i] = adjElems[i];
10094 bool operator<( const FissureBorder& other ) const
10096 return GetSortedNodes() < other.GetSortedNodes();
10099 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10101 if ( _sortedNodes.empty() && !_nodes.empty() )
10103 FissureBorder* me = const_cast<FissureBorder*>( this );
10104 me->_sortedNodes = me->_nodes;
10105 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10107 return _sortedNodes;
10110 size_t NbSub() const
10112 return _nodes.size();
10115 SubBorder Sub(size_t i) const
10117 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10120 void AddSelfTo( TBorderLinks& borderLinks )
10122 _mappedSubs.resize( NbSub() );
10123 for ( size_t i = 0; i < NbSub(); ++i )
10125 TBorderLinks::iterator s2b =
10126 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10127 s2b->second.push_back( this );
10128 _mappedSubs[ i ] = s2b;
10137 const SMDS_MeshElement* GetMarkedElem() const
10139 if ( _nodes.empty() ) return 0; // cleared
10140 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10141 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10145 gp_XYZ GetNorm() const // normal to the border
10148 if ( _nodes.size() == 2 )
10150 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10151 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10153 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10156 gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10157 norm = bordDir ^ avgNorm;
10161 SMESH_NodeXYZ p0( _nodes[0] );
10162 SMESH_NodeXYZ p1( _nodes[1] );
10163 SMESH_NodeXYZ p2( _nodes[2] );
10164 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10166 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10172 void ChooseSide() // mark an _elem located at positive side of fissure
10174 _elems[0]->setIsMarked( true );
10175 gp_XYZ norm = GetNorm();
10176 double maxX = norm.Coord(1);
10177 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10178 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10181 _elems[0]->setIsMarked( false );
10182 _elems[1]->setIsMarked( true );
10186 }; // struct FissureBorder
10188 //--------------------------------------------------------------------------------
10190 * \brief Classifier of elements at fissure edge
10192 class FissureNormal
10194 std::vector< gp_XYZ > _normals;
10198 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10201 _normals.reserve(2);
10202 _normals.push_back( bord.GetNorm() );
10203 if ( _normals.size() == 2 )
10204 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10207 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10210 switch ( _normals.size() ) {
10213 isIn = !isOut( n, _normals[0], elem );
10218 bool in1 = !isOut( n, _normals[0], elem );
10219 bool in2 = !isOut( n, _normals[1], elem );
10220 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10227 //================================================================================
10229 * \brief Classify an element by a plane passing through a node
10231 //================================================================================
10233 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10235 SMESH_NodeXYZ p = n;
10237 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10239 SMESH_NodeXYZ pi = elem->GetNode( i );
10240 sumDot += norm * ( pi - p );
10242 return sumDot < -1e-100;
10245 //================================================================================
10247 * \brief Find FissureBorder's by nodes to duplicate
10249 //================================================================================
10251 void findFissureBorders( const TIDSortedElemSet& theNodes,
10252 std::vector< FissureBorder > & theFissureBorders )
10254 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10255 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10257 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10258 if ( n->NbInverseElements( elemType ) == 0 )
10260 elemType = SMDSAbs_Face;
10261 if ( n->NbInverseElements( elemType ) == 0 )
10264 // unmark elements touching the fissure
10265 for ( ; nIt != theNodes.end(); ++nIt )
10266 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10268 // loop on elements touching the fissure to get their borders belonging to the fissure
10269 std::set< FissureBorder > fissureBorders;
10270 std::vector< const SMDS_MeshElement* > adjElems;
10271 std::vector< const SMDS_MeshNode* > nodes;
10272 SMDS_VolumeTool volTool;
10273 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10275 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10276 while ( invIt->more() )
10278 const SMDS_MeshElement* eInv = invIt->next();
10279 if ( eInv->isMarked() ) continue;
10280 eInv->setIsMarked( true );
10282 if ( elemType == SMDSAbs_Volume )
10284 volTool.Set( eInv );
10285 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10286 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10288 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10289 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10291 bool allOnFissure = true;
10292 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10293 if (( allOnFissure = theNodes.count( nn[ iN ])))
10294 nodes.push_back( nn[ iN ]);
10295 if ( allOnFissure )
10296 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10297 elemType, adjElems )));
10300 else // elemType == SMDSAbs_Face
10302 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10303 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10304 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10306 nn[1] = eInv->GetNode( iN );
10307 onFissure1 = theNodes.count( nn[1] );
10308 if ( onFissure0 && onFissure1 )
10309 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10311 onFissure0 = onFissure1;
10317 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10318 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10319 for ( ; bord != fissureBorders.end(); ++bord )
10321 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10324 } // findFissureBorders()
10326 //================================================================================
10328 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10329 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10330 * \param [in] theNodesNot - nodes not to duplicate
10331 * \param [out] theAffectedElems - the found elements
10333 //================================================================================
10335 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10336 TIDSortedElemSet& theAffectedElems)
10338 if ( theElemsOrNodes.empty() ) return;
10340 // find FissureBorder's
10342 std::vector< FissureBorder > fissure;
10343 std::vector< const SMDS_MeshElement* > elemsByFacet;
10345 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10346 if ( (*elIt)->GetType() == SMDSAbs_Node )
10348 findFissureBorders( theElemsOrNodes, fissure );
10352 fissure.reserve( theElemsOrNodes.size() );
10353 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10354 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10356 if ( fissure.empty() )
10359 // fill borderLinks
10361 TBorderLinks borderLinks;
10363 for ( size_t i = 0; i < fissure.size(); ++i )
10365 fissure[i].AddSelfTo( borderLinks );
10368 // get theAffectedElems
10370 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10371 for ( size_t i = 0; i < fissure.size(); ++i )
10372 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10374 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10375 false, /*markElem=*/true );
10378 std::vector<const SMDS_MeshNode *> facetNodes;
10379 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10380 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10382 // choose a side of fissure
10383 fissure[0].ChooseSide();
10384 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10386 size_t nbCheckedBorders = 0;
10387 while ( nbCheckedBorders < fissure.size() )
10389 // find a FissureBorder to treat
10390 FissureBorder* bord = 0;
10391 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10392 if ( fissure[i].GetMarkedElem() )
10393 bord = & fissure[i];
10394 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10395 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10397 bord = & fissure[i];
10398 bord->ChooseSide();
10399 theAffectedElems.insert( bord->GetMarkedElem() );
10401 if ( !bord ) return;
10402 ++nbCheckedBorders;
10404 // treat FissureBorder's linked to bord
10405 fissureNodes.clear();
10406 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10407 for ( size_t i = 0; i < bord->NbSub(); ++i )
10409 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10410 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10411 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10412 const SubBorder& sb = l2b->first;
10413 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10415 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10417 for ( int j = 0; j < sb._nbNodes; ++j )
10418 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10422 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10423 // until an elem adjacent to a neighbour FissureBorder is found
10424 facetNodes.clear();
10425 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10426 facetNodes.resize( sb._nbNodes + 1 );
10430 // check if bordElem is adjacent to a neighbour FissureBorder
10431 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10433 FissureBorder* bord2 = linkedBorders[j];
10434 if ( bord2 == bord ) continue;
10435 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10438 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10443 // find the next bordElem
10444 const SMDS_MeshElement* nextBordElem = 0;
10445 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10447 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10448 if ( fissureNodes.count( n )) continue;
10450 facetNodes[ sb._nbNodes ] = n;
10451 elemsByFacet.clear();
10452 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10454 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10455 if ( elemsByFacet[ iE ] != bordElem &&
10456 !elemsByFacet[ iE ]->isMarked() )
10458 theAffectedElems.insert( elemsByFacet[ iE ]);
10459 elemsByFacet[ iE ]->setIsMarked( true );
10460 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10461 nextBordElem = elemsByFacet[ iE ];
10465 bordElem = nextBordElem;
10467 } // while ( bordElem )
10469 linkedBorders.clear(); // not to treat this link any more
10471 } // loop on SubBorder's of a FissureBorder
10475 } // loop on FissureBorder's
10478 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10480 // mark nodes of theAffectedElems
10481 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10483 // unmark nodes of the fissure
10484 elIt = theElemsOrNodes.begin();
10485 if ( (*elIt)->GetType() == SMDSAbs_Node )
10486 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10488 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10490 std::vector< gp_XYZ > normVec;
10492 // loop on nodes of the fissure, add elements having marked nodes
10493 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10495 const SMDS_MeshElement* e = (*elIt);
10496 if ( e->GetType() != SMDSAbs_Node )
10497 e->setIsMarked( true ); // avoid adding a fissure element
10499 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10501 const SMDS_MeshNode* n = e->GetNode( iN );
10502 if ( fissEdgeNodes2Norm.count( n ))
10505 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10506 while ( invIt->more() )
10508 const SMDS_MeshElement* eInv = invIt->next();
10509 if ( eInv->isMarked() ) continue;
10510 eInv->setIsMarked( true );
10512 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10513 while( nIt->more() )
10514 if ( nIt->next()->isMarked())
10516 theAffectedElems.insert( eInv );
10517 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10518 n->setIsMarked( false );
10525 // add elements on the fissure edge
10526 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10527 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10529 const SMDS_MeshNode* edgeNode = n2N->first;
10530 const FissureNormal & normals = n2N->second;
10532 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10533 while ( invIt->more() )
10535 const SMDS_MeshElement* eInv = invIt->next();
10536 if ( eInv->isMarked() ) continue;
10537 eInv->setIsMarked( true );
10539 // classify eInv using normals
10540 bool toAdd = normals.IsIn( edgeNode, eInv );
10541 if ( toAdd ) // check if all nodes lie on the fissure edge
10543 bool notOnEdge = false;
10544 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
10545 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10550 theAffectedElems.insert( eInv );
10556 } // findAffectedElems()
10559 //================================================================================
10561 * \brief Create elements equal (on same nodes) to given ones
10562 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10563 * elements of the uppest dimension are duplicated.
10565 //================================================================================
10567 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10569 ClearLastCreated();
10570 SMESHDS_Mesh* mesh = GetMeshDS();
10572 // get an element type and an iterator over elements
10574 SMDSAbs_ElementType type = SMDSAbs_All;
10575 SMDS_ElemIteratorPtr elemIt;
10576 if ( theElements.empty() )
10578 if ( mesh->NbNodes() == 0 )
10580 // get most complex type
10581 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10582 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10583 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10585 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10586 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10589 elemIt = mesh->elementsIterator( type );
10595 //type = (*theElements.begin())->GetType();
10596 elemIt = SMESHUtils::elemSetIterator( theElements );
10599 // un-mark all elements to avoid duplicating just created elements
10600 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10602 // duplicate elements
10604 ElemFeatures elemType;
10606 vector< const SMDS_MeshNode* > nodes;
10607 while ( elemIt->more() )
10609 const SMDS_MeshElement* elem = elemIt->next();
10610 if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10611 ( elem->isMarked() ))
10614 elemType.Init( elem, /*basicOnly=*/false );
10615 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10617 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10618 newElem->setIsMarked( true );
10622 //================================================================================
10624 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10625 \param theElems - the list of elements (edges or faces) to be replicated
10626 The nodes for duplication could be found from these elements
10627 \param theNodesNot - list of nodes to NOT replicate
10628 \param theAffectedElems - the list of elements (cells and edges) to which the
10629 replicated nodes should be associated to.
10630 \return TRUE if operation has been completed successfully, FALSE otherwise
10632 //================================================================================
10634 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10635 const TIDSortedElemSet& theNodesNot,
10636 const TIDSortedElemSet& theAffectedElems )
10638 ClearLastCreated();
10640 if ( theElems.size() == 0 )
10643 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10648 TNodeNodeMap anOldNodeToNewNode;
10649 // duplicate elements and nodes
10650 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10651 // replce nodes by duplications
10652 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10656 //================================================================================
10658 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10659 \param theMeshDS - mesh instance
10660 \param theElems - the elements replicated or modified (nodes should be changed)
10661 \param theNodesNot - nodes to NOT replicate
10662 \param theNodeNodeMap - relation of old node to new created node
10663 \param theIsDoubleElem - flag os to replicate element or modify
10664 \return TRUE if operation has been completed successfully, FALSE otherwise
10666 //================================================================================
10668 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10669 const TIDSortedElemSet& theElems,
10670 const TIDSortedElemSet& theNodesNot,
10671 TNodeNodeMap& theNodeNodeMap,
10672 const bool theIsDoubleElem )
10674 // iterate through element and duplicate them (by nodes duplication)
10676 std::vector<const SMDS_MeshNode*> newNodes;
10677 ElemFeatures elemType;
10679 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10680 for ( ; elemItr != theElems.end(); ++elemItr )
10682 const SMDS_MeshElement* anElem = *elemItr;
10686 // duplicate nodes to duplicate element
10687 bool isDuplicate = false;
10688 newNodes.resize( anElem->NbNodes() );
10689 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10691 while ( anIter->more() )
10693 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10694 const SMDS_MeshNode* aNewNode = aCurrNode;
10695 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10696 if ( n2n != theNodeNodeMap.end() )
10698 aNewNode = n2n->second;
10700 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10703 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10704 copyPosition( aCurrNode, aNewNode );
10705 theNodeNodeMap[ aCurrNode ] = aNewNode;
10706 myLastCreatedNodes.push_back( aNewNode );
10708 isDuplicate |= (aCurrNode != aNewNode);
10709 newNodes[ ind++ ] = aNewNode;
10711 if ( !isDuplicate )
10714 if ( theIsDoubleElem )
10715 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10717 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10724 //================================================================================
10726 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10727 \param theNodes - identifiers of nodes to be doubled
10728 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10729 nodes. If list of element identifiers is empty then nodes are doubled but
10730 they not assigned to elements
10731 \return TRUE if operation has been completed successfully, FALSE otherwise
10733 //================================================================================
10735 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10736 const std::list< int >& theListOfModifiedElems )
10738 ClearLastCreated();
10740 if ( theListOfNodes.size() == 0 )
10743 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10747 // iterate through nodes and duplicate them
10749 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10751 std::list< int >::const_iterator aNodeIter;
10752 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10754 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10760 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10763 copyPosition( aNode, aNewNode );
10764 anOldNodeToNewNode[ aNode ] = aNewNode;
10765 myLastCreatedNodes.push_back( aNewNode );
10769 // Change nodes of elements
10771 std::vector<const SMDS_MeshNode*> aNodeArr;
10773 std::list< int >::const_iterator anElemIter;
10774 for ( anElemIter = theListOfModifiedElems.begin();
10775 anElemIter != theListOfModifiedElems.end();
10778 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10782 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10783 for( size_t i = 0; i < aNodeArr.size(); ++i )
10785 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10786 anOldNodeToNewNode.find( aNodeArr[ i ]);
10787 if ( n2n != anOldNodeToNewNode.end() )
10788 aNodeArr[ i ] = n2n->second;
10790 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10798 //================================================================================
10800 \brief Check if element located inside shape
10801 \return TRUE if IN or ON shape, FALSE otherwise
10803 //================================================================================
10805 template<class Classifier>
10806 bool isInside(const SMDS_MeshElement* theElem,
10807 Classifier& theClassifier,
10808 const double theTol)
10810 gp_XYZ centerXYZ (0, 0, 0);
10811 for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10812 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10814 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10815 theClassifier.Perform(aPnt, theTol);
10816 TopAbs_State aState = theClassifier.State();
10817 return (aState == TopAbs_IN || aState == TopAbs_ON );
10820 //================================================================================
10822 * \brief Classifier of the 3D point on the TopoDS_Face
10823 * with interaface suitable for isInside()
10825 //================================================================================
10827 struct _FaceClassifier
10829 Extrema_ExtPS _extremum;
10830 BRepAdaptor_Surface _surface;
10831 TopAbs_State _state;
10833 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10835 _extremum.Initialize( _surface,
10836 _surface.FirstUParameter(), _surface.LastUParameter(),
10837 _surface.FirstVParameter(), _surface.LastVParameter(),
10838 _surface.Tolerance(), _surface.Tolerance() );
10840 void Perform(const gp_Pnt& aPnt, double theTol)
10843 _state = TopAbs_OUT;
10844 _extremum.Perform(aPnt);
10845 if ( _extremum.IsDone() )
10846 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10847 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10849 TopAbs_State State() const
10856 //================================================================================
10858 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10859 This method is the first step of DoubleNodeElemGroupsInRegion.
10860 \param theElems - list of groups of elements (edges or faces) to be replicated
10861 \param theNodesNot - list of groups of nodes not to replicated
10862 \param theShape - shape to detect affected elements (element which geometric center
10863 located on or inside shape). If the shape is null, detection is done on faces orientations
10864 (select elements with a gravity center on the side given by faces normals).
10865 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10866 The replicated nodes should be associated to affected elements.
10868 \sa DoubleNodeElemGroupsInRegion()
10870 //================================================================================
10872 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10873 const TIDSortedElemSet& theNodesNot,
10874 const TopoDS_Shape& theShape,
10875 TIDSortedElemSet& theAffectedElems)
10877 if ( theShape.IsNull() )
10879 findAffectedElems( theElems, theAffectedElems );
10883 const double aTol = Precision::Confusion();
10884 std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10885 std::unique_ptr<_FaceClassifier> aFaceClassifier;
10886 if ( theShape.ShapeType() == TopAbs_SOLID )
10888 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10889 bsc3d->PerformInfinitePoint(aTol);
10891 else if (theShape.ShapeType() == TopAbs_FACE )
10893 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10896 // iterates on indicated elements and get elements by back references from their nodes
10897 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10898 for ( ; elemItr != theElems.end(); ++elemItr )
10900 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10901 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10902 while ( nodeItr->more() )
10904 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10905 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10907 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10908 while ( backElemItr->more() )
10910 const SMDS_MeshElement* curElem = backElemItr->next();
10911 if ( curElem && theElems.find(curElem) == theElems.end() &&
10913 isInside( curElem, *bsc3d, aTol ) :
10914 isInside( curElem, *aFaceClassifier, aTol )))
10915 theAffectedElems.insert( curElem );
10923 //================================================================================
10925 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10926 \param theElems - group of of elements (edges or faces) to be replicated
10927 \param theNodesNot - group of nodes not to replicate
10928 \param theShape - shape to detect affected elements (element which geometric center
10929 located on or inside shape).
10930 The replicated nodes should be associated to affected elements.
10931 \return TRUE if operation has been completed successfully, FALSE otherwise
10933 //================================================================================
10935 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10936 const TIDSortedElemSet& theNodesNot,
10937 const TopoDS_Shape& theShape )
10939 if ( theShape.IsNull() )
10942 const double aTol = Precision::Confusion();
10943 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10944 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
10945 if ( theShape.ShapeType() == TopAbs_SOLID )
10947 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10948 bsc3d->PerformInfinitePoint(aTol);
10950 else if (theShape.ShapeType() == TopAbs_FACE )
10952 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
10955 // iterates on indicated elements and get elements by back references from their nodes
10956 TIDSortedElemSet anAffected;
10957 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10958 for ( ; elemItr != theElems.end(); ++elemItr )
10960 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10964 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10965 while ( nodeItr->more() )
10967 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10968 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10970 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10971 while ( backElemItr->more() )
10973 const SMDS_MeshElement* curElem = backElemItr->next();
10974 if ( curElem && theElems.find(curElem) == theElems.end() &&
10976 isInside( curElem, *bsc3d, aTol ) :
10977 isInside( curElem, *aFaceClassifier, aTol )))
10978 anAffected.insert( curElem );
10982 return DoubleNodes( theElems, theNodesNot, anAffected );
10986 * \brief compute an oriented angle between two planes defined by four points.
10987 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10988 * @param p0 base of the rotation axe
10989 * @param p1 extremity of the rotation axe
10990 * @param g1 belongs to the first plane
10991 * @param g2 belongs to the second plane
10993 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10995 gp_Vec vref(p0, p1);
10998 gp_Vec n1 = vref.Crossed(v1);
10999 gp_Vec n2 = vref.Crossed(v2);
11001 return n2.AngleWithRef(n1, vref);
11003 catch ( Standard_Failure ) {
11005 return Max( v1.Magnitude(), v2.Magnitude() );
11009 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11010 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11011 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11012 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11013 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11014 * 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.
11015 * 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.
11016 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11017 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11018 * \param theElems - list of groups of volumes, where a group of volume is a set of
11019 * SMDS_MeshElements sorted by Id.
11020 * \param createJointElems - if TRUE, create the elements
11021 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11022 * the boundary between \a theDomains and the rest mesh
11023 * \return TRUE if operation has been completed successfully, FALSE otherwise
11025 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11026 bool createJointElems,
11027 bool onAllBoundaries)
11029 // MESSAGE("----------------------------------------------");
11030 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11031 // MESSAGE("----------------------------------------------");
11033 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11034 meshDS->BuildDownWardConnectivity(true);
11036 SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11038 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11039 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11040 // build the list of nodes shared by 2 or more domains, with their domain indexes
11042 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11043 std::map<int,int>celldom; // cell vtkId --> domain
11044 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11045 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11046 faceDomains.clear();
11048 cellDomains.clear();
11049 nodeDomains.clear();
11050 std::map<int,int> emptyMap;
11051 std::set<int> emptySet;
11054 //MESSAGE(".. Number of domains :"<<theElems.size());
11056 TIDSortedElemSet theRestDomElems;
11057 const int iRestDom = -1;
11058 const int idom0 = onAllBoundaries ? iRestDom : 0;
11059 const int nbDomains = theElems.size();
11061 // Check if the domains do not share an element
11062 for (int idom = 0; idom < nbDomains-1; idom++)
11064 // MESSAGE("... Check of domain #" << idom);
11065 const TIDSortedElemSet& domain = theElems[idom];
11066 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11067 for (; elemItr != domain.end(); ++elemItr)
11069 const SMDS_MeshElement* anElem = *elemItr;
11070 int idombisdeb = idom + 1 ;
11071 // check if the element belongs to a domain further in the list
11072 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11074 const TIDSortedElemSet& domainbis = theElems[idombis];
11075 if ( domainbis.count( anElem ))
11077 MESSAGE(".... Domain #" << idom);
11078 MESSAGE(".... Domain #" << idombis);
11079 throw SALOME_Exception("The domains are not disjoint.");
11086 for (int idom = 0; idom < nbDomains; idom++)
11089 // --- build a map (face to duplicate --> volume to modify)
11090 // with all the faces shared by 2 domains (group of elements)
11091 // and corresponding volume of this domain, for each shared face.
11092 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11094 //MESSAGE("... Neighbors of domain #" << idom);
11095 const TIDSortedElemSet& domain = theElems[idom];
11096 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11097 for (; elemItr != domain.end(); ++elemItr)
11099 const SMDS_MeshElement* anElem = *elemItr;
11102 int vtkId = anElem->GetVtkID();
11103 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11104 int neighborsVtkIds[NBMAXNEIGHBORS];
11105 int downIds[NBMAXNEIGHBORS];
11106 unsigned char downTypes[NBMAXNEIGHBORS];
11107 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11108 for (int n = 0; n < nbNeighbors; n++)
11110 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11111 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11112 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11115 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11117 // MESSAGE("Domain " << idombis);
11118 const TIDSortedElemSet& domainbis = theElems[idombis];
11119 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11121 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11123 DownIdType face(downIds[n], downTypes[n]);
11124 if (!faceDomains[face].count(idom))
11126 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11127 celldom[vtkId] = idom;
11128 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11132 theRestDomElems.insert( elem );
11133 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11134 celldom[neighborsVtkIds[n]] = iRestDom;
11142 //MESSAGE("Number of shared faces " << faceDomains.size());
11143 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11145 // --- explore the shared faces domain by domain,
11146 // explore the nodes of the face and see if they belong to a cell in the domain,
11147 // which has only a node or an edge on the border (not a shared face)
11149 for (int idomain = idom0; idomain < nbDomains; idomain++)
11151 //MESSAGE("Domain " << idomain);
11152 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11153 itface = faceDomains.begin();
11154 for (; itface != faceDomains.end(); ++itface)
11156 const std::map<int, int>& domvol = itface->second;
11157 if (!domvol.count(idomain))
11159 DownIdType face = itface->first;
11160 //MESSAGE(" --- face " << face.cellId);
11161 std::set<int> oldNodes;
11163 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11164 std::set<int>::iterator itn = oldNodes.begin();
11165 for (; itn != oldNodes.end(); ++itn)
11168 //MESSAGE(" node " << oldId);
11169 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11170 for (int i=0; i<l.ncells; i++)
11172 int vtkId = l.cells[i];
11173 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11174 if (!domain.count(anElem))
11176 int vtkType = grid->GetCellType(vtkId);
11177 int downId = grid->CellIdToDownId(vtkId);
11180 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11181 continue; // not OK at this stage of the algorithm:
11182 //no cells created after BuildDownWardConnectivity
11184 DownIdType aCell(downId, vtkType);
11185 cellDomains[aCell][idomain] = vtkId;
11186 celldom[vtkId] = idomain;
11187 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11193 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11194 // for each shared face, get the nodes
11195 // for each node, for each domain of the face, create a clone of the node
11197 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11198 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11199 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11201 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11202 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11203 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11205 //MESSAGE(".. Duplication of the nodes");
11206 for (int idomain = idom0; idomain < nbDomains; idomain++)
11208 itface = faceDomains.begin();
11209 for (; itface != faceDomains.end(); ++itface)
11211 const std::map<int, int>& domvol = itface->second;
11212 if (!domvol.count(idomain))
11214 DownIdType face = itface->first;
11215 //MESSAGE(" --- face " << face.cellId);
11216 std::set<int> oldNodes;
11218 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11219 std::set<int>::iterator itn = oldNodes.begin();
11220 for (; itn != oldNodes.end(); ++itn)
11223 if (nodeDomains[oldId].empty())
11225 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11226 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11228 std::map<int, int>::const_iterator itdom = domvol.begin();
11229 for (; itdom != domvol.end(); ++itdom)
11231 int idom = itdom->first;
11232 //MESSAGE(" domain " << idom);
11233 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11235 if (nodeDomains[oldId].size() >= 2) // a multiple node
11237 vector<int> orderedDoms;
11238 //MESSAGE("multiple node " << oldId);
11239 if (mutipleNodes.count(oldId))
11240 orderedDoms = mutipleNodes[oldId];
11243 map<int,int>::iterator it = nodeDomains[oldId].begin();
11244 for (; it != nodeDomains[oldId].end(); ++it)
11245 orderedDoms.push_back(it->first);
11247 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11248 //stringstream txt;
11249 //for (int i=0; i<orderedDoms.size(); i++)
11250 // txt << orderedDoms[i] << " ";
11251 //MESSAGE("orderedDoms " << txt.str());
11252 mutipleNodes[oldId] = orderedDoms;
11254 double *coords = grid->GetPoint(oldId);
11255 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11256 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11257 int newId = newNode->GetVtkID();
11258 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11259 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11266 //MESSAGE(".. Creation of elements");
11267 for (int idomain = idom0; idomain < nbDomains; idomain++)
11269 itface = faceDomains.begin();
11270 for (; itface != faceDomains.end(); ++itface)
11272 std::map<int, int> domvol = itface->second;
11273 if (!domvol.count(idomain))
11275 DownIdType face = itface->first;
11276 //MESSAGE(" --- face " << face.cellId);
11277 std::set<int> oldNodes;
11279 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11280 int nbMultipleNodes = 0;
11281 std::set<int>::iterator itn = oldNodes.begin();
11282 for (; itn != oldNodes.end(); ++itn)
11285 if (mutipleNodes.count(oldId))
11288 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11290 //MESSAGE("multiple Nodes detected on a shared face");
11291 int downId = itface->first.cellId;
11292 unsigned char cellType = itface->first.cellType;
11293 // --- shared edge or shared face ?
11294 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11297 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11298 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11299 if (mutipleNodes.count(nodes[i]))
11300 if (!mutipleNodesToFace.count(nodes[i]))
11301 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11303 else // shared face (between two volumes)
11305 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11306 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11307 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11308 for (int ie =0; ie < nbEdges; ie++)
11311 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11312 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11314 vector<int> vn0 = mutipleNodes[nodes[0]];
11315 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11317 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11318 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11319 if ( vn0[i0] == vn1[i1] )
11320 doms.push_back( vn0[ i0 ]);
11321 if ( doms.size() > 2 )
11323 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11324 double *coords = grid->GetPoint(nodes[0]);
11325 gp_Pnt p0(coords[0], coords[1], coords[2]);
11326 coords = grid->GetPoint(nodes[nbNodes - 1]);
11327 gp_Pnt p1(coords[0], coords[1], coords[2]);
11329 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11330 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11331 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11332 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11333 for ( size_t id = 0; id < doms.size(); id++ )
11335 int idom = doms[id];
11336 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11337 for ( int ivol = 0; ivol < nbvol; ivol++ )
11339 int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11340 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11341 if (domain.count(elem))
11343 const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11344 domvol[idom] = (SMDS_MeshVolume*) svol;
11345 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11346 double values[3] = { 0,0,0 };
11347 vtkIdType npts = 0;
11348 vtkIdType* pts = 0;
11349 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11350 for ( vtkIdType i = 0; i < npts; ++i )
11352 double *coords = grid->GetPoint( pts[i] );
11353 for ( int j = 0; j < 3; ++j )
11354 values[j] += coords[j] / npts;
11358 gref.SetCoord( values[0], values[1], values[2] );
11359 angleDom[idom] = 0;
11363 gp_Pnt g( values[0], values[1], values[2] );
11364 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11365 //MESSAGE(" angle=" << angleDom[idom]);
11371 map<double, int> sortedDom; // sort domains by angle
11372 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11373 sortedDom[ia->second] = ia->first;
11374 vector<int> vnodes;
11376 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11378 vdom.push_back(ib->second);
11379 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11381 for (int ino = 0; ino < nbNodes; ino++)
11382 vnodes.push_back(nodes[ino]);
11383 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11392 // --- iterate on shared faces (volumes to modify, face to extrude)
11393 // get node id's of the face (id SMDS = id VTK)
11394 // create flat element with old and new nodes if requested
11396 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11397 // (domain1 X domain2) = domain1 + MAXINT*domain2
11399 std::map<int, std::map<long,int> > nodeQuadDomains;
11400 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11402 //MESSAGE(".. Creation of elements: simple junction");
11403 if (createJointElems)
11405 string joints2DName = "joints2D";
11406 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11407 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11408 string joints3DName = "joints3D";
11409 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11410 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11412 itface = faceDomains.begin();
11413 for (; itface != faceDomains.end(); ++itface)
11415 DownIdType face = itface->first;
11416 std::set<int> oldNodes;
11417 std::set<int>::iterator itn;
11419 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11421 std::map<int, int> domvol = itface->second;
11422 std::map<int, int>::iterator itdom = domvol.begin();
11423 int dom1 = itdom->first;
11424 int vtkVolId = itdom->second;
11426 int dom2 = itdom->first;
11427 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11429 stringstream grpname;
11432 grpname << dom1 << "_" << dom2;
11434 grpname << dom2 << "_" << dom1;
11435 string namegrp = grpname.str();
11436 if (!mapOfJunctionGroups.count(namegrp))
11437 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11438 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11440 sgrp->Add(vol->GetID());
11441 if (vol->GetType() == SMDSAbs_Volume)
11442 joints3DGrp->Add(vol->GetID());
11443 else if (vol->GetType() == SMDSAbs_Face)
11444 joints2DGrp->Add(vol->GetID());
11448 // --- create volumes on multiple domain intersection if requested
11449 // iterate on mutipleNodesToFace
11450 // iterate on edgesMultiDomains
11452 //MESSAGE(".. Creation of elements: multiple junction");
11453 if (createJointElems)
11455 // --- iterate on mutipleNodesToFace
11457 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11458 for (; itn != mutipleNodesToFace.end(); ++itn)
11460 int node = itn->first;
11461 vector<int> orderDom = itn->second;
11462 vector<vtkIdType> orderedNodes;
11463 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11464 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11465 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11467 stringstream grpname;
11469 grpname << 0 << "_" << 0;
11470 string namegrp = grpname.str();
11471 if (!mapOfJunctionGroups.count(namegrp))
11472 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11473 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11475 sgrp->Add(face->GetID());
11478 // --- iterate on edgesMultiDomains
11480 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11481 for (; ite != edgesMultiDomains.end(); ++ite)
11483 vector<int> nodes = ite->first;
11484 vector<int> orderDom = ite->second;
11485 vector<vtkIdType> orderedNodes;
11486 if (nodes.size() == 2)
11488 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11489 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11490 if ( orderDom.size() == 3 )
11491 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11492 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11494 for (int idom = orderDom.size()-1; idom >=0; idom--)
11495 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11496 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11498 string namegrp = "jointsMultiples";
11499 if (!mapOfJunctionGroups.count(namegrp))
11500 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11501 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11503 sgrp->Add(vol->GetID());
11507 //INFOS("Quadratic multiple joints not implemented");
11508 // TODO quadratic nodes
11513 // --- list the explicit faces and edges of the mesh that need to be modified,
11514 // i.e. faces and edges built with one or more duplicated nodes.
11515 // associate these faces or edges to their corresponding domain.
11516 // only the first domain found is kept when a face or edge is shared
11518 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11519 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11520 faceOrEdgeDom.clear();
11523 //MESSAGE(".. Modification of elements");
11524 for (int idomain = idom0; idomain < nbDomains; idomain++)
11526 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11527 for (; itnod != nodeDomains.end(); ++itnod)
11529 int oldId = itnod->first;
11530 //MESSAGE(" node " << oldId);
11531 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11532 for (int i = 0; i < l.ncells; i++)
11534 int vtkId = l.cells[i];
11535 int vtkType = grid->GetCellType(vtkId);
11536 int downId = grid->CellIdToDownId(vtkId);
11538 continue; // new cells: not to be modified
11539 DownIdType aCell(downId, vtkType);
11540 int volParents[1000];
11541 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11542 for (int j = 0; j < nbvol; j++)
11543 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11544 if (!feDom.count(vtkId))
11546 feDom[vtkId] = idomain;
11547 faceOrEdgeDom[aCell] = emptyMap;
11548 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11549 //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11550 // << " type " << vtkType << " downId " << downId);
11556 // --- iterate on shared faces (volumes to modify, face to extrude)
11557 // get node id's of the face
11558 // replace old nodes by new nodes in volumes, and update inverse connectivity
11560 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11561 for (int m=0; m<3; m++)
11563 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11564 itface = (*amap).begin();
11565 for (; itface != (*amap).end(); ++itface)
11567 DownIdType face = itface->first;
11568 std::set<int> oldNodes;
11569 std::set<int>::iterator itn;
11571 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11572 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11573 std::map<int, int> localClonedNodeIds;
11575 std::map<int, int> domvol = itface->second;
11576 std::map<int, int>::iterator itdom = domvol.begin();
11577 for (; itdom != domvol.end(); ++itdom)
11579 int idom = itdom->first;
11580 int vtkVolId = itdom->second;
11581 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11582 localClonedNodeIds.clear();
11583 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11586 if (nodeDomains[oldId].count(idom))
11588 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11589 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11592 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11597 // Remove empty groups (issue 0022812)
11598 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11599 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11601 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11602 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11605 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11606 grid->DeleteLinks();
11614 * \brief Double nodes on some external faces and create flat elements.
11615 * Flat elements are mainly used by some types of mechanic calculations.
11617 * Each group of the list must be constituted of faces.
11618 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11619 * @param theElems - list of groups of faces, where a group of faces is a set of
11620 * SMDS_MeshElements sorted by Id.
11621 * @return TRUE if operation has been completed successfully, FALSE otherwise
11623 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11625 // MESSAGE("-------------------------------------------------");
11626 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11627 // MESSAGE("-------------------------------------------------");
11629 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11631 // --- For each group of faces
11632 // duplicate the nodes, create a flat element based on the face
11633 // replace the nodes of the faces by their clones
11635 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11636 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11637 clonedNodes.clear();
11638 intermediateNodes.clear();
11639 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11640 mapOfJunctionGroups.clear();
11642 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11644 const TIDSortedElemSet& domain = theElems[idom];
11645 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11646 for ( ; elemItr != domain.end(); ++elemItr )
11648 const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11651 // MESSAGE("aFace=" << aFace->GetID());
11652 bool isQuad = aFace->IsQuadratic();
11653 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11655 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11657 SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11658 while (nodeIt->more())
11660 const SMDS_MeshNode* node = nodeIt->next();
11661 bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11663 ln2.push_back(node);
11665 ln0.push_back(node);
11667 const SMDS_MeshNode* clone = 0;
11668 if (!clonedNodes.count(node))
11670 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11671 copyPosition( node, clone );
11672 clonedNodes[node] = clone;
11675 clone = clonedNodes[node];
11678 ln3.push_back(clone);
11680 ln1.push_back(clone);
11682 const SMDS_MeshNode* inter = 0;
11683 if (isQuad && (!isMedium))
11685 if (!intermediateNodes.count(node))
11687 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11688 copyPosition( node, inter );
11689 intermediateNodes[node] = inter;
11692 inter = intermediateNodes[node];
11693 ln4.push_back(inter);
11697 // --- extrude the face
11699 vector<const SMDS_MeshNode*> ln;
11700 SMDS_MeshVolume* vol = 0;
11701 vtkIdType aType = aFace->GetVtkType();
11705 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11706 // MESSAGE("vol prism " << vol->GetID());
11707 ln.push_back(ln1[0]);
11708 ln.push_back(ln1[1]);
11709 ln.push_back(ln1[2]);
11712 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11713 // MESSAGE("vol hexa " << vol->GetID());
11714 ln.push_back(ln1[0]);
11715 ln.push_back(ln1[1]);
11716 ln.push_back(ln1[2]);
11717 ln.push_back(ln1[3]);
11719 case VTK_QUADRATIC_TRIANGLE:
11720 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11721 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11722 // MESSAGE("vol quad prism " << vol->GetID());
11723 ln.push_back(ln1[0]);
11724 ln.push_back(ln1[1]);
11725 ln.push_back(ln1[2]);
11726 ln.push_back(ln3[0]);
11727 ln.push_back(ln3[1]);
11728 ln.push_back(ln3[2]);
11730 case VTK_QUADRATIC_QUAD:
11731 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11732 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11733 // ln4[0], ln4[1], ln4[2], ln4[3]);
11734 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11735 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11736 ln4[0], ln4[1], ln4[2], ln4[3]);
11737 // MESSAGE("vol quad hexa " << vol->GetID());
11738 ln.push_back(ln1[0]);
11739 ln.push_back(ln1[1]);
11740 ln.push_back(ln1[2]);
11741 ln.push_back(ln1[3]);
11742 ln.push_back(ln3[0]);
11743 ln.push_back(ln3[1]);
11744 ln.push_back(ln3[2]);
11745 ln.push_back(ln3[3]);
11755 stringstream grpname;
11758 string namegrp = grpname.str();
11759 if (!mapOfJunctionGroups.count(namegrp))
11760 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11761 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11763 sgrp->Add(vol->GetID());
11766 // --- modify the face
11768 const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11775 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11776 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11777 * groups of faces to remove inside the object, (idem edges).
11778 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11780 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11781 const TopoDS_Shape& theShape,
11782 SMESH_NodeSearcher* theNodeSearcher,
11783 const char* groupName,
11784 std::vector<double>& nodesCoords,
11785 std::vector<std::vector<int> >& listOfListOfNodes)
11787 // MESSAGE("--------------------------------");
11788 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11789 // MESSAGE("--------------------------------");
11791 // --- zone of volumes to remove is given :
11792 // 1 either by a geom shape (one or more vertices) and a radius,
11793 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11794 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11795 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11796 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11797 // defined by it's name.
11799 SMESHDS_GroupBase* groupDS = 0;
11800 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11801 while ( groupIt->more() )
11804 SMESH_Group * group = groupIt->next();
11805 if ( !group ) continue;
11806 groupDS = group->GetGroupDS();
11807 if ( !groupDS || groupDS->IsEmpty() ) continue;
11808 std::string grpName = group->GetName();
11809 //MESSAGE("grpName=" << grpName);
11810 if (grpName == groupName)
11816 bool isNodeGroup = false;
11817 bool isNodeCoords = false;
11820 if (groupDS->GetType() != SMDSAbs_Node)
11822 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11825 if (nodesCoords.size() > 0)
11826 isNodeCoords = true; // a list o nodes given by their coordinates
11827 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11829 // --- define groups to build
11831 // --- group of SMDS volumes
11832 string grpvName = groupName;
11833 grpvName += "_vol";
11834 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11837 MESSAGE("group not created " << grpvName);
11840 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11842 // --- group of SMDS faces on the skin
11843 string grpsName = groupName;
11844 grpsName += "_skin";
11845 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11848 MESSAGE("group not created " << grpsName);
11851 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11853 // --- group of SMDS faces internal (several shapes)
11854 string grpiName = groupName;
11855 grpiName += "_internalFaces";
11856 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11859 MESSAGE("group not created " << grpiName);
11862 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11864 // --- group of SMDS faces internal (several shapes)
11865 string grpeiName = groupName;
11866 grpeiName += "_internalEdges";
11867 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11870 MESSAGE("group not created " << grpeiName);
11873 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11875 // --- build downward connectivity
11877 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11878 meshDS->BuildDownWardConnectivity(true);
11879 SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11881 // --- set of volumes detected inside
11883 std::set<int> setOfInsideVol;
11884 std::set<int> setOfVolToCheck;
11886 std::vector<gp_Pnt> gpnts;
11889 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11891 //MESSAGE("group of nodes provided");
11892 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11893 while ( elemIt->more() )
11895 const SMDS_MeshElement* elem = elemIt->next();
11898 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11901 SMDS_MeshElement* vol = 0;
11902 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11903 while (volItr->more())
11905 vol = (SMDS_MeshElement*)volItr->next();
11906 setOfInsideVol.insert(vol->GetVtkID());
11907 sgrp->Add(vol->GetID());
11911 else if (isNodeCoords)
11913 //MESSAGE("list of nodes coordinates provided");
11916 while ( i < nodesCoords.size()-2 )
11918 double x = nodesCoords[i++];
11919 double y = nodesCoords[i++];
11920 double z = nodesCoords[i++];
11921 gp_Pnt p = gp_Pnt(x, y ,z);
11922 gpnts.push_back(p);
11923 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11927 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11929 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11930 TopTools_IndexedMapOfShape vertexMap;
11931 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11932 gp_Pnt p = gp_Pnt(0,0,0);
11933 if (vertexMap.Extent() < 1)
11936 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11938 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11939 p = BRep_Tool::Pnt(vertex);
11940 gpnts.push_back(p);
11941 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11945 if (gpnts.size() > 0)
11947 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11948 //MESSAGE("startNode->nodeId " << nodeId);
11950 double radius2 = radius*radius;
11951 //MESSAGE("radius2 " << radius2);
11953 // --- volumes on start node
11955 setOfVolToCheck.clear();
11956 SMDS_MeshElement* startVol = 0;
11957 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11958 while (volItr->more())
11960 startVol = (SMDS_MeshElement*)volItr->next();
11961 setOfVolToCheck.insert(startVol->GetVtkID());
11963 if (setOfVolToCheck.empty())
11965 MESSAGE("No volumes found");
11969 // --- starting with central volumes then their neighbors, check if they are inside
11970 // or outside the domain, until no more new neighbor volume is inside.
11971 // Fill the group of inside volumes
11973 std::map<int, double> mapOfNodeDistance2;
11974 mapOfNodeDistance2.clear();
11975 std::set<int> setOfOutsideVol;
11976 while (!setOfVolToCheck.empty())
11978 std::set<int>::iterator it = setOfVolToCheck.begin();
11980 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
11981 bool volInside = false;
11982 vtkIdType npts = 0;
11983 vtkIdType* pts = 0;
11984 grid->GetCellPoints(vtkId, npts, pts);
11985 for (int i=0; i<npts; i++)
11987 double distance2 = 0;
11988 if (mapOfNodeDistance2.count(pts[i]))
11990 distance2 = mapOfNodeDistance2[pts[i]];
11991 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
11995 double *coords = grid->GetPoint(pts[i]);
11996 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11998 for ( size_t j = 0; j < gpnts.size(); j++ )
12000 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12001 if (d2 < distance2)
12004 if (distance2 < radius2)
12008 mapOfNodeDistance2[pts[i]] = distance2;
12009 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12011 if (distance2 < radius2)
12013 volInside = true; // one or more nodes inside the domain
12014 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12020 setOfInsideVol.insert(vtkId);
12021 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12022 int neighborsVtkIds[NBMAXNEIGHBORS];
12023 int downIds[NBMAXNEIGHBORS];
12024 unsigned char downTypes[NBMAXNEIGHBORS];
12025 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12026 for (int n = 0; n < nbNeighbors; n++)
12027 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12028 setOfVolToCheck.insert(neighborsVtkIds[n]);
12032 setOfOutsideVol.insert(vtkId);
12033 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12035 setOfVolToCheck.erase(vtkId);
12039 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12040 // If yes, add the volume to the inside set
12042 bool addedInside = true;
12043 std::set<int> setOfVolToReCheck;
12044 while (addedInside)
12046 //MESSAGE(" --------------------------- re check");
12047 addedInside = false;
12048 std::set<int>::iterator itv = setOfInsideVol.begin();
12049 for (; itv != setOfInsideVol.end(); ++itv)
12052 int neighborsVtkIds[NBMAXNEIGHBORS];
12053 int downIds[NBMAXNEIGHBORS];
12054 unsigned char downTypes[NBMAXNEIGHBORS];
12055 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12056 for (int n = 0; n < nbNeighbors; n++)
12057 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12058 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12060 setOfVolToCheck = setOfVolToReCheck;
12061 setOfVolToReCheck.clear();
12062 while (!setOfVolToCheck.empty())
12064 std::set<int>::iterator it = setOfVolToCheck.begin();
12066 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12068 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12069 int countInside = 0;
12070 int neighborsVtkIds[NBMAXNEIGHBORS];
12071 int downIds[NBMAXNEIGHBORS];
12072 unsigned char downTypes[NBMAXNEIGHBORS];
12073 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12074 for (int n = 0; n < nbNeighbors; n++)
12075 if (setOfInsideVol.count(neighborsVtkIds[n]))
12077 //MESSAGE("countInside " << countInside);
12078 if (countInside > 1)
12080 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12081 setOfInsideVol.insert(vtkId);
12082 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12083 addedInside = true;
12086 setOfVolToReCheck.insert(vtkId);
12088 setOfVolToCheck.erase(vtkId);
12092 // --- map of Downward faces at the boundary, inside the global volume
12093 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12094 // fill group of SMDS faces inside the volume (when several volume shapes)
12095 // fill group of SMDS faces on the skin of the global volume (if skin)
12097 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12098 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12099 std::set<int>::iterator it = setOfInsideVol.begin();
12100 for (; it != setOfInsideVol.end(); ++it)
12103 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12104 int neighborsVtkIds[NBMAXNEIGHBORS];
12105 int downIds[NBMAXNEIGHBORS];
12106 unsigned char downTypes[NBMAXNEIGHBORS];
12107 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12108 for (int n = 0; n < nbNeighbors; n++)
12110 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12111 if (neighborDim == 3)
12113 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12115 DownIdType face(downIds[n], downTypes[n]);
12116 boundaryFaces[face] = vtkId;
12118 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12119 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12120 if (vtkFaceId >= 0)
12122 sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12123 // find also the smds edges on this face
12124 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12125 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12126 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12127 for (int i = 0; i < nbEdges; i++)
12129 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12130 if (vtkEdgeId >= 0)
12131 sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12135 else if (neighborDim == 2) // skin of the volume
12137 DownIdType face(downIds[n], downTypes[n]);
12138 skinFaces[face] = vtkId;
12139 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12140 if (vtkFaceId >= 0)
12141 sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12146 // --- identify the edges constituting the wire of each subshape on the skin
12147 // define polylines with the nodes of edges, equivalent to wires
12148 // project polylines on subshapes, and partition, to get geom faces
12150 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12151 std::set<int> emptySet;
12153 std::set<int> shapeIds;
12155 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12156 while (itelem->more())
12158 const SMDS_MeshElement *elem = itelem->next();
12159 int shapeId = elem->getshapeId();
12160 int vtkId = elem->GetVtkID();
12161 if (!shapeIdToVtkIdSet.count(shapeId))
12163 shapeIdToVtkIdSet[shapeId] = emptySet;
12164 shapeIds.insert(shapeId);
12166 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12169 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12170 std::set<DownIdType, DownIdCompare> emptyEdges;
12171 emptyEdges.clear();
12173 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12174 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12176 int shapeId = itShape->first;
12177 //MESSAGE(" --- Shape ID --- "<< shapeId);
12178 shapeIdToEdges[shapeId] = emptyEdges;
12180 std::vector<int> nodesEdges;
12182 std::set<int>::iterator its = itShape->second.begin();
12183 for (; its != itShape->second.end(); ++its)
12186 //MESSAGE(" " << vtkId);
12187 int neighborsVtkIds[NBMAXNEIGHBORS];
12188 int downIds[NBMAXNEIGHBORS];
12189 unsigned char downTypes[NBMAXNEIGHBORS];
12190 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12191 for (int n = 0; n < nbNeighbors; n++)
12193 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12195 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12196 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12197 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12199 DownIdType edge(downIds[n], downTypes[n]);
12200 if (!shapeIdToEdges[shapeId].count(edge))
12202 shapeIdToEdges[shapeId].insert(edge);
12204 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12205 nodesEdges.push_back(vtkNodeId[0]);
12206 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12207 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12213 std::list<int> order;
12215 if (nodesEdges.size() > 0)
12217 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12218 nodesEdges[0] = -1;
12219 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12220 nodesEdges[1] = -1; // do not reuse this edge
12224 int nodeTofind = order.back(); // try first to push back
12226 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12227 if (nodesEdges[i] == nodeTofind)
12229 if ( i == (int) nodesEdges.size() )
12230 found = false; // no follower found on back
12233 if (i%2) // odd ==> use the previous one
12234 if (nodesEdges[i-1] < 0)
12238 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12239 nodesEdges[i-1] = -1;
12241 else // even ==> use the next one
12242 if (nodesEdges[i+1] < 0)
12246 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12247 nodesEdges[i+1] = -1;
12252 // try to push front
12254 nodeTofind = order.front(); // try to push front
12255 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12256 if ( nodesEdges[i] == nodeTofind )
12258 if ( i == (int)nodesEdges.size() )
12260 found = false; // no predecessor found on front
12263 if (i%2) // odd ==> use the previous one
12264 if (nodesEdges[i-1] < 0)
12268 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12269 nodesEdges[i-1] = -1;
12271 else // even ==> use the next one
12272 if (nodesEdges[i+1] < 0)
12276 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12277 nodesEdges[i+1] = -1;
12283 std::vector<int> nodes;
12284 nodes.push_back(shapeId);
12285 std::list<int>::iterator itl = order.begin();
12286 for (; itl != order.end(); itl++)
12288 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12289 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12291 listOfListOfNodes.push_back(nodes);
12294 // partition geom faces with blocFissure
12295 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12296 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12302 //================================================================================
12304 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12305 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12306 * \return TRUE if operation has been completed successfully, FALSE otherwise
12308 //================================================================================
12310 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12312 // iterates on volume elements and detect all free faces on them
12313 SMESHDS_Mesh* aMesh = GetMeshDS();
12317 ElemFeatures faceType( SMDSAbs_Face );
12318 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12319 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12322 const SMDS_MeshVolume* volume = vIt->next();
12323 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12324 vTool.SetExternalNormal();
12325 const int iQuad = volume->IsQuadratic();
12326 faceType.SetQuad( iQuad );
12327 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12329 if (!vTool.IsFreeFace(iface))
12332 vector<const SMDS_MeshNode *> nodes;
12333 int nbFaceNodes = vTool.NbFaceNodes(iface);
12334 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12336 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12337 nodes.push_back(faceNodes[inode]);
12339 if (iQuad) // add medium nodes
12341 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12342 nodes.push_back(faceNodes[inode]);
12343 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12344 nodes.push_back(faceNodes[8]);
12346 // add new face based on volume nodes
12347 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12349 nbExisted++; // face already exists
12353 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12358 return ( nbFree == ( nbExisted + nbCreated ));
12363 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12365 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12367 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12370 //================================================================================
12372 * \brief Creates missing boundary elements
12373 * \param elements - elements whose boundary is to be checked
12374 * \param dimension - defines type of boundary elements to create
12375 * \param group - a group to store created boundary elements in
12376 * \param targetMesh - a mesh to store created boundary elements in
12377 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12378 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12379 * boundary elements will be copied into the targetMesh
12380 * \param toAddExistingBondary - if true, not only new but also pre-existing
12381 * boundary elements will be added into the new group
12382 * \param aroundElements - if true, elements will be created on boundary of given
12383 * elements else, on boundary of the whole mesh.
12384 * \return nb of added boundary elements
12386 //================================================================================
12388 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12389 Bnd_Dimension dimension,
12390 SMESH_Group* group/*=0*/,
12391 SMESH_Mesh* targetMesh/*=0*/,
12392 bool toCopyElements/*=false*/,
12393 bool toCopyExistingBoundary/*=false*/,
12394 bool toAddExistingBondary/*= false*/,
12395 bool aroundElements/*= false*/)
12397 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12398 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12399 // hope that all elements are of the same type, do not check them all
12400 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12401 throw SALOME_Exception(LOCALIZED("wrong element type"));
12404 toCopyElements = toCopyExistingBoundary = false;
12406 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12407 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12408 int nbAddedBnd = 0;
12410 // editor adding present bnd elements and optionally holding elements to add to the group
12411 SMESH_MeshEditor* presentEditor;
12412 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12413 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12415 SMESH_MesherHelper helper( *myMesh );
12416 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12417 SMDS_VolumeTool vTool;
12418 TIDSortedElemSet avoidSet;
12419 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12422 typedef vector<const SMDS_MeshNode*> TConnectivity;
12423 TConnectivity tgtNodes;
12424 ElemFeatures elemKind( missType ), elemToCopy;
12426 vector<const SMDS_MeshElement*> presentBndElems;
12427 vector<TConnectivity> missingBndElems;
12428 vector<int> freeFacets;
12429 TConnectivity nodes, elemNodes;
12431 SMDS_ElemIteratorPtr eIt;
12432 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12433 else eIt = SMESHUtils::elemSetIterator( elements );
12435 while ( eIt->more() )
12437 const SMDS_MeshElement* elem = eIt->next();
12438 const int iQuad = elem->IsQuadratic();
12439 elemKind.SetQuad( iQuad );
12441 // ------------------------------------------------------------------------------------
12442 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12443 // ------------------------------------------------------------------------------------
12444 presentBndElems.clear();
12445 missingBndElems.clear();
12446 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12447 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12449 const SMDS_MeshElement* otherVol = 0;
12450 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12452 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12453 ( !aroundElements || elements.count( otherVol )))
12455 freeFacets.push_back( iface );
12457 if ( missType == SMDSAbs_Face )
12458 vTool.SetExternalNormal();
12459 for ( size_t i = 0; i < freeFacets.size(); ++i )
12461 int iface = freeFacets[i];
12462 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12463 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12464 if ( missType == SMDSAbs_Edge ) // boundary edges
12466 nodes.resize( 2+iQuad );
12467 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12469 for ( size_t j = 0; j < nodes.size(); ++j )
12470 nodes[ j ] = nn[ i+j ];
12471 if ( const SMDS_MeshElement* edge =
12472 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12473 presentBndElems.push_back( edge );
12475 missingBndElems.push_back( nodes );
12478 else // boundary face
12481 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12482 nodes.push_back( nn[inode] ); // add corner nodes
12484 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12485 nodes.push_back( nn[inode] ); // add medium nodes
12486 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12488 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12490 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12491 SMDSAbs_Face, /*noMedium=*/false ))
12492 presentBndElems.push_back( f );
12494 missingBndElems.push_back( nodes );
12496 if ( targetMesh != myMesh )
12498 // add 1D elements on face boundary to be added to a new mesh
12499 const SMDS_MeshElement* edge;
12500 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12503 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12505 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12506 if ( edge && avoidSet.insert( edge ).second )
12507 presentBndElems.push_back( edge );
12513 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12515 avoidSet.clear(), avoidSet.insert( elem );
12516 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12517 SMDS_MeshElement::iterator() );
12518 elemNodes.push_back( elemNodes[0] );
12519 nodes.resize( 2 + iQuad );
12520 const int nbLinks = elem->NbCornerNodes();
12521 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12523 nodes[0] = elemNodes[iN];
12524 nodes[1] = elemNodes[iN+1+iQuad];
12525 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12526 continue; // not free link
12528 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12529 if ( const SMDS_MeshElement* edge =
12530 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12531 presentBndElems.push_back( edge );
12533 missingBndElems.push_back( nodes );
12537 // ---------------------------------
12538 // 2. Add missing boundary elements
12539 // ---------------------------------
12540 if ( targetMesh != myMesh )
12541 // instead of making a map of nodes in this mesh and targetMesh,
12542 // we create nodes with same IDs.
12543 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12545 TConnectivity& srcNodes = missingBndElems[i];
12546 tgtNodes.resize( srcNodes.size() );
12547 for ( inode = 0; inode < srcNodes.size(); ++inode )
12548 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12549 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12551 /*noMedium=*/false))
12553 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12557 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12559 TConnectivity& nodes = missingBndElems[ i ];
12560 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12562 /*noMedium=*/false))
12564 SMDS_MeshElement* newElem =
12565 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12566 nbAddedBnd += bool( newElem );
12568 // try to set a new element to a shape
12569 if ( myMesh->HasShapeToMesh() )
12572 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12573 const size_t nbN = nodes.size() / (iQuad+1 );
12574 for ( inode = 0; inode < nbN && ok; ++inode )
12576 pair<int, TopAbs_ShapeEnum> i_stype =
12577 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12578 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12579 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12581 if ( ok && mediumShapes.size() > 1 )
12583 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12584 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12585 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12587 if (( ok = ( stype_i->first != stype_i_0.first )))
12588 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12589 aMesh->IndexToShape( stype_i_0.second ));
12592 if ( ok && mediumShapes.begin()->first == missShapeType )
12593 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12597 // ----------------------------------
12598 // 3. Copy present boundary elements
12599 // ----------------------------------
12600 if ( toCopyExistingBoundary )
12601 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12603 const SMDS_MeshElement* e = presentBndElems[i];
12604 tgtNodes.resize( e->NbNodes() );
12605 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12606 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12607 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12609 else // store present elements to add them to a group
12610 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12612 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12615 } // loop on given elements
12617 // ---------------------------------------------
12618 // 4. Fill group with boundary elements
12619 // ---------------------------------------------
12622 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12623 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12624 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12626 tgtEditor.myLastCreatedElems.clear();
12627 tgtEditor2.myLastCreatedElems.clear();
12629 // -----------------------
12630 // 5. Copy given elements
12631 // -----------------------
12632 if ( toCopyElements && targetMesh != myMesh )
12634 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12635 else eIt = SMESHUtils::elemSetIterator( elements );
12636 while (eIt->more())
12638 const SMDS_MeshElement* elem = eIt->next();
12639 tgtNodes.resize( elem->NbNodes() );
12640 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12641 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12642 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12644 tgtEditor.myLastCreatedElems.clear();
12650 //================================================================================
12652 * \brief Copy node position and set \a to node on the same geometry
12654 //================================================================================
12656 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12657 const SMDS_MeshNode* to )
12659 if ( !from || !to ) return;
12661 SMDS_PositionPtr pos = from->GetPosition();
12662 if ( !pos || from->getshapeId() < 1 ) return;
12664 switch ( pos->GetTypeOfPosition() )
12666 case SMDS_TOP_3DSPACE: break;
12668 case SMDS_TOP_FACE:
12670 SMDS_FacePositionPtr fPos = pos;
12671 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12672 fPos->GetUParameter(), fPos->GetVParameter() );
12675 case SMDS_TOP_EDGE:
12677 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12678 SMDS_EdgePositionPtr ePos = pos;
12679 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12682 case SMDS_TOP_VERTEX:
12684 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12687 case SMDS_TOP_UNSPEC: