1 // Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
49 #include "utilities.h"
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_ListOfInteger.hxx>
66 #include <TopAbs_State.hxx>
68 #include <TopExp_Explorer.hxx>
69 #include <TopTools_ListIteratorOfListOfShape.hxx>
70 #include <TopTools_ListOfShape.hxx>
71 #include <TopTools_SequenceOfShape.hxx>
73 #include <TopoDS_Edge.hxx>
74 #include <TopoDS_Face.hxx>
75 #include <TopoDS_Solid.hxx>
81 #include <gp_Trsf.hxx>
95 #include <boost/tuple/tuple.hpp>
96 #include <boost/container/flat_set.hpp>
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
101 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
103 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
106 using namespace SMESH::Controls;
108 //=======================================================================
109 //function : SMESH_MeshEditor
111 //=======================================================================
113 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
114 :myMesh( theMesh ) // theMesh may be NULL
118 //================================================================================
120 * \brief Return mesh DS
122 //================================================================================
124 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
126 return myMesh->GetMeshDS();
130 //================================================================================
132 * \brief Clears myLastCreatedNodes and myLastCreatedElems
134 //================================================================================
136 void SMESH_MeshEditor::ClearLastCreated()
138 SMESHUtils::FreeVector( myLastCreatedElems );
139 SMESHUtils::FreeVector( myLastCreatedNodes );
142 //================================================================================
144 * \brief Initializes members by an existing element
145 * \param [in] elem - the source element
146 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
148 //================================================================================
150 SMESH_MeshEditor::ElemFeatures&
151 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
155 myType = elem->GetType();
156 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
158 myIsPoly = elem->IsPoly();
161 myIsQuad = elem->IsQuadratic();
162 if ( myType == SMDSAbs_Volume && !basicOnly )
164 vector<int> quant = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
165 myPolyhedQuantities.swap( quant );
169 else if ( myType == SMDSAbs_Ball && !basicOnly )
171 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
177 //=======================================================================
181 //=======================================================================
184 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
185 const ElemFeatures& features)
187 SMDS_MeshElement* e = 0;
188 int nbnode = node.size();
189 SMESHDS_Mesh* mesh = GetMeshDS();
190 const int ID = features.myID;
192 switch ( features.myType ) {
194 if ( !features.myIsPoly ) {
196 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
197 else e = mesh->AddFace (node[0], node[1], node[2] );
199 else if (nbnode == 4) {
200 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
201 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
203 else if (nbnode == 6) {
204 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
205 node[4], node[5], ID);
206 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
209 else if (nbnode == 7) {
210 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
211 node[4], node[5], node[6], ID);
212 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
213 node[4], node[5], node[6] );
215 else if (nbnode == 8) {
216 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
217 node[4], node[5], node[6], node[7], ID);
218 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
219 node[4], node[5], node[6], node[7] );
221 else if (nbnode == 9) {
222 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
223 node[4], node[5], node[6], node[7], node[8], ID);
224 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
225 node[4], node[5], node[6], node[7], node[8] );
228 else if ( !features.myIsQuad )
230 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
231 else e = mesh->AddPolygonalFace (node );
233 else if ( nbnode % 2 == 0 ) // just a protection
235 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
236 else e = mesh->AddQuadPolygonalFace (node );
241 if ( !features.myIsPoly ) {
243 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
244 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
246 else if (nbnode == 5) {
247 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
249 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
252 else if (nbnode == 6) {
253 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
254 node[4], node[5], ID);
255 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
258 else if (nbnode == 8) {
259 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
260 node[4], node[5], node[6], node[7], ID);
261 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
262 node[4], node[5], node[6], node[7] );
264 else if (nbnode == 10) {
265 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
266 node[4], node[5], node[6], node[7],
267 node[8], node[9], ID);
268 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
269 node[4], node[5], node[6], node[7],
272 else if (nbnode == 12) {
273 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
274 node[4], node[5], node[6], node[7],
275 node[8], node[9], node[10], node[11], ID);
276 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
277 node[4], node[5], node[6], node[7],
278 node[8], node[9], node[10], node[11] );
280 else if (nbnode == 13) {
281 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
282 node[4], node[5], node[6], node[7],
283 node[8], node[9], node[10],node[11],
285 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
286 node[4], node[5], node[6], node[7],
287 node[8], node[9], node[10],node[11],
290 else if (nbnode == 15) {
291 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
292 node[4], node[5], node[6], node[7],
293 node[8], node[9], node[10],node[11],
294 node[12],node[13],node[14],ID);
295 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
296 node[4], node[5], node[6], node[7],
297 node[8], node[9], node[10],node[11],
298 node[12],node[13],node[14] );
300 else if (nbnode == 20) {
301 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
302 node[4], node[5], node[6], node[7],
303 node[8], node[9], node[10],node[11],
304 node[12],node[13],node[14],node[15],
305 node[16],node[17],node[18],node[19],ID);
306 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
307 node[4], node[5], node[6], node[7],
308 node[8], node[9], node[10],node[11],
309 node[12],node[13],node[14],node[15],
310 node[16],node[17],node[18],node[19] );
312 else if (nbnode == 27) {
313 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
314 node[4], node[5], node[6], node[7],
315 node[8], node[9], node[10],node[11],
316 node[12],node[13],node[14],node[15],
317 node[16],node[17],node[18],node[19],
318 node[20],node[21],node[22],node[23],
319 node[24],node[25],node[26], ID);
320 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
321 node[4], node[5], node[6], node[7],
322 node[8], node[9], node[10],node[11],
323 node[12],node[13],node[14],node[15],
324 node[16],node[17],node[18],node[19],
325 node[20],node[21],node[22],node[23],
326 node[24],node[25],node[26] );
329 else if ( !features.myIsQuad )
331 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
332 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
336 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
337 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
343 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
344 else e = mesh->AddEdge (node[0], node[1] );
346 else if ( nbnode == 3 ) {
347 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
348 else e = mesh->AddEdge (node[0], node[1], node[2] );
352 case SMDSAbs_0DElement:
354 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
355 else e = mesh->Add0DElement (node[0] );
360 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
361 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
365 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
366 else e = mesh->AddBall (node[0], features.myBallDiameter );
371 if ( e ) myLastCreatedElems.push_back( e );
375 //=======================================================================
379 //=======================================================================
381 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
382 const ElemFeatures& features)
384 vector<const SMDS_MeshNode*> nodes;
385 nodes.reserve( nodeIDs.size() );
386 vector<int>::const_iterator id = nodeIDs.begin();
387 while ( id != nodeIDs.end() ) {
388 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
389 nodes.push_back( node );
393 return AddElement( nodes, features );
396 //=======================================================================
398 //purpose : Remove a node or an element.
399 // Modify a compute state of sub-meshes which become empty
400 //=======================================================================
402 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
407 SMESHDS_Mesh* aMesh = GetMeshDS();
408 set< SMESH_subMesh *> smmap;
411 list<int>::const_iterator it = theIDs.begin();
412 for ( ; it != theIDs.end(); it++ ) {
413 const SMDS_MeshElement * elem;
415 elem = aMesh->FindNode( *it );
417 elem = aMesh->FindElement( *it );
421 // Notify VERTEX sub-meshes about modification
423 const SMDS_MeshNode* node = cast2Node( elem );
424 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
425 if ( int aShapeID = node->getshapeId() )
426 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
429 // Find sub-meshes to notify about modification
430 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
431 // while ( nodeIt->more() ) {
432 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
433 // const SMDS_PositionPtr& aPosition = node->GetPosition();
434 // if ( aPosition.get() ) {
435 // if ( int aShapeID = aPosition->GetShapeId() ) {
436 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
437 // smmap.insert( sm );
444 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
446 aMesh->RemoveElement( elem );
450 // Notify sub-meshes about modification
451 if ( !smmap.empty() ) {
452 set< SMESH_subMesh *>::iterator smIt;
453 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
454 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
457 // // Check if the whole mesh becomes empty
458 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
459 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
464 //================================================================================
466 * \brief Create 0D elements on all nodes of the given object.
467 * \param elements - Elements on whose nodes to create 0D elements; if empty,
468 * the all mesh is treated
469 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
470 * \param duplicateElements - to add one more 0D element to a node or not
472 //================================================================================
474 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
475 TIDSortedElemSet& all0DElems,
476 const bool duplicateElements )
478 SMDS_ElemIteratorPtr elemIt;
479 if ( elements.empty() )
481 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
485 elemIt = SMESHUtils::elemSetIterator( elements );
488 while ( elemIt->more() )
490 const SMDS_MeshElement* e = elemIt->next();
491 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
492 while ( nodeIt->more() )
494 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
495 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
496 if ( duplicateElements || !it0D->more() )
498 myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
499 all0DElems.insert( myLastCreatedElems.back() );
501 while ( it0D->more() )
502 all0DElems.insert( it0D->next() );
507 //=======================================================================
508 //function : FindShape
509 //purpose : Return an index of the shape theElem is on
510 // or zero if a shape not found
511 //=======================================================================
513 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
517 SMESHDS_Mesh * aMesh = GetMeshDS();
518 if ( aMesh->ShapeToMesh().IsNull() )
521 int aShapeID = theElem->getshapeId();
525 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
526 if ( sm->Contains( theElem ))
529 if ( theElem->GetType() == SMDSAbs_Node ) {
530 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
533 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
536 TopoDS_Shape aShape; // the shape a node of theElem is on
537 if ( theElem->GetType() != SMDSAbs_Node )
539 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
540 while ( nodeIt->more() ) {
541 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
542 if ((aShapeID = node->getshapeId()) > 0) {
543 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
544 if ( sm->Contains( theElem ))
546 if ( aShape.IsNull() )
547 aShape = aMesh->IndexToShape( aShapeID );
553 // None of nodes is on a proper shape,
554 // find the shape among ancestors of aShape on which a node is
555 if ( !aShape.IsNull() ) {
556 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
557 for ( ; ancIt.More(); ancIt.Next() ) {
558 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
559 if ( sm && sm->Contains( theElem ))
560 return aMesh->ShapeToIndex( ancIt.Value() );
565 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
566 while ( const SMESHDS_SubMesh* sm = smIt->next() )
567 if ( sm->Contains( theElem ))
574 //=======================================================================
575 //function : IsMedium
577 //=======================================================================
579 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
580 const SMDSAbs_ElementType typeToCheck)
582 bool isMedium = false;
583 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
584 while (it->more() && !isMedium ) {
585 const SMDS_MeshElement* elem = it->next();
586 isMedium = elem->IsMediumNode(node);
591 //=======================================================================
592 //function : shiftNodesQuadTria
593 //purpose : Shift nodes in the array corresponded to quadratic triangle
594 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
595 //=======================================================================
597 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
599 const SMDS_MeshNode* nd1 = aNodes[0];
600 aNodes[0] = aNodes[1];
601 aNodes[1] = aNodes[2];
603 const SMDS_MeshNode* nd2 = aNodes[3];
604 aNodes[3] = aNodes[4];
605 aNodes[4] = aNodes[5];
609 //=======================================================================
610 //function : getNodesFromTwoTria
612 //=======================================================================
614 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
615 const SMDS_MeshElement * theTria2,
616 vector< const SMDS_MeshNode*>& N1,
617 vector< const SMDS_MeshNode*>& N2)
619 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
620 if ( N1.size() < 6 ) return false;
621 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
622 if ( N2.size() < 6 ) return false;
624 int sames[3] = {-1,-1,-1};
636 if(nbsames!=2) return false;
638 shiftNodesQuadTria(N1);
640 shiftNodesQuadTria(N1);
643 i = sames[0] + sames[1] + sames[2];
645 shiftNodesQuadTria(N2);
647 // now we receive following N1 and N2 (using numeration as in the image below)
648 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
649 // i.e. first nodes from both arrays form a new diagonal
653 //=======================================================================
654 //function : InverseDiag
655 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
656 // but having other common link.
657 // Return False if args are improper
658 //=======================================================================
660 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
661 const SMDS_MeshElement * theTria2 )
665 if ( !theTria1 || !theTria2 ||
666 !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
667 !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
668 theTria1->GetType() != SMDSAbs_Face ||
669 theTria2->GetType() != SMDSAbs_Face )
672 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
673 (theTria2->GetEntityType() == SMDSEntity_Triangle))
675 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
676 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
680 // put nodes in array and find out indices of the same ones
681 const SMDS_MeshNode* aNodes [6];
682 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
684 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
685 while ( it->more() ) {
686 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
688 if ( i > 2 ) // theTria2
689 // find same node of theTria1
690 for ( int j = 0; j < 3; j++ )
691 if ( aNodes[ i ] == aNodes[ j ]) {
700 return false; // theTria1 is not a triangle
701 it = theTria2->nodesIterator();
703 if ( i == 6 && it->more() )
704 return false; // theTria2 is not a triangle
707 // find indices of 1,2 and of A,B in theTria1
708 int iA = -1, iB = 0, i1 = 0, i2 = 0;
709 for ( i = 0; i < 6; i++ ) {
710 if ( sameInd [ i ] == -1 ) {
715 if ( iA >= 0) iB = i;
719 // nodes 1 and 2 should not be the same
720 if ( aNodes[ i1 ] == aNodes[ i2 ] )
724 aNodes[ iA ] = aNodes[ i2 ];
726 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
728 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
729 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
733 } // end if(F1 && F2)
735 // check case of quadratic faces
736 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
737 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
739 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
740 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
744 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
745 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
753 vector< const SMDS_MeshNode* > N1;
754 vector< const SMDS_MeshNode* > N2;
755 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
757 // now we receive following N1 and N2 (using numeration as above image)
758 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
759 // i.e. first nodes from both arrays determ new diagonal
761 vector< const SMDS_MeshNode*> N1new( N1.size() );
762 vector< const SMDS_MeshNode*> N2new( N2.size() );
763 N1new.back() = N1.back(); // central node of biquadratic
764 N2new.back() = N2.back();
765 N1new[0] = N1[0]; N2new[0] = N1[0];
766 N1new[1] = N2[0]; N2new[1] = N1[1];
767 N1new[2] = N2[1]; N2new[2] = N2[0];
768 N1new[3] = N1[4]; N2new[3] = N1[3];
769 N1new[4] = N2[3]; N2new[4] = N2[5];
770 N1new[5] = N1[5]; N2new[5] = N1[4];
771 // change nodes in faces
772 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
773 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
775 // move the central node of biquadratic triangle
776 SMESH_MesherHelper helper( *GetMesh() );
777 for ( int is2nd = 0; is2nd < 2; ++is2nd )
779 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
780 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
781 if ( nodes.size() < 7 )
783 helper.SetSubShape( tria->getshapeId() );
784 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
788 xyz = ( SMESH_NodeXYZ( nodes[3] ) +
789 SMESH_NodeXYZ( nodes[4] ) +
790 SMESH_NodeXYZ( nodes[5] )) / 3.;
795 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
796 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
797 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
799 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
800 xyz = S->Value( uv.X(), uv.Y() );
801 xyz.Transform( loc );
802 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
803 nodes[6]->getshapeId() > 0 )
804 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
806 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
811 //=======================================================================
812 //function : findTriangles
813 //purpose : find triangles sharing theNode1-theNode2 link
814 //=======================================================================
816 static bool findTriangles(const SMDS_MeshNode * theNode1,
817 const SMDS_MeshNode * theNode2,
818 const SMDS_MeshElement*& theTria1,
819 const SMDS_MeshElement*& theTria2)
821 if ( !theNode1 || !theNode2 ) return false;
823 theTria1 = theTria2 = 0;
825 set< const SMDS_MeshElement* > emap;
826 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
828 const SMDS_MeshElement* elem = it->next();
829 if ( elem->NbCornerNodes() == 3 )
832 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
834 const SMDS_MeshElement* elem = it->next();
835 if ( emap.count( elem )) {
843 // theTria1 must be element with minimum ID
844 if ( theTria2->GetID() < theTria1->GetID() )
845 std::swap( theTria2, theTria1 );
853 //=======================================================================
854 //function : InverseDiag
855 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
856 // with ones built on the same 4 nodes but having other common link.
857 // Return false if proper faces not found
858 //=======================================================================
860 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
861 const SMDS_MeshNode * theNode2)
865 const SMDS_MeshElement *tr1, *tr2;
866 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
869 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
870 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
873 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
874 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
876 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
877 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
881 // put nodes in array
882 // and find indices of 1,2 and of A in tr1 and of B in tr2
883 int i, iA1 = 0, i1 = 0;
884 const SMDS_MeshNode* aNodes1 [3];
885 SMDS_ElemIteratorPtr it;
886 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
887 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
888 if ( aNodes1[ i ] == theNode1 )
889 iA1 = i; // node A in tr1
890 else if ( aNodes1[ i ] != theNode2 )
894 const SMDS_MeshNode* aNodes2 [3];
895 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
896 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
897 if ( aNodes2[ i ] == theNode2 )
898 iB2 = i; // node B in tr2
899 else if ( aNodes2[ i ] != theNode1 )
903 // nodes 1 and 2 should not be the same
904 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
908 aNodes1[ iA1 ] = aNodes2[ i2 ];
910 aNodes2[ iB2 ] = aNodes1[ i1 ];
912 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
913 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
918 // check case of quadratic faces
919 return InverseDiag(tr1,tr2);
922 //=======================================================================
923 //function : getQuadrangleNodes
924 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
925 // fusion of triangles tr1 and tr2 having shared link on
926 // theNode1 and theNode2
927 //=======================================================================
929 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
930 const SMDS_MeshNode * theNode1,
931 const SMDS_MeshNode * theNode2,
932 const SMDS_MeshElement * tr1,
933 const SMDS_MeshElement * tr2 )
935 if( tr1->NbNodes() != tr2->NbNodes() )
937 // find the 4-th node to insert into tr1
938 const SMDS_MeshNode* n4 = 0;
939 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
941 while ( !n4 && i<3 ) {
942 const SMDS_MeshNode * n = cast2Node( it->next() );
944 bool isDiag = ( n == theNode1 || n == theNode2 );
948 // Make an array of nodes to be in a quadrangle
949 int iNode = 0, iFirstDiag = -1;
950 it = tr1->nodesIterator();
953 const SMDS_MeshNode * n = cast2Node( it->next() );
955 bool isDiag = ( n == theNode1 || n == theNode2 );
957 if ( iFirstDiag < 0 )
959 else if ( iNode - iFirstDiag == 1 )
960 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
962 else if ( n == n4 ) {
963 return false; // tr1 and tr2 should not have all the same nodes
965 theQuadNodes[ iNode++ ] = n;
967 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
968 theQuadNodes[ iNode ] = n4;
973 //=======================================================================
974 //function : DeleteDiag
975 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
976 // with a quadrangle built on the same 4 nodes.
977 // Return false if proper faces not found
978 //=======================================================================
980 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
981 const SMDS_MeshNode * theNode2)
985 const SMDS_MeshElement *tr1, *tr2;
986 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
989 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
990 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
993 SMESHDS_Mesh * aMesh = GetMeshDS();
995 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
996 (tr2->GetEntityType() == SMDSEntity_Triangle))
998 const SMDS_MeshNode* aNodes [ 4 ];
999 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1002 const SMDS_MeshElement* newElem = 0;
1003 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1004 myLastCreatedElems.push_back(newElem);
1005 AddToSameGroups( newElem, tr1, aMesh );
1006 int aShapeId = tr1->getshapeId();
1008 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1010 aMesh->RemoveElement( tr1 );
1011 aMesh->RemoveElement( tr2 );
1016 // check case of quadratic faces
1017 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1019 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1023 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1024 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1032 vector< const SMDS_MeshNode* > N1;
1033 vector< const SMDS_MeshNode* > N2;
1034 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1036 // now we receive following N1 and N2 (using numeration as above image)
1037 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1038 // i.e. first nodes from both arrays determ new diagonal
1040 const SMDS_MeshNode* aNodes[8];
1050 const SMDS_MeshElement* newElem = 0;
1051 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1052 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1053 myLastCreatedElems.push_back(newElem);
1054 AddToSameGroups( newElem, tr1, aMesh );
1055 int aShapeId = tr1->getshapeId();
1058 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1060 aMesh->RemoveElement( tr1 );
1061 aMesh->RemoveElement( tr2 );
1063 // remove middle node (9)
1064 GetMeshDS()->RemoveNode( N1[4] );
1069 //=======================================================================
1070 //function : Reorient
1071 //purpose : Reverse theElement orientation
1072 //=======================================================================
1074 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1080 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1081 if ( !it || !it->more() )
1084 const SMDSAbs_ElementType type = theElem->GetType();
1085 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1088 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1089 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1091 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1093 MESSAGE("Warning: bad volumic element");
1096 const int nbFaces = aPolyedre->NbFaces();
1097 vector<const SMDS_MeshNode *> poly_nodes;
1098 vector<int> quantities (nbFaces);
1100 // reverse each face of the polyedre
1101 for (int iface = 1; iface <= nbFaces; iface++) {
1102 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1103 quantities[iface - 1] = nbFaceNodes;
1105 for (inode = nbFaceNodes; inode >= 1; inode--) {
1106 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1107 poly_nodes.push_back(curNode);
1110 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1112 else // other elements
1114 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1115 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1116 if ( interlace.empty() )
1118 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1122 SMDS_MeshCell::applyInterlace( interlace, nodes );
1124 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1129 //================================================================================
1131 * \brief Reorient faces.
1132 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1133 * \param theDirection - desired direction of normal of \a theFace
1134 * \param theFace - one of \a theFaces that should be oriented according to
1135 * \a theDirection and whose orientation defines orientation of other faces
1136 * \return number of reoriented faces.
1138 //================================================================================
1140 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1141 const gp_Dir& theDirection,
1142 const SMDS_MeshElement * theFace)
1145 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1147 if ( theFaces.empty() )
1149 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
1150 while ( fIt->more() )
1151 theFaces.insert( theFaces.end(), fIt->next() );
1154 // orient theFace according to theDirection
1156 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1157 if ( normal * theDirection.XYZ() < 0 )
1158 nbReori += Reorient( theFace );
1160 // Orient other faces
1162 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1163 TIDSortedElemSet avoidSet;
1164 set< SMESH_TLink > checkedLinks;
1165 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1167 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1168 theFaces.erase( theFace );
1169 startFaces.insert( theFace );
1171 int nodeInd1, nodeInd2;
1172 const SMDS_MeshElement* otherFace;
1173 vector< const SMDS_MeshElement* > facesNearLink;
1174 vector< std::pair< int, int > > nodeIndsOfFace;
1176 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1177 while ( !startFaces.empty() )
1179 startFace = startFaces.begin();
1180 theFace = *startFace;
1181 startFaces.erase( startFace );
1182 if ( !visitedFaces.insert( theFace ).second )
1186 avoidSet.insert(theFace);
1188 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1190 const int nbNodes = theFace->NbCornerNodes();
1191 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1193 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1194 linkIt_isNew = checkedLinks.insert( link );
1195 if ( !linkIt_isNew.second )
1197 // link has already been checked and won't be encountered more
1198 // if the group (theFaces) is manifold
1199 //checkedLinks.erase( linkIt_isNew.first );
1203 facesNearLink.clear();
1204 nodeIndsOfFace.clear();
1205 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1207 &nodeInd1, &nodeInd2 )))
1208 if ( otherFace != theFace)
1210 facesNearLink.push_back( otherFace );
1211 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1212 avoidSet.insert( otherFace );
1214 if ( facesNearLink.size() > 1 )
1216 // NON-MANIFOLD mesh shell !
1217 // select a face most co-directed with theFace,
1218 // other faces won't be visited this time
1220 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1221 double proj, maxProj = -1;
1222 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1223 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1224 if (( proj = Abs( NF * NOF )) > maxProj ) {
1226 otherFace = facesNearLink[i];
1227 nodeInd1 = nodeIndsOfFace[i].first;
1228 nodeInd2 = nodeIndsOfFace[i].second;
1231 // not to visit rejected faces
1232 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1233 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1234 visitedFaces.insert( facesNearLink[i] );
1236 else if ( facesNearLink.size() == 1 )
1238 otherFace = facesNearLink[0];
1239 nodeInd1 = nodeIndsOfFace.back().first;
1240 nodeInd2 = nodeIndsOfFace.back().second;
1242 if ( otherFace && otherFace != theFace)
1244 // link must be reverse in otherFace if orientation to otherFace
1245 // is same as that of theFace
1246 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1248 nbReori += Reorient( otherFace );
1250 startFaces.insert( otherFace );
1253 std::swap( link.first, link.second ); // reverse the link
1259 //================================================================================
1261 * \brief Reorient faces basing on orientation of adjacent volumes.
1262 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1263 * \param theVolumes - reference volumes.
1264 * \param theOutsideNormal - to orient faces to have their normal
1265 * pointing either \a outside or \a inside the adjacent volumes.
1266 * \return number of reoriented faces.
1268 //================================================================================
1270 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1271 TIDSortedElemSet & theVolumes,
1272 const bool theOutsideNormal)
1276 SMDS_ElemIteratorPtr faceIt;
1277 if ( theFaces.empty() )
1278 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1280 faceIt = SMESHUtils::elemSetIterator( theFaces );
1282 vector< const SMDS_MeshNode* > faceNodes;
1283 TIDSortedElemSet checkedVolumes;
1284 set< const SMDS_MeshNode* > faceNodesSet;
1285 SMDS_VolumeTool volumeTool;
1287 while ( faceIt->more() ) // loop on given faces
1289 const SMDS_MeshElement* face = faceIt->next();
1290 if ( face->GetType() != SMDSAbs_Face )
1293 const size_t nbCornersNodes = face->NbCornerNodes();
1294 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1296 checkedVolumes.clear();
1297 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1298 while ( vIt->more() )
1300 const SMDS_MeshElement* volume = vIt->next();
1302 if ( !checkedVolumes.insert( volume ).second )
1304 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1307 // is volume adjacent?
1308 bool allNodesCommon = true;
1309 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1310 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1311 if ( !allNodesCommon )
1314 // get nodes of a corresponding volume facet
1315 faceNodesSet.clear();
1316 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1317 volumeTool.Set( volume );
1318 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1319 if ( facetID < 0 ) continue;
1320 volumeTool.SetExternalNormal();
1321 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1323 // compare order of faceNodes and facetNodes
1324 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1326 for ( int i = 0; i < 2; ++i )
1328 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1329 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1330 if ( faceNodes[ iN ] == n )
1336 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1337 if ( isOutside != theOutsideNormal )
1338 nbReori += Reorient( face );
1340 } // loop on given faces
1345 //=======================================================================
1346 //function : getBadRate
1348 //=======================================================================
1350 static double getBadRate (const SMDS_MeshElement* theElem,
1351 SMESH::Controls::NumericalFunctorPtr& theCrit)
1353 SMESH::Controls::TSequenceOfXYZ P;
1354 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1356 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1357 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1360 //=======================================================================
1361 //function : QuadToTri
1362 //purpose : Cut quadrangles into triangles.
1363 // theCrit is used to select a diagonal to cut
1364 //=======================================================================
1366 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1367 SMESH::Controls::NumericalFunctorPtr theCrit)
1371 if ( !theCrit.get() )
1374 SMESHDS_Mesh * aMesh = GetMeshDS();
1375 Handle(Geom_Surface) surface;
1376 SMESH_MesherHelper helper( *GetMesh() );
1378 myLastCreatedElems.reserve( theElems.size() * 2 );
1380 TIDSortedElemSet::iterator itElem;
1381 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1383 const SMDS_MeshElement* elem = *itElem;
1384 if ( !elem || elem->GetType() != SMDSAbs_Face )
1386 if ( elem->NbCornerNodes() != 4 )
1389 // retrieve element nodes
1390 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1392 // compare two sets of possible triangles
1393 double aBadRate1, aBadRate2; // to what extent a set is bad
1394 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1395 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1396 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1398 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1399 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1400 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1402 const int aShapeId = FindShape( elem );
1403 const SMDS_MeshElement* newElem1 = 0;
1404 const SMDS_MeshElement* newElem2 = 0;
1406 if ( !elem->IsQuadratic() ) // split linear quadrangle
1408 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1409 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1410 if ( aBadRate1 <= aBadRate2 ) {
1411 // tr1 + tr2 is better
1412 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1413 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1416 // tr3 + tr4 is better
1417 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1418 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1421 else // split quadratic quadrangle
1423 helper.SetIsQuadratic( true );
1424 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1426 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1427 if ( aNodes.size() == 9 )
1429 helper.SetIsBiQuadratic( true );
1430 if ( aBadRate1 <= aBadRate2 )
1431 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1433 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1435 // create a new element
1436 if ( aBadRate1 <= aBadRate2 ) {
1437 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1438 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1441 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1442 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1446 // care of a new element
1448 myLastCreatedElems.push_back(newElem1);
1449 myLastCreatedElems.push_back(newElem2);
1450 AddToSameGroups( newElem1, elem, aMesh );
1451 AddToSameGroups( newElem2, elem, aMesh );
1453 // put a new triangle on the same shape
1455 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1456 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1458 aMesh->RemoveElement( elem );
1463 //=======================================================================
1465 * \brief Split each of given quadrangles into 4 triangles.
1466 * \param theElems - The faces to be split. If empty all faces are split.
1468 //=======================================================================
1470 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1473 myLastCreatedElems.reserve( theElems.size() * 4 );
1475 SMESH_MesherHelper helper( *GetMesh() );
1476 helper.SetElementsOnShape( true );
1478 SMDS_ElemIteratorPtr faceIt;
1479 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1480 else faceIt = SMESHUtils::elemSetIterator( theElems );
1483 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1485 vector< const SMDS_MeshNode* > nodes;
1486 SMESHDS_SubMesh* subMeshDS = 0;
1488 Handle(Geom_Surface) surface;
1489 TopLoc_Location loc;
1491 while ( faceIt->more() )
1493 const SMDS_MeshElement* quad = faceIt->next();
1494 if ( !quad || quad->NbCornerNodes() != 4 )
1497 // get a surface the quad is on
1499 if ( quad->getshapeId() < 1 )
1502 helper.SetSubShape( 0 );
1505 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1507 helper.SetSubShape( quad->getshapeId() );
1508 if ( !helper.GetSubShape().IsNull() &&
1509 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1511 F = TopoDS::Face( helper.GetSubShape() );
1512 surface = BRep_Tool::Surface( F, loc );
1513 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1517 helper.SetSubShape( 0 );
1522 // create a central node
1524 const SMDS_MeshNode* nCentral;
1525 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1527 if ( nodes.size() == 9 )
1529 nCentral = nodes.back();
1536 for ( ; iN < nodes.size(); ++iN )
1537 xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1539 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1540 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1542 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1543 xyz[0], xyz[1], xyz[2], xyz[3],
1544 xyz[4], xyz[5], xyz[6], xyz[7] );
1548 for ( ; iN < nodes.size(); ++iN )
1549 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1551 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1552 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1554 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1555 uv[0], uv[1], uv[2], uv[3],
1556 uv[4], uv[5], uv[6], uv[7] );
1558 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1562 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1563 uv[8].X(), uv[8].Y() );
1564 myLastCreatedNodes.push_back( nCentral );
1567 // create 4 triangles
1569 helper.SetIsQuadratic ( nodes.size() > 4 );
1570 helper.SetIsBiQuadratic( nodes.size() == 9 );
1571 if ( helper.GetIsQuadratic() )
1572 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1574 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1576 for ( int i = 0; i < 4; ++i )
1578 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1581 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1582 myLastCreatedElems.push_back( tria );
1587 //=======================================================================
1588 //function : BestSplit
1589 //purpose : Find better diagonal for cutting.
1590 //=======================================================================
1592 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1593 SMESH::Controls::NumericalFunctorPtr theCrit)
1600 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1603 if( theQuad->NbNodes()==4 ||
1604 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1606 // retrieve element nodes
1607 const SMDS_MeshNode* aNodes [4];
1608 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1610 //while (itN->more())
1612 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1614 // compare two sets of possible triangles
1615 double aBadRate1, aBadRate2; // to what extent a set is bad
1616 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1617 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1618 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1620 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1621 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1622 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1623 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1624 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1625 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1626 return 1; // diagonal 1-3
1628 return 2; // diagonal 2-4
1635 // Methods of splitting volumes into tetra
1637 const int theHexTo5_1[5*4+1] =
1639 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1641 const int theHexTo5_2[5*4+1] =
1643 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1645 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1647 const int theHexTo6_1[6*4+1] =
1649 1, 5, 6, 0, 0, 1, 2, 6, 0, 4, 5, 6, 0, 4, 6, 7, 0, 2, 3, 6, 0, 3, 7, 6, -1
1651 const int theHexTo6_2[6*4+1] =
1653 2, 6, 7, 1, 1, 2, 3, 7, 1, 5, 6, 7, 1, 5, 7, 4, 1, 3, 0, 7, 1, 0, 4, 7, -1
1655 const int theHexTo6_3[6*4+1] =
1657 3, 7, 4, 2, 2, 3, 0, 4, 2, 6, 7, 4, 2, 6, 4, 5, 2, 0, 1, 4, 2, 1, 5, 4, -1
1659 const int theHexTo6_4[6*4+1] =
1661 0, 4, 5, 3, 3, 0, 1, 5, 3, 7, 4, 5, 3, 7, 5, 6, 3, 1, 2, 5, 3, 2, 6, 5, -1
1663 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1665 const int thePyraTo2_1[2*4+1] =
1667 0, 1, 2, 4, 0, 2, 3, 4, -1
1669 const int thePyraTo2_2[2*4+1] =
1671 1, 2, 3, 4, 1, 3, 0, 4, -1
1673 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1675 const int thePentaTo3_1[3*4+1] =
1677 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1679 const int thePentaTo3_2[3*4+1] =
1681 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1683 const int thePentaTo3_3[3*4+1] =
1685 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1687 const int thePentaTo3_4[3*4+1] =
1689 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1691 const int thePentaTo3_5[3*4+1] =
1693 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1695 const int thePentaTo3_6[3*4+1] =
1697 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1699 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1700 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1702 // Methods of splitting hexahedron into prisms
1704 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1706 0, 1, 8, 4, 5, 9, 1, 2, 8, 5, 6, 9, 2, 3, 8, 6, 7, 9, 3, 0, 8, 7, 4, 9, -1
1708 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1710 1, 0, 8, 2, 3, 9, 0, 4, 8, 3, 7, 9, 4, 5, 8, 7, 6, 9, 5, 1, 8, 6, 2, 9, -1
1712 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1714 0, 3, 9, 1, 2, 8, 3, 7, 9, 2, 6, 8, 7, 4, 9, 6, 5, 8, 4, 0, 9, 5, 1, 8, -1
1717 const int theHexTo2Prisms_BT_1[6*2+1] =
1719 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1721 const int theHexTo2Prisms_BT_2[6*2+1] =
1723 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1725 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1727 const int theHexTo2Prisms_LR_1[6*2+1] =
1729 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1731 const int theHexTo2Prisms_LR_2[6*2+1] =
1733 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1735 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1737 const int theHexTo2Prisms_FB_1[6*2+1] =
1739 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1741 const int theHexTo2Prisms_FB_2[6*2+1] =
1743 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1745 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1748 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1751 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1752 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1753 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1754 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1760 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1761 bool _baryNode; //!< additional node is to be created at cell barycenter
1762 bool _ownConn; //!< to delete _connectivity in destructor
1763 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1765 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1766 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1767 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1768 bool hasFacet( const TTriangleFacet& facet ) const
1770 if ( _nbCorners == 4 )
1772 const int* tetConn = _connectivity;
1773 for ( ; tetConn[0] >= 0; tetConn += 4 )
1774 if (( facet.contains( tetConn[0] ) +
1775 facet.contains( tetConn[1] ) +
1776 facet.contains( tetConn[2] ) +
1777 facet.contains( tetConn[3] )) == 3 )
1780 else // prism, _nbCorners == 6
1782 const int* prismConn = _connectivity;
1783 for ( ; prismConn[0] >= 0; prismConn += 6 )
1785 if (( facet.contains( prismConn[0] ) &&
1786 facet.contains( prismConn[1] ) &&
1787 facet.contains( prismConn[2] ))
1789 ( facet.contains( prismConn[3] ) &&
1790 facet.contains( prismConn[4] ) &&
1791 facet.contains( prismConn[5] )))
1799 //=======================================================================
1801 * \brief return TSplitMethod for the given element to split into tetrahedra
1803 //=======================================================================
1805 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1807 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1809 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1810 // an edge and a face barycenter; tertaherdons are based on triangles and
1811 // a volume barycenter
1812 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1814 // Find out how adjacent volumes are split
1816 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1817 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1818 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1820 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1821 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1822 if ( nbNodes < 4 ) continue;
1824 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1825 const int* nInd = vol.GetFaceNodesIndices( iF );
1828 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1829 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1830 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1831 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1835 int iCom = 0; // common node of triangle faces to split into
1836 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1838 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1839 nInd[ iQ * ( (iCom+1)%nbNodes )],
1840 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1841 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1842 nInd[ iQ * ( (iCom+2)%nbNodes )],
1843 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1844 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1846 triaSplits.push_back( t012 );
1847 triaSplits.push_back( t023 );
1852 if ( !triaSplits.empty() )
1853 hasAdjacentSplits = true;
1856 // Among variants of split method select one compliant with adjacent volumes
1858 TSplitMethod method;
1859 if ( !vol.Element()->IsPoly() && !is24TetMode )
1861 int nbVariants = 2, nbTet = 0;
1862 const int** connVariants = 0;
1863 switch ( vol.Element()->GetEntityType() )
1865 case SMDSEntity_Hexa:
1866 case SMDSEntity_Quad_Hexa:
1867 case SMDSEntity_TriQuad_Hexa:
1868 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1869 connVariants = theHexTo5, nbTet = 5;
1871 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1873 case SMDSEntity_Pyramid:
1874 case SMDSEntity_Quad_Pyramid:
1875 connVariants = thePyraTo2; nbTet = 2;
1877 case SMDSEntity_Penta:
1878 case SMDSEntity_Quad_Penta:
1879 case SMDSEntity_BiQuad_Penta:
1880 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1885 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1887 // check method compliancy with adjacent tetras,
1888 // all found splits must be among facets of tetras described by this method
1889 method = TSplitMethod( nbTet, connVariants[variant] );
1890 if ( hasAdjacentSplits && method._nbSplits > 0 )
1892 bool facetCreated = true;
1893 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1895 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1896 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1897 facetCreated = method.hasFacet( *facet );
1899 if ( !facetCreated )
1900 method = TSplitMethod(0); // incompatible method
1904 if ( method._nbSplits < 1 )
1906 // No standard method is applicable, use a generic solution:
1907 // each facet of a volume is split into triangles and
1908 // each of triangles and a volume barycenter form a tetrahedron.
1910 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1912 int* connectivity = new int[ maxTetConnSize + 1 ];
1913 method._connectivity = connectivity;
1914 method._ownConn = true;
1915 method._baryNode = !isHex27; // to create central node or not
1918 int baryCenInd = vol.NbNodes() - int( isHex27 );
1919 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1921 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1922 const int* nInd = vol.GetFaceNodesIndices( iF );
1923 // find common node of triangle facets of tetra to create
1924 int iCommon = 0; // index in linear numeration
1925 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1926 if ( !triaSplits.empty() )
1929 const TTriangleFacet* facet = &triaSplits.front();
1930 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1931 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1932 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1935 else if ( nbNodes > 3 && !is24TetMode )
1937 // find the best method of splitting into triangles by aspect ratio
1938 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1939 map< double, int > badness2iCommon;
1940 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1941 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1942 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1945 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1947 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1948 nodes[ iQ*((iLast-1)%nbNodes)],
1949 nodes[ iQ*((iLast )%nbNodes)]);
1950 badness += getBadRate( &tria, aspectRatio );
1952 badness2iCommon.insert( make_pair( badness, iCommon ));
1954 // use iCommon with lowest badness
1955 iCommon = badness2iCommon.begin()->second;
1957 if ( iCommon >= nbNodes )
1958 iCommon = 0; // something wrong
1960 // fill connectivity of tetrahedra based on a current face
1961 int nbTet = nbNodes - 2;
1962 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1967 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1968 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1972 method._faceBaryNode[ iF ] = 0;
1973 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1976 for ( int i = 0; i < nbTet; ++i )
1978 int i1 = i, i2 = (i+1) % nbNodes;
1979 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1980 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1981 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1982 connectivity[ connSize++ ] = faceBaryCenInd;
1983 connectivity[ connSize++ ] = baryCenInd;
1988 for ( int i = 0; i < nbTet; ++i )
1990 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1991 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1992 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1993 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1994 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1995 connectivity[ connSize++ ] = baryCenInd;
1998 method._nbSplits += nbTet;
2000 } // loop on volume faces
2002 connectivity[ connSize++ ] = -1;
2004 } // end of generic solution
2008 //=======================================================================
2010 * \brief return TSplitMethod to split haxhedron into prisms
2012 //=======================================================================
2014 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2015 const int methodFlags,
2016 const int facetToSplit)
2018 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2020 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2022 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2024 static TSplitMethod to4methods[4]; // order BT, LR, FB
2025 if ( to4methods[iF]._nbSplits == 0 )
2029 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2030 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2031 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2034 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2035 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2036 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2039 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2040 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2041 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2043 default: return to4methods[3];
2045 to4methods[iF]._nbSplits = 4;
2046 to4methods[iF]._nbCorners = 6;
2048 return to4methods[iF];
2050 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2052 TSplitMethod method;
2054 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2056 const int nbVariants = 2, nbSplits = 2;
2057 const int** connVariants = 0;
2059 case 0: connVariants = theHexTo2Prisms_BT; break;
2060 case 1: connVariants = theHexTo2Prisms_LR; break;
2061 case 2: connVariants = theHexTo2Prisms_FB; break;
2062 default: return method;
2065 // look for prisms adjacent via facetToSplit and an opposite one
2066 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2068 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2069 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2070 if ( nbNodes != 4 ) return method;
2072 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2073 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2074 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2076 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2078 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2083 // there are adjacent prism
2084 for ( int variant = 0; variant < nbVariants; ++variant )
2086 // check method compliancy with adjacent prisms,
2087 // the found prism facets must be among facets of prisms described by current method
2088 method._nbSplits = nbSplits;
2089 method._nbCorners = 6;
2090 method._connectivity = connVariants[ variant ];
2091 if ( method.hasFacet( *t ))
2096 // No adjacent prisms. Select a variant with a best aspect ratio.
2098 double badness[2] = { 0., 0. };
2099 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2100 const SMDS_MeshNode** nodes = vol.GetNodes();
2101 for ( int variant = 0; variant < nbVariants; ++variant )
2102 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2104 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2105 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2107 method._connectivity = connVariants[ variant ];
2108 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2109 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2110 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2112 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2115 badness[ variant ] += getBadRate( &tria, aspectRatio );
2117 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2119 method._nbSplits = nbSplits;
2120 method._nbCorners = 6;
2121 method._connectivity = connVariants[ iBetter ];
2126 //================================================================================
2128 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2130 //================================================================================
2132 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2133 const SMDSAbs_GeometryType geom ) const
2135 // find the tetrahedron including the three nodes of facet
2136 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2137 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2138 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2139 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2140 while ( volIt1->more() )
2142 const SMDS_MeshElement* v = volIt1->next();
2143 if ( v->GetGeomType() != geom )
2145 const int lastCornerInd = v->NbCornerNodes() - 1;
2146 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2147 continue; // medium node not allowed
2148 const int ind2 = v->GetNodeIndex( n2 );
2149 if ( ind2 < 0 || lastCornerInd < ind2 )
2151 const int ind3 = v->GetNodeIndex( n3 );
2152 if ( ind3 < 0 || lastCornerInd < ind3 )
2159 //=======================================================================
2161 * \brief A key of a face of volume
2163 //=======================================================================
2165 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2167 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2169 TIDSortedNodeSet sortedNodes;
2170 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2171 int nbNodes = vol.NbFaceNodes( iF );
2172 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2173 for ( int i = 0; i < nbNodes; i += iQ )
2174 sortedNodes.insert( fNodes[i] );
2175 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2176 first.first = (*(n++))->GetID();
2177 first.second = (*(n++))->GetID();
2178 second.first = (*(n++))->GetID();
2179 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2184 //=======================================================================
2185 //function : SplitVolumes
2186 //purpose : Split volume elements into tetrahedra or prisms.
2187 // If facet ID < 0, element is split into tetrahedra,
2188 // else a hexahedron is split into prisms so that the given facet is
2189 // split into triangles
2190 //=======================================================================
2192 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2193 const int theMethodFlags)
2195 SMDS_VolumeTool volTool;
2196 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2197 fHelper.ToFixNodeParameters( true );
2199 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2200 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2202 SMESH_SequenceOfElemPtr newNodes, newElems;
2204 // map face of volume to it's baricenrtic node
2205 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2207 vector<const SMDS_MeshElement* > splitVols;
2209 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2210 for ( ; elem2facet != theElems.end(); ++elem2facet )
2212 const SMDS_MeshElement* elem = elem2facet->first;
2213 const int facetToSplit = elem2facet->second;
2214 if ( elem->GetType() != SMDSAbs_Volume )
2216 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2217 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2220 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2222 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2223 getTetraSplitMethod( volTool, theMethodFlags ) :
2224 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2225 if ( splitMethod._nbSplits < 1 ) continue;
2227 // find submesh to add new tetras to
2228 if ( !subMesh || !subMesh->Contains( elem ))
2230 int shapeID = FindShape( elem );
2231 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2232 subMesh = GetMeshDS()->MeshElements( shapeID );
2235 if ( elem->IsQuadratic() )
2238 // add quadratic links to the helper
2239 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2241 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2242 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2243 for ( int iN = 0; iN < nbN; iN += iQ )
2244 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2246 helper.SetIsQuadratic( true );
2251 helper.SetIsQuadratic( false );
2253 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2254 volTool.GetNodes() + elem->NbNodes() );
2255 helper.SetElementsOnShape( true );
2256 if ( splitMethod._baryNode )
2258 // make a node at barycenter
2259 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2260 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2261 nodes.push_back( gcNode );
2262 newNodes.push_back( gcNode );
2264 if ( !splitMethod._faceBaryNode.empty() )
2266 // make or find baricentric nodes of faces
2267 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2268 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2270 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2271 volFace2BaryNode.insert
2272 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2275 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2276 newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2278 nodes.push_back( iF_n->second = f_n->second );
2283 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2284 const int* volConn = splitMethod._connectivity;
2285 if ( splitMethod._nbCorners == 4 ) // tetra
2286 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2287 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2288 nodes[ volConn[1] ],
2289 nodes[ volConn[2] ],
2290 nodes[ volConn[3] ]));
2292 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2293 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2294 nodes[ volConn[1] ],
2295 nodes[ volConn[2] ],
2296 nodes[ volConn[3] ],
2297 nodes[ volConn[4] ],
2298 nodes[ volConn[5] ]));
2300 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2302 // Split faces on sides of the split volume
2304 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2305 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2307 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2308 if ( nbNodes < 4 ) continue;
2310 // find an existing face
2311 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2312 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2313 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2314 /*noMedium=*/false))
2317 helper.SetElementsOnShape( false );
2318 vector< const SMDS_MeshElement* > triangles;
2320 // find submesh to add new triangles in
2321 if ( !fSubMesh || !fSubMesh->Contains( face ))
2323 int shapeID = FindShape( face );
2324 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2326 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2327 if ( iF_n != splitMethod._faceBaryNode.end() )
2329 const SMDS_MeshNode *baryNode = iF_n->second;
2330 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2332 const SMDS_MeshNode* n1 = fNodes[iN];
2333 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2334 const SMDS_MeshNode *n3 = baryNode;
2335 if ( !volTool.IsFaceExternal( iF ))
2337 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2339 if ( fSubMesh ) // update position of the bary node on geometry
2342 subMesh->RemoveNode( baryNode );
2343 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2344 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2345 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2347 fHelper.SetSubShape( s );
2348 gp_XY uv( 1e100, 1e100 );
2350 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2351 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2354 // node is too far from the surface
2355 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2356 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2357 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2364 // among possible triangles create ones described by split method
2365 const int* nInd = volTool.GetFaceNodesIndices( iF );
2366 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2367 int iCom = 0; // common node of triangle faces to split into
2368 list< TTriangleFacet > facets;
2369 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2371 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2372 nInd[ iQ * ( (iCom+1)%nbNodes )],
2373 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2374 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2375 nInd[ iQ * ( (iCom+2)%nbNodes )],
2376 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2377 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2379 facets.push_back( t012 );
2380 facets.push_back( t023 );
2381 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2382 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2383 nInd[ iQ * ((iLast-1)%nbNodes )],
2384 nInd[ iQ * ((iLast )%nbNodes )]));
2388 list< TTriangleFacet >::iterator facet = facets.begin();
2389 if ( facet == facets.end() )
2391 for ( ; facet != facets.end(); ++facet )
2393 if ( !volTool.IsFaceExternal( iF ))
2394 swap( facet->_n2, facet->_n3 );
2395 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2396 volNodes[ facet->_n2 ],
2397 volNodes[ facet->_n3 ]));
2400 for ( size_t i = 0; i < triangles.size(); ++i )
2402 if ( !triangles[ i ]) continue;
2404 fSubMesh->AddElement( triangles[ i ]);
2405 newElems.push_back( triangles[ i ]);
2407 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2408 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2410 } // while a face based on facet nodes exists
2411 } // loop on volume faces to split them into triangles
2413 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2415 if ( geomType == SMDSEntity_TriQuad_Hexa )
2417 // remove medium nodes that could become free
2418 for ( int i = 20; i < volTool.NbNodes(); ++i )
2419 if ( volNodes[i]->NbInverseElements() == 0 )
2420 GetMeshDS()->RemoveNode( volNodes[i] );
2422 } // loop on volumes to split
2424 myLastCreatedNodes = newNodes;
2425 myLastCreatedElems = newElems;
2428 //=======================================================================
2429 //function : GetHexaFacetsToSplit
2430 //purpose : For hexahedra that will be split into prisms, finds facets to
2431 // split into triangles. Only hexahedra adjacent to the one closest
2432 // to theFacetNormal.Location() are returned.
2433 //param [in,out] theHexas - the hexahedra
2434 //param [in] theFacetNormal - facet normal
2435 //param [out] theFacets - the hexahedra and found facet IDs
2436 //=======================================================================
2438 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2439 const gp_Ax1& theFacetNormal,
2440 TFacetOfElem & theFacets)
2442 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2444 // Find a hexa closest to the location of theFacetNormal
2446 const SMDS_MeshElement* startHex;
2448 // get SMDS_ElemIteratorPtr on theHexas
2449 typedef const SMDS_MeshElement* TValue;
2450 typedef TIDSortedElemSet::iterator TSetIterator;
2451 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2452 typedef SMDS_MeshElement::GeomFilter TFilter;
2453 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2454 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2455 ( new TElemSetIter( theHexas.begin(),
2457 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2459 SMESH_ElementSearcher* searcher =
2460 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2462 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2467 throw SALOME_Exception( THIS_METHOD "startHex not found");
2470 // Select a facet of startHex by theFacetNormal
2472 SMDS_VolumeTool vTool( startHex );
2473 double norm[3], dot, maxDot = 0;
2475 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2476 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2478 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2486 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2488 // Fill theFacets starting from facetID of startHex
2490 // facets used for searching of volumes adjacent to already treated ones
2491 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2492 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2493 TFacetMap facetsToCheck;
2495 set<const SMDS_MeshNode*> facetNodes;
2496 const SMDS_MeshElement* curHex;
2498 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2502 // move in two directions from startHex via facetID
2503 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2506 int curFacet = facetID;
2507 if ( is2nd ) // do not treat startHex twice
2509 vTool.Set( curHex );
2510 if ( vTool.IsFreeFace( curFacet, &curHex ))
2516 vTool.GetFaceNodes( curFacet, facetNodes );
2517 vTool.Set( curHex );
2518 curFacet = vTool.GetFaceIndex( facetNodes );
2523 // store a facet to split
2524 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2526 theFacets.insert( make_pair( curHex, -1 ));
2529 if ( !allHex && !theHexas.count( curHex ))
2532 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2533 theFacets.insert( make_pair( curHex, curFacet ));
2534 if ( !facetIt2isNew.second )
2537 // remember not-to-split facets in facetsToCheck
2538 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2539 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2541 if ( iF == curFacet && iF == oppFacet )
2543 TVolumeFaceKey facetKey ( vTool, iF );
2544 TElemFacets elemFacet( facetIt2isNew.first, iF );
2545 pair< TFacetMap::iterator, bool > it2isnew =
2546 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2547 if ( !it2isnew.second )
2548 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2550 // pass to a volume adjacent via oppFacet
2551 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2557 // get a new curFacet
2558 vTool.GetFaceNodes( oppFacet, facetNodes );
2559 vTool.Set( curHex );
2560 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2563 } // move in two directions from startHex via facetID
2565 // Find a new startHex by facetsToCheck
2569 TFacetMap::iterator fIt = facetsToCheck.begin();
2570 while ( !startHex && fIt != facetsToCheck.end() )
2572 const TElemFacets& elemFacets = fIt->second;
2573 const SMDS_MeshElement* hex = elemFacets.first->first;
2574 int splitFacet = elemFacets.first->second;
2575 int lateralFacet = elemFacets.second;
2576 facetsToCheck.erase( fIt );
2577 fIt = facetsToCheck.begin();
2580 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2581 curHex->GetGeomType() != SMDSGeom_HEXA )
2583 if ( !allHex && !theHexas.count( curHex ))
2588 // find a facet of startHex to split
2590 set<const SMDS_MeshNode*> lateralNodes;
2591 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2592 vTool.GetFaceNodes( splitFacet, facetNodes );
2593 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2594 vTool.Set( startHex );
2595 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2597 // look for a facet of startHex having common nodes with facetNodes
2598 // but not lateralFacet
2599 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2601 if ( iF == lateralFacet )
2603 int nbCommonNodes = 0;
2604 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2605 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2606 nbCommonNodes += facetNodes.count( nn[ iN ]);
2608 if ( nbCommonNodes >= 2 )
2615 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2617 } // while ( startHex )
2624 //================================================================================
2626 * \brief Selects nodes of several elements according to a given interlace
2627 * \param [in] srcNodes - nodes to select from
2628 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2629 * \param [in] interlace - indices of nodes for all elements
2630 * \param [in] nbElems - nb of elements
2631 * \param [in] nbNodes - nb of nodes in each element
2632 * \param [in] mesh - the mesh
2633 * \param [out] elemQueue - a list to push elements found by the selected nodes
2634 * \param [in] type - type of elements to look for
2636 //================================================================================
2638 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2639 vector< const SMDS_MeshNode* >* tgtNodesVec,
2640 const int* interlace,
2643 SMESHDS_Mesh* mesh = 0,
2644 list< const SMDS_MeshElement* >* elemQueue=0,
2645 SMDSAbs_ElementType type=SMDSAbs_All)
2647 for ( int iE = 0; iE < nbElems; ++iE )
2649 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2650 const int* select = & interlace[iE*nbNodes];
2651 elemNodes.resize( nbNodes );
2652 for ( int iN = 0; iN < nbNodes; ++iN )
2653 elemNodes[iN] = srcNodes[ select[ iN ]];
2655 const SMDS_MeshElement* e;
2657 for ( int iE = 0; iE < nbElems; ++iE )
2658 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2659 elemQueue->push_back( e );
2663 //=======================================================================
2665 * Split bi-quadratic elements into linear ones without creation of additional nodes
2666 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2667 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2668 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2669 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2670 * will be split in order to keep the mesh conformal.
2671 * \param elems - elements to split
2673 //=======================================================================
2675 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2677 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2678 vector<const SMDS_MeshElement* > splitElems;
2679 list< const SMDS_MeshElement* > elemQueue;
2680 list< const SMDS_MeshElement* >::iterator elemIt;
2682 SMESHDS_Mesh * mesh = GetMeshDS();
2683 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2684 int nbElems, nbNodes;
2686 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2687 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2690 elemQueue.push_back( *elemSetIt );
2691 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2693 const SMDS_MeshElement* elem = *elemIt;
2694 switch( elem->GetEntityType() )
2696 case SMDSEntity_TriQuad_Hexa: // HEX27
2698 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2699 nbElems = nbNodes = 8;
2700 elemType = & hexaType;
2702 // get nodes for new elements
2703 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2704 { 1,9,20,8, 17,22,26,21 },
2705 { 2,10,20,9, 18,23,26,22 },
2706 { 3,11,20,10, 19,24,26,23 },
2707 { 16,21,26,24, 4,12,25,15 },
2708 { 17,22,26,21, 5,13,25,12 },
2709 { 18,23,26,22, 6,14,25,13 },
2710 { 19,24,26,23, 7,15,25,14 }};
2711 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2713 // add boundary faces to elemQueue
2714 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2715 { 4,5,6,7, 12,13,14,15, 25 },
2716 { 0,1,5,4, 8,17,12,16, 21 },
2717 { 1,2,6,5, 9,18,13,17, 22 },
2718 { 2,3,7,6, 10,19,14,18, 23 },
2719 { 3,0,4,7, 11,16,15,19, 24 }};
2720 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2722 // add boundary segments to elemQueue
2723 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2724 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2725 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2726 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2729 case SMDSEntity_BiQuad_Triangle: // TRIA7
2731 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2734 elemType = & quadType;
2736 // get nodes for new elements
2737 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2738 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2740 // add boundary segments to elemQueue
2741 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2742 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2745 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2747 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2750 elemType = & quadType;
2752 // get nodes for new elements
2753 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2754 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2756 // add boundary segments to elemQueue
2757 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2758 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2761 case SMDSEntity_Quad_Edge:
2763 if ( elemIt == elemQueue.begin() )
2764 continue; // an elem is in theElems
2765 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2768 elemType = & segType;
2770 // get nodes for new elements
2771 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2772 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2776 } // switch( elem->GetEntityType() )
2778 // Create new elements
2780 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2784 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2785 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2786 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2787 //elemType->SetID( -1 );
2789 for ( int iE = 0; iE < nbElems; ++iE )
2790 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2793 ReplaceElemInGroups( elem, splitElems, mesh );
2796 for ( size_t i = 0; i < splitElems.size(); ++i )
2797 subMesh->AddElement( splitElems[i] );
2802 //=======================================================================
2803 //function : AddToSameGroups
2804 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2805 //=======================================================================
2807 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2808 const SMDS_MeshElement* elemInGroups,
2809 SMESHDS_Mesh * aMesh)
2811 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2812 if (!groups.empty()) {
2813 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2814 for ( ; grIt != groups.end(); grIt++ ) {
2815 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2816 if ( group && group->Contains( elemInGroups ))
2817 group->SMDSGroup().Add( elemToAdd );
2823 //=======================================================================
2824 //function : RemoveElemFromGroups
2825 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2826 //=======================================================================
2827 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2828 SMESHDS_Mesh * aMesh)
2830 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2831 if (!groups.empty())
2833 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2834 for (; GrIt != groups.end(); GrIt++)
2836 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2837 if (!grp || grp->IsEmpty()) continue;
2838 grp->SMDSGroup().Remove(removeelem);
2843 //================================================================================
2845 * \brief Replace elemToRm by elemToAdd in the all groups
2847 //================================================================================
2849 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2850 const SMDS_MeshElement* elemToAdd,
2851 SMESHDS_Mesh * aMesh)
2853 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2854 if (!groups.empty()) {
2855 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2856 for ( ; grIt != groups.end(); grIt++ ) {
2857 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2858 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2859 group->SMDSGroup().Add( elemToAdd );
2864 //================================================================================
2866 * \brief Replace elemToRm by elemToAdd in the all groups
2868 //================================================================================
2870 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2871 const vector<const SMDS_MeshElement*>& elemToAdd,
2872 SMESHDS_Mesh * aMesh)
2874 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2875 if (!groups.empty())
2877 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2878 for ( ; grIt != groups.end(); grIt++ ) {
2879 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2880 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2881 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2882 group->SMDSGroup().Add( elemToAdd[ i ] );
2887 //=======================================================================
2888 //function : QuadToTri
2889 //purpose : Cut quadrangles into triangles.
2890 // theCrit is used to select a diagonal to cut
2891 //=======================================================================
2893 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2894 const bool the13Diag)
2897 myLastCreatedElems.reserve( theElems.size() * 2 );
2899 SMESHDS_Mesh * aMesh = GetMeshDS();
2900 Handle(Geom_Surface) surface;
2901 SMESH_MesherHelper helper( *GetMesh() );
2903 TIDSortedElemSet::iterator itElem;
2904 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2906 const SMDS_MeshElement* elem = *itElem;
2907 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2910 if ( elem->NbNodes() == 4 ) {
2911 // retrieve element nodes
2912 const SMDS_MeshNode* aNodes [4];
2913 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2915 while ( itN->more() )
2916 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2918 int aShapeId = FindShape( elem );
2919 const SMDS_MeshElement* newElem1 = 0;
2920 const SMDS_MeshElement* newElem2 = 0;
2922 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2923 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2926 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2927 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2929 myLastCreatedElems.push_back(newElem1);
2930 myLastCreatedElems.push_back(newElem2);
2931 // put a new triangle on the same shape and add to the same groups
2934 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2935 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2937 AddToSameGroups( newElem1, elem, aMesh );
2938 AddToSameGroups( newElem2, elem, aMesh );
2939 aMesh->RemoveElement( elem );
2942 // Quadratic quadrangle
2944 else if ( elem->NbNodes() >= 8 )
2946 // get surface elem is on
2947 int aShapeId = FindShape( elem );
2948 if ( aShapeId != helper.GetSubShapeID() ) {
2952 shape = aMesh->IndexToShape( aShapeId );
2953 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2954 TopoDS_Face face = TopoDS::Face( shape );
2955 surface = BRep_Tool::Surface( face );
2956 if ( !surface.IsNull() )
2957 helper.SetSubShape( shape );
2961 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
2962 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2963 for ( int i = 0; itN->more(); ++i )
2964 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2966 const SMDS_MeshNode* centrNode = aNodes[8];
2967 if ( centrNode == 0 )
2969 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2970 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
2972 myLastCreatedNodes.push_back(centrNode);
2975 // create a new element
2976 const SMDS_MeshElement* newElem1 = 0;
2977 const SMDS_MeshElement* newElem2 = 0;
2979 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2980 aNodes[6], aNodes[7], centrNode );
2981 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2982 centrNode, aNodes[4], aNodes[5] );
2985 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2986 aNodes[7], aNodes[4], centrNode );
2987 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2988 centrNode, aNodes[5], aNodes[6] );
2990 myLastCreatedElems.push_back(newElem1);
2991 myLastCreatedElems.push_back(newElem2);
2992 // put a new triangle on the same shape and add to the same groups
2995 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2996 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2998 AddToSameGroups( newElem1, elem, aMesh );
2999 AddToSameGroups( newElem2, elem, aMesh );
3000 aMesh->RemoveElement( elem );
3007 //=======================================================================
3008 //function : getAngle
3010 //=======================================================================
3012 double getAngle(const SMDS_MeshElement * tr1,
3013 const SMDS_MeshElement * tr2,
3014 const SMDS_MeshNode * n1,
3015 const SMDS_MeshNode * n2)
3017 double angle = 2. * M_PI; // bad angle
3020 SMESH::Controls::TSequenceOfXYZ P1, P2;
3021 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3022 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3025 if(!tr1->IsQuadratic())
3026 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3028 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3029 if ( N1.SquareMagnitude() <= gp::Resolution() )
3031 if(!tr2->IsQuadratic())
3032 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3034 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3035 if ( N2.SquareMagnitude() <= gp::Resolution() )
3038 // find the first diagonal node n1 in the triangles:
3039 // take in account a diagonal link orientation
3040 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3041 for ( int t = 0; t < 2; t++ ) {
3042 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3043 int i = 0, iDiag = -1;
3044 while ( it->more()) {
3045 const SMDS_MeshElement *n = it->next();
3046 if ( n == n1 || n == n2 ) {
3050 if ( i - iDiag == 1 )
3051 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3060 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3063 angle = N1.Angle( N2 );
3068 // =================================================
3069 // class generating a unique ID for a pair of nodes
3070 // and able to return nodes by that ID
3071 // =================================================
3075 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3076 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3079 long GetLinkID (const SMDS_MeshNode * n1,
3080 const SMDS_MeshNode * n2) const
3082 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3085 bool GetNodes (const long theLinkID,
3086 const SMDS_MeshNode* & theNode1,
3087 const SMDS_MeshNode* & theNode2) const
3089 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3090 if ( !theNode1 ) return false;
3091 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3092 if ( !theNode2 ) return false;
3098 const SMESHDS_Mesh* myMesh;
3103 //=======================================================================
3104 //function : TriToQuad
3105 //purpose : Fuse neighbour triangles into quadrangles.
3106 // theCrit is used to select a neighbour to fuse with.
3107 // theMaxAngle is a max angle between element normals at which
3108 // fusion is still performed.
3109 //=======================================================================
3111 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3112 SMESH::Controls::NumericalFunctorPtr theCrit,
3113 const double theMaxAngle)
3116 myLastCreatedElems.reserve( theElems.size() / 2 );
3118 if ( !theCrit.get() )
3121 SMESHDS_Mesh * aMesh = GetMeshDS();
3123 // Prepare data for algo: build
3124 // 1. map of elements with their linkIDs
3125 // 2. map of linkIDs with their elements
3127 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3128 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3129 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3130 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3132 TIDSortedElemSet::iterator itElem;
3133 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3135 const SMDS_MeshElement* elem = *itElem;
3136 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3137 bool IsTria = ( elem->NbCornerNodes()==3 );
3138 if (!IsTria) continue;
3140 // retrieve element nodes
3141 const SMDS_MeshNode* aNodes [4];
3142 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3145 aNodes[ i++ ] = itN->next();
3146 aNodes[ 3 ] = aNodes[ 0 ];
3149 for ( i = 0; i < 3; i++ ) {
3150 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3151 // check if elements sharing a link can be fused
3152 itLE = mapLi_listEl.find( link );
3153 if ( itLE != mapLi_listEl.end() ) {
3154 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3156 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3157 //if ( FindShape( elem ) != FindShape( elem2 ))
3158 // continue; // do not fuse triangles laying on different shapes
3159 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3160 continue; // avoid making badly shaped quads
3161 (*itLE).second.push_back( elem );
3164 mapLi_listEl[ link ].push_back( elem );
3166 mapEl_setLi [ elem ].insert( link );
3169 // Clean the maps from the links shared by a sole element, ie
3170 // links to which only one element is bound in mapLi_listEl
3172 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3173 int nbElems = (*itLE).second.size();
3174 if ( nbElems < 2 ) {
3175 const SMDS_MeshElement* elem = (*itLE).second.front();
3176 SMESH_TLink link = (*itLE).first;
3177 mapEl_setLi[ elem ].erase( link );
3178 if ( mapEl_setLi[ elem ].empty() )
3179 mapEl_setLi.erase( elem );
3183 // Algo: fuse triangles into quadrangles
3185 while ( ! mapEl_setLi.empty() ) {
3186 // Look for the start element:
3187 // the element having the least nb of shared links
3188 const SMDS_MeshElement* startElem = 0;
3190 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3191 int nbLinks = (*itEL).second.size();
3192 if ( nbLinks < minNbLinks ) {
3193 startElem = (*itEL).first;
3194 minNbLinks = nbLinks;
3195 if ( minNbLinks == 1 )
3200 // search elements to fuse starting from startElem or links of elements
3201 // fused earlyer - startLinks
3202 list< SMESH_TLink > startLinks;
3203 while ( startElem || !startLinks.empty() ) {
3204 while ( !startElem && !startLinks.empty() ) {
3205 // Get an element to start, by a link
3206 SMESH_TLink linkId = startLinks.front();
3207 startLinks.pop_front();
3208 itLE = mapLi_listEl.find( linkId );
3209 if ( itLE != mapLi_listEl.end() ) {
3210 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3211 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3212 for ( ; itE != listElem.end() ; itE++ )
3213 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3215 mapLi_listEl.erase( itLE );
3220 // Get candidates to be fused
3221 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3222 const SMESH_TLink *link12 = 0, *link13 = 0;
3224 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3225 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3226 ASSERT( !setLi.empty() );
3227 set< SMESH_TLink >::iterator itLi;
3228 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3230 const SMESH_TLink & link = (*itLi);
3231 itLE = mapLi_listEl.find( link );
3232 if ( itLE == mapLi_listEl.end() )
3235 const SMDS_MeshElement* elem = (*itLE).second.front();
3237 elem = (*itLE).second.back();
3238 mapLi_listEl.erase( itLE );
3239 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3250 // add other links of elem to list of links to re-start from
3251 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3252 set< SMESH_TLink >::iterator it;
3253 for ( it = links.begin(); it != links.end(); it++ ) {
3254 const SMESH_TLink& link2 = (*it);
3255 if ( link2 != link )
3256 startLinks.push_back( link2 );
3260 // Get nodes of possible quadrangles
3261 const SMDS_MeshNode *n12 [4], *n13 [4];
3262 bool Ok12 = false, Ok13 = false;
3263 const SMDS_MeshNode *linkNode1, *linkNode2;
3265 linkNode1 = link12->first;
3266 linkNode2 = link12->second;
3267 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3271 linkNode1 = link13->first;
3272 linkNode2 = link13->second;
3273 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3277 // Choose a pair to fuse
3278 if ( Ok12 && Ok13 ) {
3279 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3280 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3281 double aBadRate12 = getBadRate( &quad12, theCrit );
3282 double aBadRate13 = getBadRate( &quad13, theCrit );
3283 if ( aBadRate13 < aBadRate12 )
3290 // and remove fused elems and remove links from the maps
3291 mapEl_setLi.erase( tr1 );
3294 mapEl_setLi.erase( tr2 );
3295 mapLi_listEl.erase( *link12 );
3296 if ( tr1->NbNodes() == 3 )
3298 const SMDS_MeshElement* newElem = 0;
3299 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3300 myLastCreatedElems.push_back(newElem);
3301 AddToSameGroups( newElem, tr1, aMesh );
3302 int aShapeId = tr1->getshapeId();
3304 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3305 aMesh->RemoveElement( tr1 );
3306 aMesh->RemoveElement( tr2 );
3309 vector< const SMDS_MeshNode* > N1;
3310 vector< const SMDS_MeshNode* > N2;
3311 getNodesFromTwoTria(tr1,tr2,N1,N2);
3312 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3313 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3314 // i.e. first nodes from both arrays form a new diagonal
3315 const SMDS_MeshNode* aNodes[8];
3324 const SMDS_MeshElement* newElem = 0;
3325 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3326 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3327 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3329 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3330 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3331 myLastCreatedElems.push_back(newElem);
3332 AddToSameGroups( newElem, tr1, aMesh );
3333 int aShapeId = tr1->getshapeId();
3335 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3336 aMesh->RemoveElement( tr1 );
3337 aMesh->RemoveElement( tr2 );
3338 // remove middle node (9)
3339 if ( N1[4]->NbInverseElements() == 0 )
3340 aMesh->RemoveNode( N1[4] );
3341 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3342 aMesh->RemoveNode( N1[6] );
3343 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3344 aMesh->RemoveNode( N2[6] );
3349 mapEl_setLi.erase( tr3 );
3350 mapLi_listEl.erase( *link13 );
3351 if ( tr1->NbNodes() == 3 ) {
3352 const SMDS_MeshElement* newElem = 0;
3353 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3354 myLastCreatedElems.push_back(newElem);
3355 AddToSameGroups( newElem, tr1, aMesh );
3356 int aShapeId = tr1->getshapeId();
3358 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3359 aMesh->RemoveElement( tr1 );
3360 aMesh->RemoveElement( tr3 );
3363 vector< const SMDS_MeshNode* > N1;
3364 vector< const SMDS_MeshNode* > N2;
3365 getNodesFromTwoTria(tr1,tr3,N1,N2);
3366 // now we receive following N1 and N2 (using numeration as above image)
3367 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3368 // i.e. first nodes from both arrays form a new diagonal
3369 const SMDS_MeshNode* aNodes[8];
3378 const SMDS_MeshElement* newElem = 0;
3379 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3380 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3381 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3383 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3384 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3385 myLastCreatedElems.push_back(newElem);
3386 AddToSameGroups( newElem, tr1, aMesh );
3387 int aShapeId = tr1->getshapeId();
3389 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3390 aMesh->RemoveElement( tr1 );
3391 aMesh->RemoveElement( tr3 );
3392 // remove middle node (9)
3393 if ( N1[4]->NbInverseElements() == 0 )
3394 aMesh->RemoveNode( N1[4] );
3395 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3396 aMesh->RemoveNode( N1[6] );
3397 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3398 aMesh->RemoveNode( N2[6] );
3402 // Next element to fuse: the rejected one
3404 startElem = Ok12 ? tr3 : tr2;
3406 } // if ( startElem )
3407 } // while ( startElem || !startLinks.empty() )
3408 } // while ( ! mapEl_setLi.empty() )
3413 //================================================================================
3415 * \brief Return nodes linked to the given one
3416 * \param theNode - the node
3417 * \param linkedNodes - the found nodes
3418 * \param type - the type of elements to check
3420 * Medium nodes are ignored
3422 //================================================================================
3424 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3425 TIDSortedElemSet & linkedNodes,
3426 SMDSAbs_ElementType type )
3428 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3429 while ( elemIt->more() )
3431 const SMDS_MeshElement* elem = elemIt->next();
3432 if(elem->GetType() == SMDSAbs_0DElement)
3435 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3436 if ( elem->GetType() == SMDSAbs_Volume )
3438 SMDS_VolumeTool vol( elem );
3439 while ( nodeIt->more() ) {
3440 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3441 if ( theNode != n && vol.IsLinked( theNode, n ))
3442 linkedNodes.insert( n );
3447 for ( int i = 0; nodeIt->more(); ++i ) {
3448 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3449 if ( n == theNode ) {
3450 int iBefore = i - 1;
3452 if ( elem->IsQuadratic() ) {
3453 int nb = elem->NbNodes() / 2;
3454 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3455 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3457 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3458 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3465 //=======================================================================
3466 //function : laplacianSmooth
3467 //purpose : pulls theNode toward the center of surrounding nodes directly
3468 // connected to that node along an element edge
3469 //=======================================================================
3471 void laplacianSmooth(const SMDS_MeshNode* theNode,
3472 const Handle(Geom_Surface)& theSurface,
3473 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3475 // find surrounding nodes
3477 TIDSortedElemSet nodeSet;
3478 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3480 // compute new coodrs
3482 double coord[] = { 0., 0., 0. };
3483 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3484 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3485 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3486 if ( theSurface.IsNull() ) { // smooth in 3D
3487 coord[0] += node->X();
3488 coord[1] += node->Y();
3489 coord[2] += node->Z();
3491 else { // smooth in 2D
3492 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3493 gp_XY* uv = theUVMap[ node ];
3494 coord[0] += uv->X();
3495 coord[1] += uv->Y();
3498 int nbNodes = nodeSet.size();
3501 coord[0] /= nbNodes;
3502 coord[1] /= nbNodes;
3504 if ( !theSurface.IsNull() ) {
3505 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3506 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3507 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3513 coord[2] /= nbNodes;
3517 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3520 //=======================================================================
3521 //function : centroidalSmooth
3522 //purpose : pulls theNode toward the element-area-weighted centroid of the
3523 // surrounding elements
3524 //=======================================================================
3526 void centroidalSmooth(const SMDS_MeshNode* theNode,
3527 const Handle(Geom_Surface)& theSurface,
3528 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3530 gp_XYZ aNewXYZ(0.,0.,0.);
3531 SMESH::Controls::Area anAreaFunc;
3532 double totalArea = 0.;
3537 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3538 while ( elemIt->more() )
3540 const SMDS_MeshElement* elem = elemIt->next();
3543 gp_XYZ elemCenter(0.,0.,0.);
3544 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3545 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3546 int nn = elem->NbNodes();
3547 if(elem->IsQuadratic()) nn = nn/2;
3549 //while ( itN->more() ) {
3551 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3553 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3554 aNodePoints.push_back( aP );
3555 if ( !theSurface.IsNull() ) { // smooth in 2D
3556 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3557 gp_XY* uv = theUVMap[ aNode ];
3558 aP.SetCoord( uv->X(), uv->Y(), 0. );
3562 double elemArea = anAreaFunc.GetValue( aNodePoints );
3563 totalArea += elemArea;
3565 aNewXYZ += elemCenter * elemArea;
3567 aNewXYZ /= totalArea;
3568 if ( !theSurface.IsNull() ) {
3569 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3570 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3575 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3578 //=======================================================================
3579 //function : getClosestUV
3580 //purpose : return UV of closest projection
3581 //=======================================================================
3583 static bool getClosestUV (Extrema_GenExtPS& projector,
3584 const gp_Pnt& point,
3587 projector.Perform( point );
3588 if ( projector.IsDone() ) {
3589 double u, v, minVal = DBL_MAX;
3590 for ( int i = projector.NbExt(); i > 0; i-- )
3591 if ( projector.SquareDistance( i ) < minVal ) {
3592 minVal = projector.SquareDistance( i );
3593 projector.Point( i ).Parameter( u, v );
3595 result.SetCoord( u, v );
3601 //=======================================================================
3603 //purpose : Smooth theElements during theNbIterations or until a worst
3604 // element has aspect ratio <= theTgtAspectRatio.
3605 // Aspect Ratio varies in range [1.0, inf].
3606 // If theElements is empty, the whole mesh is smoothed.
3607 // theFixedNodes contains additionally fixed nodes. Nodes built
3608 // on edges and boundary nodes are always fixed.
3609 //=======================================================================
3611 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3612 set<const SMDS_MeshNode*> & theFixedNodes,
3613 const SmoothMethod theSmoothMethod,
3614 const int theNbIterations,
3615 double theTgtAspectRatio,
3620 if ( theTgtAspectRatio < 1.0 )
3621 theTgtAspectRatio = 1.0;
3623 const double disttol = 1.e-16;
3625 SMESH::Controls::AspectRatio aQualityFunc;
3627 SMESHDS_Mesh* aMesh = GetMeshDS();
3629 if ( theElems.empty() ) {
3630 // add all faces to theElems
3631 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3632 while ( fIt->more() ) {
3633 const SMDS_MeshElement* face = fIt->next();
3634 theElems.insert( theElems.end(), face );
3637 // get all face ids theElems are on
3638 set< int > faceIdSet;
3639 TIDSortedElemSet::iterator itElem;
3641 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3642 int fId = FindShape( *itElem );
3643 // check that corresponding submesh exists and a shape is face
3645 faceIdSet.find( fId ) == faceIdSet.end() &&
3646 aMesh->MeshElements( fId )) {
3647 TopoDS_Shape F = aMesh->IndexToShape( fId );
3648 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3649 faceIdSet.insert( fId );
3652 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3654 // ===============================================
3655 // smooth elements on each TopoDS_Face separately
3656 // ===============================================
3658 SMESH_MesherHelper helper( *GetMesh() );
3660 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3661 for ( ; fId != faceIdSet.rend(); ++fId )
3663 // get face surface and submesh
3664 Handle(Geom_Surface) surface;
3665 SMESHDS_SubMesh* faceSubMesh = 0;
3668 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3669 bool isUPeriodic = false, isVPeriodic = false;
3672 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3673 surface = BRep_Tool::Surface( face );
3674 faceSubMesh = aMesh->MeshElements( *fId );
3675 fToler2 = BRep_Tool::Tolerance( face );
3676 fToler2 *= fToler2 * 10.;
3677 isUPeriodic = surface->IsUPeriodic();
3678 // if ( isUPeriodic )
3679 // surface->UPeriod();
3680 isVPeriodic = surface->IsVPeriodic();
3681 // if ( isVPeriodic )
3682 // surface->VPeriod();
3683 surface->Bounds( u1, u2, v1, v2 );
3684 helper.SetSubShape( face );
3686 // ---------------------------------------------------------
3687 // for elements on a face, find movable and fixed nodes and
3688 // compute UV for them
3689 // ---------------------------------------------------------
3690 bool checkBoundaryNodes = false;
3691 bool isQuadratic = false;
3692 set<const SMDS_MeshNode*> setMovableNodes;
3693 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3694 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3695 list< const SMDS_MeshElement* > elemsOnFace;
3697 Extrema_GenExtPS projector;
3698 GeomAdaptor_Surface surfAdaptor;
3699 if ( !surface.IsNull() ) {
3700 surfAdaptor.Load( surface );
3701 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3703 int nbElemOnFace = 0;
3704 itElem = theElems.begin();
3705 // loop on not yet smoothed elements: look for elems on a face
3706 while ( itElem != theElems.end() )
3708 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3709 break; // all elements found
3711 const SMDS_MeshElement* elem = *itElem;
3712 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3713 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3717 elemsOnFace.push_back( elem );
3718 theElems.erase( itElem++ );
3722 isQuadratic = elem->IsQuadratic();
3724 // get movable nodes of elem
3725 const SMDS_MeshNode* node;
3726 SMDS_TypeOfPosition posType;
3727 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3728 int nn = 0, nbn = elem->NbNodes();
3729 if(elem->IsQuadratic())
3731 while ( nn++ < nbn ) {
3732 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3733 const SMDS_PositionPtr& pos = node->GetPosition();
3734 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3735 if (posType != SMDS_TOP_EDGE &&
3736 posType != SMDS_TOP_VERTEX &&
3737 theFixedNodes.find( node ) == theFixedNodes.end())
3739 // check if all faces around the node are on faceSubMesh
3740 // because a node on edge may be bound to face
3742 if ( faceSubMesh ) {
3743 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3744 while ( eIt->more() && all ) {
3745 const SMDS_MeshElement* e = eIt->next();
3746 all = faceSubMesh->Contains( e );
3750 setMovableNodes.insert( node );
3752 checkBoundaryNodes = true;
3754 if ( posType == SMDS_TOP_3DSPACE )
3755 checkBoundaryNodes = true;
3758 if ( surface.IsNull() )
3761 // get nodes to check UV
3762 list< const SMDS_MeshNode* > uvCheckNodes;
3763 const SMDS_MeshNode* nodeInFace = 0;
3764 itN = elem->nodesIterator();
3765 nn = 0; nbn = elem->NbNodes();
3766 if(elem->IsQuadratic())
3768 while ( nn++ < nbn ) {
3769 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3770 if ( node->GetPosition()->GetDim() == 2 )
3772 if ( uvMap.find( node ) == uvMap.end() )
3773 uvCheckNodes.push_back( node );
3774 // add nodes of elems sharing node
3775 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3776 // while ( eIt->more() ) {
3777 // const SMDS_MeshElement* e = eIt->next();
3778 // if ( e != elem ) {
3779 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3780 // while ( nIt->more() ) {
3781 // const SMDS_MeshNode* n =
3782 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3783 // if ( uvMap.find( n ) == uvMap.end() )
3784 // uvCheckNodes.push_back( n );
3790 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3791 for ( ; n != uvCheckNodes.end(); ++n ) {
3794 const SMDS_PositionPtr& pos = node->GetPosition();
3795 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3799 bool toCheck = true;
3800 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3802 // compute not existing UV
3803 bool project = ( posType == SMDS_TOP_3DSPACE );
3804 // double dist1 = DBL_MAX, dist2 = 0;
3805 // if ( posType != SMDS_TOP_3DSPACE ) {
3806 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3807 // project = dist1 > fToler2;
3809 if ( project ) { // compute new UV
3811 gp_Pnt pNode = SMESH_NodeXYZ( node );
3812 if ( !getClosestUV( projector, pNode, newUV )) {
3813 MESSAGE("Node Projection Failed " << node);
3817 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3819 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3821 // if ( posType != SMDS_TOP_3DSPACE )
3822 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3823 // if ( dist2 < dist1 )
3827 // store UV in the map
3828 listUV.push_back( uv );
3829 uvMap.insert( make_pair( node, &listUV.back() ));
3831 } // loop on not yet smoothed elements
3833 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3834 checkBoundaryNodes = true;
3836 // fix nodes on mesh boundary
3838 if ( checkBoundaryNodes ) {
3839 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3840 map< SMESH_TLink, int >::iterator link_nb;
3841 // put all elements links to linkNbMap
3842 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3843 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3844 const SMDS_MeshElement* elem = (*elemIt);
3845 int nbn = elem->NbCornerNodes();
3846 // loop on elem links: insert them in linkNbMap
3847 for ( int iN = 0; iN < nbn; ++iN ) {
3848 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3849 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3850 SMESH_TLink link( n1, n2 );
3851 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3855 // remove nodes that are in links encountered only once from setMovableNodes
3856 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3857 if ( link_nb->second == 1 ) {
3858 setMovableNodes.erase( link_nb->first.node1() );
3859 setMovableNodes.erase( link_nb->first.node2() );
3864 // -----------------------------------------------------
3865 // for nodes on seam edge, compute one more UV ( uvMap2 );
3866 // find movable nodes linked to nodes on seam and which
3867 // are to be smoothed using the second UV ( uvMap2 )
3868 // -----------------------------------------------------
3870 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3871 if ( !surface.IsNull() ) {
3872 TopExp_Explorer eExp( face, TopAbs_EDGE );
3873 for ( ; eExp.More(); eExp.Next() ) {
3874 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3875 if ( !BRep_Tool::IsClosed( edge, face ))
3877 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3878 if ( !sm ) continue;
3879 // find out which parameter varies for a node on seam
3882 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3883 if ( pcurve.IsNull() ) continue;
3884 uv1 = pcurve->Value( f );
3886 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3887 if ( pcurve.IsNull() ) continue;
3888 uv2 = pcurve->Value( f );
3889 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3891 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3892 std::swap( uv1, uv2 );
3893 // get nodes on seam and its vertices
3894 list< const SMDS_MeshNode* > seamNodes;
3895 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3896 while ( nSeamIt->more() ) {
3897 const SMDS_MeshNode* node = nSeamIt->next();
3898 if ( !isQuadratic || !IsMedium( node ))
3899 seamNodes.push_back( node );
3901 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3902 for ( ; vExp.More(); vExp.Next() ) {
3903 sm = aMesh->MeshElements( vExp.Current() );
3905 nSeamIt = sm->GetNodes();
3906 while ( nSeamIt->more() )
3907 seamNodes.push_back( nSeamIt->next() );
3910 // loop on nodes on seam
3911 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3912 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3913 const SMDS_MeshNode* nSeam = *noSeIt;
3914 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3915 if ( n_uv == uvMap.end() )
3918 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3919 // set the second UV
3920 listUV.push_back( *n_uv->second );
3921 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3922 if ( uvMap2.empty() )
3923 uvMap2 = uvMap; // copy the uvMap contents
3924 uvMap2[ nSeam ] = &listUV.back();
3926 // collect movable nodes linked to ones on seam in nodesNearSeam
3927 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3928 while ( eIt->more() ) {
3929 const SMDS_MeshElement* e = eIt->next();
3930 int nbUseMap1 = 0, nbUseMap2 = 0;
3931 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3932 int nn = 0, nbn = e->NbNodes();
3933 if(e->IsQuadratic()) nbn = nbn/2;
3934 while ( nn++ < nbn )
3936 const SMDS_MeshNode* n =
3937 static_cast<const SMDS_MeshNode*>( nIt->next() );
3939 setMovableNodes.find( n ) == setMovableNodes.end() )
3941 // add only nodes being closer to uv2 than to uv1
3942 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3943 // 0.5 * ( n->Y() + nSeam->Y() ),
3944 // 0.5 * ( n->Z() + nSeam->Z() ));
3946 // getClosestUV( projector, pMid, uv );
3947 double x = uvMap[ n ]->Coord( iPar );
3948 if ( Abs( uv1.Coord( iPar ) - x ) >
3949 Abs( uv2.Coord( iPar ) - x )) {
3950 nodesNearSeam.insert( n );
3956 // for centroidalSmooth all element nodes must
3957 // be on one side of a seam
3958 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3959 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3961 while ( nn++ < nbn ) {
3962 const SMDS_MeshNode* n =
3963 static_cast<const SMDS_MeshNode*>( nIt->next() );
3964 setMovableNodes.erase( n );
3968 } // loop on nodes on seam
3969 } // loop on edge of a face
3970 } // if ( !face.IsNull() )
3972 if ( setMovableNodes.empty() ) {
3973 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3974 continue; // goto next face
3982 double maxRatio = -1., maxDisplacement = -1.;
3983 set<const SMDS_MeshNode*>::iterator nodeToMove;
3984 for ( it = 0; it < theNbIterations; it++ ) {
3985 maxDisplacement = 0.;
3986 nodeToMove = setMovableNodes.begin();
3987 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3988 const SMDS_MeshNode* node = (*nodeToMove);
3989 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3992 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3993 if ( theSmoothMethod == LAPLACIAN )
3994 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3996 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3998 // node displacement
3999 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4000 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4001 if ( aDispl > maxDisplacement )
4002 maxDisplacement = aDispl;
4004 // no node movement => exit
4005 //if ( maxDisplacement < 1.e-16 ) {
4006 if ( maxDisplacement < disttol ) {
4007 MESSAGE("-- no node movement --");
4011 // check elements quality
4013 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4014 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4015 const SMDS_MeshElement* elem = (*elemIt);
4016 if ( !elem || elem->GetType() != SMDSAbs_Face )
4018 SMESH::Controls::TSequenceOfXYZ aPoints;
4019 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4020 double aValue = aQualityFunc.GetValue( aPoints );
4021 if ( aValue > maxRatio )
4025 if ( maxRatio <= theTgtAspectRatio ) {
4026 //MESSAGE("-- quality achieved --");
4029 if (it+1 == theNbIterations) {
4030 //MESSAGE("-- Iteration limit exceeded --");
4032 } // smoothing iterations
4034 // MESSAGE(" Face id: " << *fId <<
4035 // " Nb iterstions: " << it <<
4036 // " Displacement: " << maxDisplacement <<
4037 // " Aspect Ratio " << maxRatio);
4039 // ---------------------------------------
4040 // new nodes positions are computed,
4041 // record movement in DS and set new UV
4042 // ---------------------------------------
4043 nodeToMove = setMovableNodes.begin();
4044 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4045 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4046 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4047 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4048 if ( node_uv != uvMap.end() ) {
4049 gp_XY* uv = node_uv->second;
4051 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4055 // move medium nodes of quadratic elements
4058 vector<const SMDS_MeshNode*> nodes;
4060 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4061 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4063 const SMDS_MeshElement* QF = *elemIt;
4064 if ( QF->IsQuadratic() )
4066 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4067 SMDS_MeshElement::iterator() );
4068 nodes.push_back( nodes[0] );
4070 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4072 if ( !surface.IsNull() )
4074 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4075 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4076 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4077 xyz = surface->Value( uv.X(), uv.Y() );
4080 xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4082 if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4083 // we have to move a medium node
4084 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4090 } // loop on face ids
4096 //=======================================================================
4097 //function : isReverse
4098 //purpose : Return true if normal of prevNodes is not co-directied with
4099 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4100 // iNotSame is where prevNodes and nextNodes are different.
4101 // If result is true then future volume orientation is OK
4102 //=======================================================================
4104 bool isReverse(const SMDS_MeshElement* face,
4105 const vector<const SMDS_MeshNode*>& prevNodes,
4106 const vector<const SMDS_MeshNode*>& nextNodes,
4110 SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4111 SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4112 gp_XYZ extrDir( pN - pP ), faceNorm;
4113 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4115 return faceNorm * extrDir < 0.0;
4118 //================================================================================
4120 * \brief Assure that theElemSets[0] holds elements, not nodes
4122 //================================================================================
4124 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4126 if ( !theElemSets[0].empty() &&
4127 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4129 std::swap( theElemSets[0], theElemSets[1] );
4131 else if ( !theElemSets[1].empty() &&
4132 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4134 std::swap( theElemSets[0], theElemSets[1] );
4139 //=======================================================================
4141 * \brief Create elements by sweeping an element
4142 * \param elem - element to sweep
4143 * \param newNodesItVec - nodes generated from each node of the element
4144 * \param newElems - generated elements
4145 * \param nbSteps - number of sweeping steps
4146 * \param srcElements - to append elem for each generated element
4148 //=======================================================================
4150 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4151 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4152 list<const SMDS_MeshElement*>& newElems,
4153 const size_t nbSteps,
4154 SMESH_SequenceOfElemPtr& srcElements)
4156 SMESHDS_Mesh* aMesh = GetMeshDS();
4158 const int nbNodes = elem->NbNodes();
4159 const int nbCorners = elem->NbCornerNodes();
4160 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4161 polyhedron creation !!! */
4162 // Loop on elem nodes:
4163 // find new nodes and detect same nodes indices
4164 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4165 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4166 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4167 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4169 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4170 vector<int> sames(nbNodes);
4171 vector<bool> isSingleNode(nbNodes);
4173 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4174 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4175 const SMDS_MeshNode* node = nnIt->first;
4176 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4177 if ( listNewNodes.empty() )
4180 itNN [ iNode ] = listNewNodes.begin();
4181 prevNod[ iNode ] = node;
4182 nextNod[ iNode ] = listNewNodes.front();
4184 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4185 corner node of linear */
4186 if ( prevNod[ iNode ] != nextNod [ iNode ])
4187 nbDouble += !isSingleNode[iNode];
4189 if( iNode < nbCorners ) { // check corners only
4190 if ( prevNod[ iNode ] == nextNod [ iNode ])
4191 sames[nbSame++] = iNode;
4193 iNotSameNode = iNode;
4197 if ( nbSame == nbNodes || nbSame > 2) {
4198 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4202 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4204 // fix nodes order to have bottom normal external
4205 if ( baseType == SMDSEntity_Polygon )
4207 std::reverse( itNN.begin(), itNN.end() );
4208 std::reverse( prevNod.begin(), prevNod.end() );
4209 std::reverse( midlNod.begin(), midlNod.end() );
4210 std::reverse( nextNod.begin(), nextNod.end() );
4211 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4215 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4216 SMDS_MeshCell::applyInterlace( ind, itNN );
4217 SMDS_MeshCell::applyInterlace( ind, prevNod );
4218 SMDS_MeshCell::applyInterlace( ind, nextNod );
4219 SMDS_MeshCell::applyInterlace( ind, midlNod );
4220 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4223 sames[nbSame] = iNotSameNode;
4224 for ( int j = 0; j <= nbSame; ++j )
4225 for ( size_t i = 0; i < ind.size(); ++i )
4226 if ( ind[i] == sames[j] )
4231 iNotSameNode = sames[nbSame];
4235 else if ( elem->GetType() == SMDSAbs_Edge )
4237 // orient a new face same as adjacent one
4239 const SMDS_MeshElement* e;
4240 TIDSortedElemSet dummy;
4241 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4242 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4243 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4245 // there is an adjacent face, check order of nodes in it
4246 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4249 std::swap( itNN[0], itNN[1] );
4250 std::swap( prevNod[0], prevNod[1] );
4251 std::swap( nextNod[0], nextNod[1] );
4252 std::swap( isSingleNode[0], isSingleNode[1] );
4254 sames[0] = 1 - sames[0];
4255 iNotSameNode = 1 - iNotSameNode;
4260 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4262 iSameNode = sames[ nbSame-1 ];
4263 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4264 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4265 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4268 if ( baseType == SMDSEntity_Polygon )
4270 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4271 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4273 else if ( baseType == SMDSEntity_Quad_Polygon )
4275 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4276 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4279 // make new elements
4280 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4283 for ( iNode = 0; iNode < nbNodes; iNode++ )
4285 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4286 nextNod[ iNode ] = *itNN[ iNode ]++;
4289 SMDS_MeshElement* aNewElem = 0;
4290 /*if(!elem->IsPoly())*/ {
4291 switch ( baseType ) {
4293 case SMDSEntity_Node: { // sweep NODE
4294 if ( nbSame == 0 ) {
4295 if ( isSingleNode[0] )
4296 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4298 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4304 case SMDSEntity_Edge: { // sweep EDGE
4305 if ( nbDouble == 0 )
4307 if ( nbSame == 0 ) // ---> quadrangle
4308 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4309 nextNod[ 1 ], nextNod[ 0 ] );
4310 else // ---> triangle
4311 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4312 nextNod[ iNotSameNode ] );
4314 else // ---> polygon
4316 vector<const SMDS_MeshNode*> poly_nodes;
4317 poly_nodes.push_back( prevNod[0] );
4318 poly_nodes.push_back( prevNod[1] );
4319 if ( prevNod[1] != nextNod[1] )
4321 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4322 poly_nodes.push_back( nextNod[1] );
4324 if ( prevNod[0] != nextNod[0] )
4326 poly_nodes.push_back( nextNod[0] );
4327 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4329 switch ( poly_nodes.size() ) {
4331 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4334 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4335 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4338 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4343 case SMDSEntity_Triangle: // TRIANGLE --->
4345 if ( nbDouble > 0 ) break;
4346 if ( nbSame == 0 ) // ---> pentahedron
4347 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4348 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4350 else if ( nbSame == 1 ) // ---> pyramid
4351 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4352 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4353 nextNod[ iSameNode ]);
4355 else // 2 same nodes: ---> tetrahedron
4356 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4357 nextNod[ iNotSameNode ]);
4360 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4364 if ( nbDouble+nbSame == 2 )
4366 if(nbSame==0) { // ---> quadratic quadrangle
4367 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4368 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4370 else { //(nbSame==1) // ---> quadratic triangle
4372 return; // medium node on axis
4374 else if(sames[0]==0)
4375 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4376 prevNod[2], midlNod[1], nextNod[2] );
4378 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4379 prevNod[2], nextNod[2], midlNod[0]);
4382 else if ( nbDouble == 3 )
4384 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4385 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4386 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4393 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4394 if ( nbDouble > 0 ) break;
4396 if ( nbSame == 0 ) // ---> hexahedron
4397 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4398 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4400 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4401 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4402 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4403 nextNod[ iSameNode ]);
4404 newElems.push_back( aNewElem );
4405 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4406 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4407 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4409 else if ( nbSame == 2 ) { // ---> pentahedron
4410 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4411 // iBeforeSame is same too
4412 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4413 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4414 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4416 // iAfterSame is same too
4417 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4418 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4419 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4423 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4424 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4425 if ( nbDouble+nbSame != 3 ) break;
4427 // ---> pentahedron with 15 nodes
4428 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4429 nextNod[0], nextNod[1], nextNod[2],
4430 prevNod[3], prevNod[4], prevNod[5],
4431 nextNod[3], nextNod[4], nextNod[5],
4432 midlNod[0], midlNod[1], midlNod[2]);
4434 else if(nbSame==1) {
4435 // ---> 2d order pyramid of 13 nodes
4436 int apex = iSameNode;
4437 int i0 = ( apex + 1 ) % nbCorners;
4438 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4442 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4443 nextNod[i0], nextNod[i1], prevNod[apex],
4444 prevNod[i01], midlNod[i0],
4445 nextNod[i01], midlNod[i1],
4446 prevNod[i1a], prevNod[i0a],
4447 nextNod[i0a], nextNod[i1a]);
4449 else if(nbSame==2) {
4450 // ---> 2d order tetrahedron of 10 nodes
4451 int n1 = iNotSameNode;
4452 int n2 = ( n1 + 1 ) % nbCorners;
4453 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4457 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4458 prevNod[n12], prevNod[n23], prevNod[n31],
4459 midlNod[n1], nextNod[n12], nextNod[n31]);
4463 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4465 if ( nbDouble != 4 ) break;
4466 // ---> hexahedron with 20 nodes
4467 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4468 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4469 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4470 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4471 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4473 else if(nbSame==1) {
4474 // ---> pyramid + pentahedron - can not be created since it is needed
4475 // additional middle node at the center of face
4476 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4479 else if( nbSame == 2 ) {
4480 if ( nbDouble != 2 ) break;
4481 // ---> 2d order Pentahedron with 15 nodes
4483 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4484 // iBeforeSame is same too
4491 // iAfterSame is same too
4501 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4502 prevNod[n4], prevNod[n5], nextNod[n5],
4503 prevNod[n12], midlNod[n2], nextNod[n12],
4504 prevNod[n45], midlNod[n5], nextNod[n45],
4505 prevNod[n14], prevNod[n25], nextNod[n25]);
4509 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4511 if( nbSame == 0 && nbDouble == 9 ) {
4512 // ---> tri-quadratic hexahedron with 27 nodes
4513 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4514 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4515 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4516 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4517 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4518 prevNod[8], // bottom center
4519 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4520 nextNod[8], // top center
4521 midlNod[8]);// elem center
4529 case SMDSEntity_Polygon: { // sweep POLYGON
4531 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4532 // ---> hexagonal prism
4533 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4534 prevNod[3], prevNod[4], prevNod[5],
4535 nextNod[0], nextNod[1], nextNod[2],
4536 nextNod[3], nextNod[4], nextNod[5]);
4540 case SMDSEntity_Ball:
4545 } // switch ( baseType )
4548 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4550 if ( baseType != SMDSEntity_Polygon )
4552 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4553 SMDS_MeshCell::applyInterlace( ind, prevNod );
4554 SMDS_MeshCell::applyInterlace( ind, nextNod );
4555 SMDS_MeshCell::applyInterlace( ind, midlNod );
4556 SMDS_MeshCell::applyInterlace( ind, itNN );
4557 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4558 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4560 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4561 vector<int> quantities (nbNodes + 2);
4562 polyedre_nodes.clear();
4566 for (int inode = 0; inode < nbNodes; inode++)
4567 polyedre_nodes.push_back( prevNod[inode] );
4568 quantities.push_back( nbNodes );
4571 polyedre_nodes.push_back( nextNod[0] );
4572 for (int inode = nbNodes; inode-1; --inode )
4573 polyedre_nodes.push_back( nextNod[inode-1] );
4574 quantities.push_back( nbNodes );
4582 const int iQuad = elem->IsQuadratic();
4583 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4585 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4586 int inextface = (iface+1+iQuad) % nbNodes;
4587 int imid = (iface+1) % nbNodes;
4588 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4589 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4590 polyedre_nodes.push_back( prevNod[iface] ); // 1
4591 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4593 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4594 polyedre_nodes.push_back( nextNod[iface] ); // 2
4596 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4597 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4599 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4600 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4602 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4603 if ( nbFaceNodes > 2 )
4604 quantities.push_back( nbFaceNodes );
4605 else // degenerated face
4606 polyedre_nodes.resize( prevNbNodes );
4608 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4610 } // try to create a polyherdal prism
4613 newElems.push_back( aNewElem );
4614 myLastCreatedElems.push_back(aNewElem);
4615 srcElements.push_back( elem );
4618 // set new prev nodes
4619 for ( iNode = 0; iNode < nbNodes; iNode++ )
4620 prevNod[ iNode ] = nextNod[ iNode ];
4625 //=======================================================================
4627 * \brief Create 1D and 2D elements around swept elements
4628 * \param mapNewNodes - source nodes and ones generated from them
4629 * \param newElemsMap - source elements and ones generated from them
4630 * \param elemNewNodesMap - nodes generated from each node of each element
4631 * \param elemSet - all swept elements
4632 * \param nbSteps - number of sweeping steps
4633 * \param srcElements - to append elem for each generated element
4635 //=======================================================================
4637 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4638 TTElemOfElemListMap & newElemsMap,
4639 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4640 TIDSortedElemSet& elemSet,
4642 SMESH_SequenceOfElemPtr& srcElements)
4644 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4645 SMESHDS_Mesh* aMesh = GetMeshDS();
4647 // Find nodes belonging to only one initial element - sweep them into edges.
4649 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4650 for ( ; nList != mapNewNodes.end(); nList++ )
4652 const SMDS_MeshNode* node =
4653 static_cast<const SMDS_MeshNode*>( nList->first );
4654 if ( newElemsMap.count( node ))
4655 continue; // node was extruded into edge
4656 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4657 int nbInitElems = 0;
4658 const SMDS_MeshElement* el = 0;
4659 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4660 while ( eIt->more() && nbInitElems < 2 ) {
4661 const SMDS_MeshElement* e = eIt->next();
4662 SMDSAbs_ElementType type = e->GetType();
4663 if ( type == SMDSAbs_Volume ||
4667 if ( type > highType ) {
4674 if ( nbInitElems == 1 ) {
4675 bool NotCreateEdge = el && el->IsMediumNode(node);
4676 if(!NotCreateEdge) {
4677 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4678 list<const SMDS_MeshElement*> newEdges;
4679 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4684 // Make a ceiling for each element ie an equal element of last new nodes.
4685 // Find free links of faces - make edges and sweep them into faces.
4687 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4689 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4690 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4691 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4693 const SMDS_MeshElement* elem = itElem->first;
4694 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4696 if(itElem->second.size()==0) continue;
4698 const bool isQuadratic = elem->IsQuadratic();
4700 if ( elem->GetType() == SMDSAbs_Edge ) {
4701 // create a ceiling edge
4702 if ( !isQuadratic ) {
4703 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4704 vecNewNodes[ 1 ]->second.back())) {
4705 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4706 vecNewNodes[ 1 ]->second.back()));
4707 srcElements.push_back( elem );
4711 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4712 vecNewNodes[ 1 ]->second.back(),
4713 vecNewNodes[ 2 ]->second.back())) {
4714 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4715 vecNewNodes[ 1 ]->second.back(),
4716 vecNewNodes[ 2 ]->second.back()));
4717 srcElements.push_back( elem );
4721 if ( elem->GetType() != SMDSAbs_Face )
4724 bool hasFreeLinks = false;
4726 TIDSortedElemSet avoidSet;
4727 avoidSet.insert( elem );
4729 set<const SMDS_MeshNode*> aFaceLastNodes;
4730 int iNode, nbNodes = vecNewNodes.size();
4731 if ( !isQuadratic ) {
4732 // loop on the face nodes
4733 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4734 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4735 // look for free links of the face
4736 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4737 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4738 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4739 // check if a link n1-n2 is free
4740 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4741 hasFreeLinks = true;
4742 // make a new edge and a ceiling for a new edge
4743 const SMDS_MeshElement* edge;
4744 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4745 myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4746 srcElements.push_back( myLastCreatedElems.back() );
4748 n1 = vecNewNodes[ iNode ]->second.back();
4749 n2 = vecNewNodes[ iNext ]->second.back();
4750 if ( !aMesh->FindEdge( n1, n2 )) {
4751 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4752 srcElements.push_back( edge );
4757 else { // elem is quadratic face
4758 int nbn = nbNodes/2;
4759 for ( iNode = 0; iNode < nbn; iNode++ ) {
4760 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4761 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4762 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4763 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4764 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4765 // check if a link is free
4766 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4767 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4768 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4769 hasFreeLinks = true;
4770 // make an edge and a ceiling for a new edge
4772 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4773 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4774 srcElements.push_back( elem );
4776 n1 = vecNewNodes[ iNode ]->second.back();
4777 n2 = vecNewNodes[ iNext ]->second.back();
4778 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4779 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4780 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4781 srcElements.push_back( elem );
4785 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4786 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4790 // sweep free links into faces
4792 if ( hasFreeLinks ) {
4793 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4794 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4796 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4797 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4798 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4799 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4800 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4802 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4803 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4804 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4806 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4807 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4808 std::advance( v, volNb );
4809 // find indices of free faces of a volume and their source edges
4810 list< int > freeInd;
4811 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4812 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4813 int iF, nbF = vTool.NbFaces();
4814 for ( iF = 0; iF < nbF; iF ++ ) {
4815 if ( vTool.IsFreeFace( iF ) &&
4816 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4817 initNodeSet != faceNodeSet) // except an initial face
4819 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4821 if ( faceNodeSet == initNodeSetNoCenter )
4823 freeInd.push_back( iF );
4824 // find source edge of a free face iF
4825 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4826 vector<const SMDS_MeshNode*>::iterator lastCommom;
4827 commonNodes.resize( nbNodes, 0 );
4828 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4829 initNodeSet.begin(), initNodeSet.end(),
4830 commonNodes.begin());
4831 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4832 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4834 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4836 if ( !srcEdges.back() )
4838 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4839 << iF << " of volume #" << vTool.ID() << endl;
4844 if ( freeInd.empty() )
4847 // create wall faces for all steps;
4848 // if such a face has been already created by sweep of edge,
4849 // assure that its orientation is OK
4850 for ( int iStep = 0; iStep < nbSteps; iStep++ )
4852 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4853 vTool.SetExternalNormal();
4854 const int nextShift = vTool.IsForward() ? +1 : -1;
4855 list< int >::iterator ind = freeInd.begin();
4856 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4857 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4859 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4860 int nbn = vTool.NbFaceNodes( *ind );
4861 const SMDS_MeshElement * f = 0;
4862 if ( nbn == 3 ) ///// triangle
4864 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4866 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4868 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4870 nodes[ 1 + nextShift ] };
4872 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4874 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4878 else if ( nbn == 4 ) ///// quadrangle
4880 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4882 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4884 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4885 nodes[ 2 ], nodes[ 2+nextShift ] };
4887 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4889 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4890 newOrder[ 2 ], newOrder[ 3 ]));
4893 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4895 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4897 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4899 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4901 nodes[2 + 2*nextShift],
4902 nodes[3 - 2*nextShift],
4904 nodes[3 + 2*nextShift]};
4906 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4908 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4916 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4918 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4919 nodes[1], nodes[3], nodes[5], nodes[7] );
4921 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4923 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4924 nodes[4 - 2*nextShift],
4926 nodes[4 + 2*nextShift],
4928 nodes[5 - 2*nextShift],
4930 nodes[5 + 2*nextShift] };
4932 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4934 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4935 newOrder[ 2 ], newOrder[ 3 ],
4936 newOrder[ 4 ], newOrder[ 5 ],
4937 newOrder[ 6 ], newOrder[ 7 ]));
4940 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4942 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4943 SMDSAbs_Face, /*noMedium=*/false);
4945 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4947 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4948 nodes[4 - 2*nextShift],
4950 nodes[4 + 2*nextShift],
4952 nodes[5 - 2*nextShift],
4954 nodes[5 + 2*nextShift],
4957 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4959 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4960 newOrder[ 2 ], newOrder[ 3 ],
4961 newOrder[ 4 ], newOrder[ 5 ],
4962 newOrder[ 6 ], newOrder[ 7 ],
4966 else //////// polygon
4968 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4969 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4971 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4973 if ( !vTool.IsForward() )
4974 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4976 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4978 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
4982 while ( srcElements.size() < myLastCreatedElems.size() )
4983 srcElements.push_back( *srcEdge );
4985 } // loop on free faces
4987 // go to the next volume
4989 while ( iVol++ < nbVolumesByStep ) v++;
4992 } // loop on volumes of one step
4993 } // sweep free links into faces
4995 // Make a ceiling face with a normal external to a volume
4997 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
4998 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4999 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5001 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5002 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5003 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5007 lastVol.SetExternalNormal();
5008 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5009 const int nbn = lastVol.NbFaceNodes( iF );
5010 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5011 if ( !hasFreeLinks ||
5012 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5014 const vector<int>& interlace =
5015 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5016 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5018 AddElement( nodeVec, anyFace.Init( elem ));
5020 while ( srcElements.size() < myLastCreatedElems.size() )
5021 srcElements.push_back( elem );
5024 } // loop on swept elements
5027 //=======================================================================
5028 //function : RotationSweep
5030 //=======================================================================
5032 SMESH_MeshEditor::PGroupIDs
5033 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5034 const gp_Ax1& theAxis,
5035 const double theAngle,
5036 const int theNbSteps,
5037 const double theTol,
5038 const bool theMakeGroups,
5039 const bool theMakeWalls)
5043 setElemsFirst( theElemSets );
5044 myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5045 myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5047 // source elements for each generated one
5048 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5049 srcElems.reserve( theElemSets[0].size() );
5050 srcNodes.reserve( theElemSets[1].size() );
5053 aTrsf.SetRotation( theAxis, theAngle );
5055 aTrsf2.SetRotation( theAxis, theAngle/2. );
5057 gp_Lin aLine( theAxis );
5058 double aSqTol = theTol * theTol;
5060 SMESHDS_Mesh* aMesh = GetMeshDS();
5062 TNodeOfNodeListMap mapNewNodes;
5063 TElemOfVecOfNnlmiMap mapElemNewNodes;
5064 TTElemOfElemListMap newElemsMap;
5066 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5067 myMesh->NbFaces(ORDER_QUADRATIC) +
5068 myMesh->NbVolumes(ORDER_QUADRATIC) );
5069 // loop on theElemSets
5070 TIDSortedElemSet::iterator itElem;
5071 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5073 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5074 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5075 const SMDS_MeshElement* elem = *itElem;
5076 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5078 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5079 newNodesItVec.reserve( elem->NbNodes() );
5081 // loop on elem nodes
5082 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5083 while ( itN->more() )
5085 const SMDS_MeshNode* node = cast2Node( itN->next() );
5087 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5089 aXYZ.Coord( coord[0], coord[1], coord[2] );
5090 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5092 // check if a node has been already sweeped
5093 TNodeOfNodeListMapItr nIt =
5094 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5095 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5096 if ( listNewNodes.empty() )
5098 // check if we are to create medium nodes between corner ones
5099 bool needMediumNodes = false;
5100 if ( isQuadraticMesh )
5102 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5103 while (it->more() && !needMediumNodes )
5105 const SMDS_MeshElement* invElem = it->next();
5106 if ( invElem != elem && !theElems.count( invElem )) continue;
5107 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5108 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5109 needMediumNodes = true;
5114 const SMDS_MeshNode * newNode = node;
5115 for ( int i = 0; i < theNbSteps; i++ ) {
5117 if ( needMediumNodes ) // create a medium node
5119 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5120 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5121 myLastCreatedNodes.push_back(newNode);
5122 srcNodes.push_back( node );
5123 listNewNodes.push_back( newNode );
5124 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5127 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5129 // create a corner node
5130 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5131 myLastCreatedNodes.push_back(newNode);
5132 srcNodes.push_back( node );
5133 listNewNodes.push_back( newNode );
5136 listNewNodes.push_back( newNode );
5137 // if ( needMediumNodes )
5138 // listNewNodes.push_back( newNode );
5142 newNodesItVec.push_back( nIt );
5144 // make new elements
5145 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5150 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5152 PGroupIDs newGroupIDs;
5153 if ( theMakeGroups )
5154 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5159 //=======================================================================
5160 //function : ExtrusParam
5161 //purpose : standard construction
5162 //=======================================================================
5164 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5165 const int theNbSteps,
5166 const std::list<double>& theScales,
5167 const std::list<double>& theAngles,
5168 const gp_XYZ* theBasePoint,
5170 const double theTolerance):
5172 myBaseP( Precision::Infinite(), 0, 0 ),
5173 myFlags( theFlags ),
5174 myTolerance( theTolerance ),
5175 myElemsToUse( NULL )
5177 mySteps = new TColStd_HSequenceOfReal;
5178 const double stepSize = theStep.Magnitude();
5179 for (int i=1; i<=theNbSteps; i++ )
5180 mySteps->Append( stepSize );
5182 if ( !theScales.empty() )
5184 if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5185 linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5187 // add medium scales
5188 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5189 myScales.reserve( theNbSteps * 2 );
5190 myScales.push_back( 0.5 * ( *s1 + 1. ));
5191 myScales.push_back( *s1 );
5192 for ( ; s2 != theScales.end(); s1 = s2++ )
5194 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5195 myScales.push_back( *s2 );
5199 if ( !theAngles.empty() )
5201 std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5202 if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5203 linearAngleVariation( theNbSteps, angles );
5205 // accumulate angles
5208 std::list<double>::iterator a1 = angles.begin(), a2;
5209 for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5214 while ( nbAngles++ < theNbSteps )
5215 angles.push_back( angles.back() );
5217 // add medium angles
5218 a2 = angles.begin(), a1 = a2++;
5219 myAngles.push_back( 0.5 * *a1 );
5220 myAngles.push_back( *a1 );
5221 for ( ; a2 != angles.end(); a1 = a2++ )
5223 myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5224 myAngles.push_back( *a2 );
5230 myBaseP = *theBasePoint;
5233 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5234 ( theTolerance > 0 ))
5236 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5240 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5244 //=======================================================================
5245 //function : ExtrusParam
5246 //purpose : steps are given explicitly
5247 //=======================================================================
5249 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5250 Handle(TColStd_HSequenceOfReal) theSteps,
5252 const double theTolerance):
5254 mySteps( theSteps ),
5255 myFlags( theFlags ),
5256 myTolerance( theTolerance ),
5257 myElemsToUse( NULL )
5259 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5260 ( theTolerance > 0 ))
5262 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5266 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5270 //=======================================================================
5271 //function : ExtrusParam
5272 //purpose : for extrusion by normal
5273 //=======================================================================
5275 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5276 const int theNbSteps,
5280 mySteps( new TColStd_HSequenceOfReal ),
5281 myFlags( theFlags ),
5283 myElemsToUse( NULL )
5285 for (int i = 0; i < theNbSteps; i++ )
5286 mySteps->Append( theStepSize );
5290 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5294 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5298 //=======================================================================
5299 //function : ExtrusParam
5300 //purpose : for extrusion along path
5301 //=======================================================================
5303 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5304 const gp_Pnt* theBasePoint,
5305 const std::list<double>& theScales,
5306 const bool theMakeGroups )
5307 : myBaseP( Precision::Infinite(), 0, 0 ),
5308 myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5309 myPathPoints( thePoints )
5313 myBaseP = theBasePoint->XYZ();
5316 if ( !theScales.empty() )
5318 // add medium scales
5319 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5320 myScales.reserve( thePoints.size() * 2 );
5321 myScales.push_back( 0.5 * ( 1. + *s1 ));
5322 myScales.push_back( *s1 );
5323 for ( ; s2 != theScales.end(); s1 = s2++ )
5325 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5326 myScales.push_back( *s2 );
5330 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5333 //=======================================================================
5334 //function : ExtrusParam::SetElementsToUse
5335 //purpose : stores elements to use for extrusion by normal, depending on
5336 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5337 // define myBaseP for scaling
5338 //=======================================================================
5340 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5341 const TIDSortedElemSet& nodes )
5343 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5345 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5347 myBaseP.SetCoord( 0.,0.,0. );
5348 TIDSortedElemSet newNodes;
5350 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5351 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5353 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5354 TIDSortedElemSet::const_iterator itElem = elements.begin();
5355 for ( ; itElem != elements.end(); itElem++ )
5357 const SMDS_MeshElement* elem = *itElem;
5358 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5359 while ( itN->more() ) {
5360 const SMDS_MeshElement* node = itN->next();
5361 if ( newNodes.insert( node ).second )
5362 myBaseP += SMESH_NodeXYZ( node );
5366 myBaseP /= newNodes.size();
5370 //=======================================================================
5371 //function : ExtrusParam::beginStepIter
5372 //purpose : prepare iteration on steps
5373 //=======================================================================
5375 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5377 myWithMediumNodes = withMediumNodes;
5381 //=======================================================================
5382 //function : ExtrusParam::moreSteps
5383 //purpose : are there more steps?
5384 //=======================================================================
5386 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5388 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5390 //=======================================================================
5391 //function : ExtrusParam::nextStep
5392 //purpose : returns the next step
5393 //=======================================================================
5395 double SMESH_MeshEditor::ExtrusParam::nextStep()
5398 if ( !myCurSteps.empty() )
5400 res = myCurSteps.back();
5401 myCurSteps.pop_back();
5403 else if ( myNextStep <= mySteps->Length() )
5405 myCurSteps.push_back( mySteps->Value( myNextStep ));
5407 if ( myWithMediumNodes )
5409 myCurSteps.back() /= 2.;
5410 myCurSteps.push_back( myCurSteps.back() );
5417 //=======================================================================
5418 //function : ExtrusParam::makeNodesByDir
5419 //purpose : create nodes for standard extrusion
5420 //=======================================================================
5422 int SMESH_MeshEditor::ExtrusParam::
5423 makeNodesByDir( SMESHDS_Mesh* mesh,
5424 const SMDS_MeshNode* srcNode,
5425 std::list<const SMDS_MeshNode*> & newNodes,
5426 const bool makeMediumNodes)
5428 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5431 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5433 p += myDir.XYZ() * nextStep();
5434 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5435 newNodes.push_back( newNode );
5438 if ( !myScales.empty() || !myAngles.empty() )
5440 gp_XYZ center = myBaseP;
5441 gp_Ax1 ratationAxis( center, myDir );
5444 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5445 size_t i = !makeMediumNodes;
5446 for ( beginStepIter( makeMediumNodes );
5448 ++nIt, i += 1 + !makeMediumNodes )
5450 center += myDir.XYZ() * nextStep();
5452 gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5454 if ( i < myScales.size() )
5456 xyz = ( myScales[i] * ( xyz - center )) + center;
5459 if ( !myAngles.empty() )
5461 rotation.SetRotation( ratationAxis, myAngles[i] );
5462 rotation.Transforms( xyz );
5466 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5474 //=======================================================================
5475 //function : ExtrusParam::makeNodesByDirAndSew
5476 //purpose : create nodes for standard extrusion with sewing
5477 //=======================================================================
5479 int SMESH_MeshEditor::ExtrusParam::
5480 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5481 const SMDS_MeshNode* srcNode,
5482 std::list<const SMDS_MeshNode*> & newNodes,
5483 const bool makeMediumNodes)
5485 gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5488 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5490 P1 += myDir.XYZ() * nextStep();
5492 // try to search in sequence of existing nodes
5493 // if myNodes.size()>0 we 'nave to use given sequence
5494 // else - use all nodes of mesh
5495 const SMDS_MeshNode * node = 0;
5496 if ( myNodes.Length() > 0 )
5498 for ( int i = 1; i <= myNodes.Length(); i++ )
5500 SMESH_NodeXYZ P2 = myNodes.Value(i);
5501 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5503 node = myNodes.Value(i);
5510 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5513 SMESH_NodeXYZ P2 = itn->next();
5514 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5523 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5525 newNodes.push_back( node );
5532 //=======================================================================
5533 //function : ExtrusParam::makeNodesByNormal2D
5534 //purpose : create nodes for extrusion using normals of faces
5535 //=======================================================================
5537 int SMESH_MeshEditor::ExtrusParam::
5538 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5539 const SMDS_MeshNode* srcNode,
5540 std::list<const SMDS_MeshNode*> & newNodes,
5541 const bool makeMediumNodes)
5543 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5545 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5547 // get normals to faces sharing srcNode
5548 vector< gp_XYZ > norms, baryCenters;
5549 gp_XYZ norm, avgNorm( 0,0,0 );
5550 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5551 while ( faceIt->more() )
5553 const SMDS_MeshElement* face = faceIt->next();
5554 if ( myElemsToUse && !myElemsToUse->count( face ))
5556 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5558 norms.push_back( norm );
5560 if ( !alongAvgNorm )
5564 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5565 bc += SMESH_NodeXYZ( nIt->next() );
5566 baryCenters.push_back( bc / nbN );
5571 if ( norms.empty() ) return 0;
5573 double normSize = avgNorm.Modulus();
5574 if ( normSize < std::numeric_limits<double>::min() )
5577 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5580 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5583 avgNorm /= normSize;
5586 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5589 double stepSize = nextStep();
5591 if ( norms.size() > 1 )
5593 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5595 // translate plane of a face
5596 baryCenters[ iF ] += norms[ iF ] * stepSize;
5598 // find point of intersection of the face plane located at baryCenters[ iF ]
5599 // and avgNorm located at pNew
5600 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5601 double dot = ( norms[ iF ] * avgNorm );
5602 if ( dot < std::numeric_limits<double>::min() )
5603 dot = stepSize * 1e-3;
5604 double step = -( norms[ iF ] * pNew + d ) / dot;
5605 pNew += step * avgNorm;
5610 pNew += stepSize * avgNorm;
5614 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5615 newNodes.push_back( newNode );
5620 //=======================================================================
5621 //function : ExtrusParam::makeNodesByNormal1D
5622 //purpose : create nodes for extrusion using normals of edges
5623 //=======================================================================
5625 int SMESH_MeshEditor::ExtrusParam::
5626 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5627 const SMDS_MeshNode* srcNode,
5628 std::list<const SMDS_MeshNode*> & newNodes,
5629 const bool makeMediumNodes)
5631 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5635 //=======================================================================
5636 //function : ExtrusParam::makeNodesAlongTrack
5637 //purpose : create nodes for extrusion along path
5638 //=======================================================================
5640 int SMESH_MeshEditor::ExtrusParam::
5641 makeNodesAlongTrack( SMESHDS_Mesh* mesh,
5642 const SMDS_MeshNode* srcNode,
5643 std::list<const SMDS_MeshNode*> & newNodes,
5644 const bool makeMediumNodes)
5646 const Standard_Real aTolAng=1.e-4;
5648 gp_Pnt aV0x = myBaseP;
5649 gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
5651 const PathPoint& aPP0 = myPathPoints[0];
5652 gp_Pnt aP0x = aPP0.myPnt;
5653 gp_Dir aDT0x= aPP0.myTgt;
5655 std::vector< gp_Pnt > centers;
5656 centers.reserve( NbSteps() * 2 );
5658 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5660 for ( size_t j = 1; j < myPathPoints.size(); ++j )
5662 const PathPoint& aPP = myPathPoints[j];
5663 const gp_Pnt& aP1x = aPP.myPnt;
5664 const gp_Dir& aDT1x = aPP.myTgt;
5667 gp_Vec aV01x( aP0x, aP1x );
5668 aTrsf.SetTranslation( aV01x );
5669 gp_Pnt aV1x = aV0x.Transformed( aTrsf );
5670 gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
5672 // rotation 1 [ T1,T0 ]
5673 Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
5674 if ( fabs( aAngleT1T0 ) > aTolAng )
5676 gp_Dir aDT1T0 = aDT1x ^ aDT0x;
5677 aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
5679 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5683 if ( aPP.myAngle != 0. )
5685 aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
5686 aPN1 = aPN1.Transformed( aTrsfRot );
5690 if ( makeMediumNodes )
5692 // create additional node
5693 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
5694 const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
5695 newNodes.push_back( newNode );
5698 const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5699 newNodes.push_back( newNode );
5701 centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
5702 centers.push_back( aV1x );
5711 if ( !myScales.empty() )
5714 std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
5715 for ( size_t i = !makeMediumNodes;
5716 i < myScales.size() && node != newNodes.end();
5717 i += ( 1 + !makeMediumNodes ), ++node )
5719 aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
5720 gp_Pnt aN = SMESH_NodeXYZ( *node );
5721 gp_Pnt aP = aN.Transformed( aTrsfScale );
5722 mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
5726 return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
5729 //=======================================================================
5730 //function : ExtrusionSweep
5732 //=======================================================================
5734 SMESH_MeshEditor::PGroupIDs
5735 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5736 const gp_Vec& theStep,
5737 const int theNbSteps,
5738 TTElemOfElemListMap& newElemsMap,
5740 const double theTolerance)
5742 std::list<double> dummy;
5743 ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
5744 theFlags, theTolerance );
5745 return ExtrusionSweep( theElems, aParams, newElemsMap );
5751 //=======================================================================
5752 //function : getOriFactor
5753 //purpose : Return -1 or 1 depending on if order of given nodes corresponds to
5754 // edge curve orientation
5755 //=======================================================================
5757 double getOriFactor( const TopoDS_Edge& edge,
5758 const SMDS_MeshNode* n1,
5759 const SMDS_MeshNode* n2,
5760 SMESH_MesherHelper& helper)
5762 double u1 = helper.GetNodeU( edge, n1, n2 );
5763 double u2 = helper.GetNodeU( edge, n2, n1 );
5764 return u1 < u2 ? 1. : -1.;
5768 //=======================================================================
5769 //function : ExtrusionSweep
5771 //=======================================================================
5773 SMESH_MeshEditor::PGroupIDs
5774 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5775 ExtrusParam& theParams,
5776 TTElemOfElemListMap& newElemsMap)
5780 setElemsFirst( theElemSets );
5781 myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5782 myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5784 // source elements for each generated one
5785 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5786 srcElems.reserve( theElemSets[0].size() );
5787 srcNodes.reserve( theElemSets[1].size() );
5789 const int nbSteps = theParams.NbSteps();
5790 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5792 TNodeOfNodeListMap mapNewNodes;
5793 TElemOfVecOfNnlmiMap mapElemNewNodes;
5795 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5796 myMesh->NbFaces(ORDER_QUADRATIC) +
5797 myMesh->NbVolumes(ORDER_QUADRATIC) );
5799 TIDSortedElemSet::iterator itElem;
5800 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5802 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5803 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5805 // check element type
5806 const SMDS_MeshElement* elem = *itElem;
5807 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5810 const size_t nbNodes = elem->NbNodes();
5811 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5812 newNodesItVec.reserve( nbNodes );
5814 // loop on elem nodes
5815 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
5816 while ( itN->more() )
5818 // check if a node has been already sweeped
5819 const SMDS_MeshNode* node = itN->next();
5820 TNodeOfNodeListMap::iterator nIt =
5821 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5822 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5823 if ( listNewNodes.empty() )
5827 // check if we are to create medium nodes between corner ones
5828 bool needMediumNodes = false;
5829 if ( isQuadraticMesh )
5831 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5832 while (it->more() && !needMediumNodes )
5834 const SMDS_MeshElement* invElem = it->next();
5835 if ( invElem != elem && !theElems.count( invElem )) continue;
5836 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5837 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5838 needMediumNodes = true;
5841 // create nodes for all steps
5842 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5844 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5845 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5847 myLastCreatedNodes.push_back( *newNodesIt );
5848 srcNodes.push_back( node );
5853 break; // newNodesItVec will be shorter than nbNodes
5856 newNodesItVec.push_back( nIt );
5858 // make new elements
5859 if ( newNodesItVec.size() == nbNodes )
5860 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5864 if ( theParams.ToMakeBoundary() ) {
5865 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5867 PGroupIDs newGroupIDs;
5868 if ( theParams.ToMakeGroups() )
5869 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5874 //=======================================================================
5875 //function : ExtrusionAlongTrack
5877 //=======================================================================
5878 SMESH_MeshEditor::Extrusion_Error
5879 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5880 SMESH_Mesh* theTrackMesh,
5881 SMDS_ElemIteratorPtr theTrackIterator,
5882 const SMDS_MeshNode* theN1,
5883 std::list<double>& theAngles,
5884 const bool theAngleVariation,
5885 std::list<double>& theScales,
5886 const bool theScaleVariation,
5887 const gp_Pnt* theRefPoint,
5888 const bool theMakeGroups)
5893 if ( theElements[0].empty() && theElements[1].empty() )
5894 return EXTR_NO_ELEMENTS;
5896 ASSERT( theTrackMesh );
5897 if ( ! theTrackIterator || !theTrackIterator->more() )
5898 return EXTR_NO_ELEMENTS;
5900 // 2. Get ordered nodes
5901 SMESH_MeshAlgos::TElemGroupVector branchEdges;
5902 SMESH_MeshAlgos::TNodeGroupVector branchNods;
5903 SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
5904 if ( branchEdges.empty() )
5905 return EXTR_PATH_NOT_EDGE;
5907 if ( branchEdges.size() > 1 )
5908 return EXTR_BAD_PATH_SHAPE;
5910 std::vector< const SMDS_MeshNode* >& pathNodes = branchNods[0];
5911 std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
5912 if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
5913 return EXTR_BAD_STARTING_NODE;
5915 if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
5917 // add medium nodes to pathNodes
5918 std::vector< const SMDS_MeshNode* > pathNodes2;
5919 std::vector< const SMDS_MeshElement* > pathEdges2;
5920 pathNodes2.reserve( pathNodes.size() * 2 );
5921 pathEdges2.reserve( pathEdges.size() * 2 );
5922 for ( size_t i = 0; i < pathEdges.size(); ++i )
5924 pathNodes2.push_back( pathNodes[i] );
5925 pathEdges2.push_back( pathEdges[i] );
5926 if ( pathEdges[i]->IsQuadratic() )
5928 pathNodes2.push_back( pathEdges[i]->GetNode(2) );
5929 pathEdges2.push_back( pathEdges[i] );
5932 pathNodes2.push_back( pathNodes.back() );
5933 pathEdges.swap( pathEdges2 );
5934 pathNodes.swap( pathNodes2 );
5937 // 3. Get path data at pathNodes
5939 std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
5941 if ( theAngleVariation )
5942 linearAngleVariation( points.size()-1, theAngles );
5943 if ( theScaleVariation )
5944 linearScaleVariation( points.size()-1, theScales );
5946 theAngles.push_front( 0 ); // for the 1st point that is not transformed
5947 std::list<double>::iterator angle = theAngles.begin();
5949 SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
5951 std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
5952 std::map< int, double >::iterator id2factor;
5953 SMESH_MesherHelper pathHelper( *theTrackMesh );
5954 gp_Pnt p; gp_Vec tangent;
5955 const double tol2 = gp::Resolution() * gp::Resolution();
5957 for ( size_t i = 0; i < pathNodes.size(); ++i )
5959 ExtrusParam::PathPoint & point = points[ i ];
5961 point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
5963 if ( angle != theAngles.end() )
5964 point.myAngle = *angle++;
5966 tangent.SetCoord( 0,0,0 );
5967 const int shapeID = pathNodes[ i ]->GetShapeID();
5968 const TopoDS_Shape& shape = pathMeshDS->IndexToShape( shapeID );
5969 TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
5970 switch ( shapeType )
5974 TopoDS_Edge edge = TopoDS::Edge( shape );
5975 id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
5976 if ( id2factor->second == 0 )
5978 if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
5979 else id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
5981 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
5982 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
5983 curve->D1( u, p, tangent );
5984 tangent *= id2factor->second;
5990 PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
5991 while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
5993 int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
5994 for ( int di = -1; di <= 0; ++di )
5997 if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
5999 TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6000 id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6001 if ( id2factor->second == 0 )
6004 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6006 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6008 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6009 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6011 curve->D1( u, p, du );
6012 double size2 = du.SquareMagnitude();
6013 if ( du.SquareMagnitude() > tol2 )
6015 tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6027 for ( int di = -1; di <= 1; di += 2 )
6030 if ( j < pathNodes.size() )
6032 gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6033 double size2 = dir.SquareMagnitude();
6035 tangent += dir.Divided( Sqrt( size2 )) * di;
6039 } // switch ( shapeType )
6041 if ( tangent.SquareMagnitude() < tol2 )
6042 return EXTR_CANT_GET_TANGENT;
6044 point.myTgt = tangent;
6046 } // loop on pathNodes
6049 ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6050 TTElemOfElemListMap newElemsMap;
6052 ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6057 //=======================================================================
6058 //function : linearAngleVariation
6059 //purpose : spread values over nbSteps
6060 //=======================================================================
6062 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6063 list<double>& Angles)
6065 int nbAngles = Angles.size();
6066 if( nbSteps > nbAngles && nbAngles > 0 )
6068 vector<double> theAngles(nbAngles);
6069 theAngles.assign( Angles.begin(), Angles.end() );
6072 double rAn2St = double( nbAngles ) / double( nbSteps );
6073 double angPrev = 0, angle;
6074 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6076 double angCur = rAn2St * ( iSt+1 );
6077 double angCurFloor = floor( angCur );
6078 double angPrevFloor = floor( angPrev );
6079 if ( angPrevFloor == angCurFloor )
6080 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6082 int iP = int( angPrevFloor );
6083 double angPrevCeil = ceil(angPrev);
6084 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6086 int iC = int( angCurFloor );
6087 if ( iC < nbAngles )
6088 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6090 iP = int( angPrevCeil );
6092 angle += theAngles[ iC ];
6094 res.push_back(angle);
6101 //=======================================================================
6102 //function : linearScaleVariation
6103 //purpose : spread values over nbSteps
6104 //=======================================================================
6106 void SMESH_MeshEditor::linearScaleVariation(const int theNbSteps,
6107 std::list<double>& theScales)
6109 int nbScales = theScales.size();
6110 std::vector<double> myScales;
6111 myScales.reserve( theNbSteps );
6112 std::list<double>::const_iterator scale = theScales.begin();
6113 double prevScale = 1.0;
6114 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6116 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6117 int stDelta = Max( 1, iStep - myScales.size());
6118 double scDelta = ( *scale - prevScale ) / stDelta;
6119 for ( int iStep = 0; iStep < stDelta; ++iStep )
6121 myScales.push_back( prevScale + scDelta );
6122 prevScale = myScales.back();
6126 theScales.assign( myScales.begin(), myScales.end() );
6129 //================================================================================
6131 * \brief Move or copy theElements applying theTrsf to their nodes
6132 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6133 * \param theTrsf - transformation to apply
6134 * \param theCopy - if true, create translated copies of theElems
6135 * \param theMakeGroups - if true and theCopy, create translated groups
6136 * \param theTargetMesh - mesh to copy translated elements into
6137 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6139 //================================================================================
6141 SMESH_MeshEditor::PGroupIDs
6142 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6143 const gp_Trsf& theTrsf,
6145 const bool theMakeGroups,
6146 SMESH_Mesh* theTargetMesh)
6149 myLastCreatedElems.reserve( theElems.size() );
6151 bool needReverse = false;
6152 string groupPostfix;
6153 switch ( theTrsf.Form() ) {
6156 groupPostfix = "mirrored";
6159 groupPostfix = "mirrored";
6163 groupPostfix = "mirrored";
6166 groupPostfix = "rotated";
6168 case gp_Translation:
6169 groupPostfix = "translated";
6172 groupPostfix = "scaled";
6174 case gp_CompoundTrsf: // different scale by axis
6175 groupPostfix = "scaled";
6178 needReverse = false;
6179 groupPostfix = "transformed";
6182 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6183 SMESHDS_Mesh* aMesh = GetMeshDS();
6185 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6186 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6187 SMESH_MeshEditor::ElemFeatures elemType;
6189 // map old node to new one
6190 TNodeNodeMap nodeMap;
6192 // elements sharing moved nodes; those of them which have all
6193 // nodes mirrored but are not in theElems are to be reversed
6194 TIDSortedElemSet inverseElemSet;
6196 // source elements for each generated one
6197 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6199 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6200 TIDSortedElemSet orphanNode;
6202 if ( theElems.empty() ) // transform the whole mesh
6205 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6206 while ( eIt->more() ) theElems.insert( eIt->next() );
6208 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6209 while ( nIt->more() )
6211 const SMDS_MeshNode* node = nIt->next();
6212 if ( node->NbInverseElements() == 0)
6213 orphanNode.insert( node );
6217 // loop on elements to transform nodes : first orphan nodes then elems
6218 TIDSortedElemSet::iterator itElem;
6219 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6220 for (int i=0; i<2; i++)
6221 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6223 const SMDS_MeshElement* elem = *itElem;
6227 // loop on elem nodes
6229 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6230 while ( itN->more() )
6232 const SMDS_MeshNode* node = cast2Node( itN->next() );
6233 // check if a node has been already transformed
6234 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6235 nodeMap.insert( make_pair ( node, node ));
6236 if ( !n2n_isnew.second )
6239 node->GetXYZ( coord );
6240 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6241 if ( theTargetMesh ) {
6242 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6243 n2n_isnew.first->second = newNode;
6244 myLastCreatedNodes.push_back(newNode);
6245 srcNodes.push_back( node );
6247 else if ( theCopy ) {
6248 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6249 n2n_isnew.first->second = newNode;
6250 myLastCreatedNodes.push_back(newNode);
6251 srcNodes.push_back( node );
6254 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6255 // node position on shape becomes invalid
6256 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6257 ( SMDS_SpacePosition::originSpacePosition() );
6260 // keep inverse elements
6261 if ( !theCopy && !theTargetMesh && needReverse ) {
6262 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6263 while ( invElemIt->more() ) {
6264 const SMDS_MeshElement* iel = invElemIt->next();
6265 inverseElemSet.insert( iel );
6269 } // loop on elems in { &orphanNode, &theElems };
6271 // either create new elements or reverse mirrored ones
6272 if ( !theCopy && !needReverse && !theTargetMesh )
6275 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6277 // Replicate or reverse elements
6279 std::vector<int> iForw;
6280 vector<const SMDS_MeshNode*> nodes;
6281 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6283 const SMDS_MeshElement* elem = *itElem;
6284 if ( !elem ) continue;
6286 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6287 size_t nbNodes = elem->NbNodes();
6288 if ( geomType == SMDSGeom_NONE ) continue; // node
6290 nodes.resize( nbNodes );
6292 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6294 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6298 bool allTransformed = true;
6299 int nbFaces = aPolyedre->NbFaces();
6300 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6302 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6303 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6305 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6306 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6307 if ( nodeMapIt == nodeMap.end() )
6308 allTransformed = false; // not all nodes transformed
6310 nodes.push_back((*nodeMapIt).second);
6312 if ( needReverse && allTransformed )
6313 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6315 if ( !allTransformed )
6316 continue; // not all nodes transformed
6318 else // ----------------------- the rest element types
6320 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6321 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6322 const vector<int>& i = needReverse ? iRev : iForw;
6324 // find transformed nodes
6326 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6327 while ( itN->more() ) {
6328 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6329 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6330 if ( nodeMapIt == nodeMap.end() )
6331 break; // not all nodes transformed
6332 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6334 if ( iNode != nbNodes )
6335 continue; // not all nodes transformed
6339 // copy in this or a new mesh
6340 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6341 srcElems.push_back( elem );
6344 // reverse element as it was reversed by transformation
6346 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6349 } // loop on elements
6351 if ( editor && editor != this )
6352 myLastCreatedElems.swap( editor->myLastCreatedElems );
6354 PGroupIDs newGroupIDs;
6356 if ( ( theMakeGroups && theCopy ) ||
6357 ( theMakeGroups && theTargetMesh ) )
6358 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6363 //================================================================================
6365 * \brief Make an offset mesh from a source 2D mesh
6366 * \param [in] theElements - source faces
6367 * \param [in] theValue - offset value
6368 * \param [out] theTgtMesh - a mesh to add offset elements to
6369 * \param [in] theMakeGroups - to generate groups
6370 * \return PGroupIDs - IDs of created groups
6372 //================================================================================
6374 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6375 const double theValue,
6376 SMESH_Mesh* theTgtMesh,
6377 const bool theMakeGroups,
6378 const bool theCopyElements,
6379 const bool theFixSelfIntersection)
6381 SMESHDS_Mesh* meshDS = GetMeshDS();
6382 SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6383 SMESH_MeshEditor tgtEditor( theTgtMesh );
6385 SMDS_ElemIteratorPtr eIt;
6386 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6387 else eIt = SMESHUtils::elemSetIterator( theElements );
6389 SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6390 SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6391 std::unique_ptr< SMDS_Mesh > offsetMesh
6392 ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6393 theFixSelfIntersection,
6394 new2OldFaces, new2OldNodes ));
6395 if ( offsetMesh->NbElements() == 0 )
6396 return PGroupIDs(); // MakeOffset() failed
6399 if ( theTgtMesh == myMesh && !theCopyElements )
6401 // clear the source elements
6402 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6403 else eIt = SMESHUtils::elemSetIterator( theElements );
6404 while ( eIt->more() )
6405 meshDS->RemoveFreeElement( eIt->next(), 0 );
6408 // offsetMesh->Modified();
6409 // offsetMesh->CompactMesh(); // make IDs start from 1
6411 // source elements for each generated one
6412 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6413 srcElems.reserve( new2OldFaces.size() );
6414 srcNodes.reserve( new2OldNodes.size() );
6417 myLastCreatedElems.reserve( new2OldFaces.size() );
6418 myLastCreatedNodes.reserve( new2OldNodes.size() );
6420 // copy offsetMesh to theTgtMesh
6422 int idShift = meshDS->MaxNodeID();
6423 for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6424 if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6427 if ( n->NbInverseElements() > 0 )
6430 const SMDS_MeshNode* n2 =
6431 tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6432 myLastCreatedNodes.push_back( n2 );
6433 srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6437 ElemFeatures elemType;
6438 for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6439 if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6442 elemType.myNodes.clear();
6443 for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6445 const SMDS_MeshNode* n2 = nIt->next();
6446 elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6448 tgtEditor.AddElement( elemType.myNodes, elemType );
6449 srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6452 myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6454 PGroupIDs newGroupIDs;
6455 if ( theMakeGroups )
6456 newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6461 //=======================================================================
6463 * \brief Create groups of elements made during transformation
6464 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6465 * \param elemGens - elements making corresponding myLastCreatedElems
6466 * \param postfix - to push_back to names of new groups
6467 * \param targetMesh - mesh to create groups in
6468 * \param topPresent - is there are "top" elements that are created by sweeping
6470 //=======================================================================
6472 SMESH_MeshEditor::PGroupIDs
6473 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6474 const SMESH_SequenceOfElemPtr& elemGens,
6475 const std::string& postfix,
6476 SMESH_Mesh* targetMesh,
6477 const bool topPresent)
6479 PGroupIDs newGroupIDs( new list<int> );
6480 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6482 // Sort existing groups by types and collect their names
6484 // containers to store an old group and generated new ones;
6485 // 1st new group is for result elems of different type than a source one;
6486 // 2nd new group is for same type result elems ("top" group at extrusion)
6488 using boost::make_tuple;
6489 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6490 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6491 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6493 set< string > groupNames;
6495 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6496 if ( !groupIt->more() ) return newGroupIDs;
6498 int newGroupID = mesh->GetGroupIds().back()+1;
6499 while ( groupIt->more() )
6501 SMESH_Group * group = groupIt->next();
6502 if ( !group ) continue;
6503 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6504 if ( !groupDS || groupDS->IsEmpty() ) continue;
6505 groupNames.insert ( group->GetName() );
6506 groupDS->SetStoreName( group->GetName() );
6507 const SMDSAbs_ElementType type = groupDS->GetType();
6508 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6509 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6510 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6511 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6514 // Loop on nodes and elements to add them in new groups
6516 vector< const SMDS_MeshElement* > resultElems;
6517 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6519 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6520 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6521 if ( gens.size() != elems.size() )
6522 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6524 // loop on created elements
6525 for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6527 const SMDS_MeshElement* sourceElem = gens[ iElem ];
6528 if ( !sourceElem ) {
6529 MESSAGE("generateGroups(): NULL source element");
6532 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6533 if ( groupsOldNew.empty() ) { // no groups of this type at all
6534 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6535 ++iElem; // skip all elements made by sourceElem
6538 // collect all elements made by the iElem-th sourceElem
6539 resultElems.clear();
6540 if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6541 if ( resElem != sourceElem )
6542 resultElems.push_back( resElem );
6543 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6544 if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6545 if ( resElem != sourceElem )
6546 resultElems.push_back( resElem );
6548 const SMDS_MeshElement* topElem = 0;
6549 if ( isNodes ) // there must be a top element
6551 topElem = resultElems.back();
6552 resultElems.pop_back();
6556 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6557 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6558 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6560 topElem = *resElemIt;
6561 *resElemIt = 0; // erase *resElemIt
6565 // add resultElems to groups originted from ones the sourceElem belongs to
6566 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6567 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6569 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6570 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6572 // fill in a new group
6573 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6574 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6575 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6577 newGroup.Add( *resElemIt );
6579 // fill a "top" group
6582 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6583 newTopGroup.Add( topElem );
6587 } // loop on created elements
6588 }// loop on nodes and elements
6590 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6592 list<int> topGrouIds;
6593 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6595 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6596 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6597 orderedOldNewGroups[i]->get<2>() };
6598 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6600 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6601 if ( newGroupDS->IsEmpty() )
6603 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6608 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6611 const bool isTop = ( topPresent &&
6612 newGroupDS->GetType() == oldGroupDS->GetType() &&
6615 string name = oldGroupDS->GetStoreName();
6616 { // remove trailing whitespaces (issue 22599)
6617 size_t size = name.size();
6618 while ( size > 1 && isspace( name[ size-1 ]))
6620 if ( size != name.size() )
6622 name.resize( size );
6623 oldGroupDS->SetStoreName( name.c_str() );
6626 if ( !targetMesh ) {
6627 string suffix = ( isTop ? "top": postfix.c_str() );
6631 while ( !groupNames.insert( name ).second ) // name exists
6632 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6637 newGroupDS->SetStoreName( name.c_str() );
6639 // make a SMESH_Groups
6640 mesh->AddGroup( newGroupDS );
6642 topGrouIds.push_back( newGroupDS->GetID() );
6644 newGroupIDs->push_back( newGroupDS->GetID() );
6648 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6653 //================================================================================
6655 * * \brief Return list of group of nodes close to each other within theTolerance
6656 * * Search among theNodes or in the whole mesh if theNodes is empty using
6657 * * an Octree algorithm
6658 * \param [in,out] theNodes - the nodes to treat
6659 * \param [in] theTolerance - the tolerance
6660 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
6661 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
6662 * corner and medium nodes in separate groups
6664 //================================================================================
6666 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6667 const double theTolerance,
6668 TListOfListOfNodes & theGroupsOfNodes,
6669 bool theSeparateCornersAndMedium)
6673 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
6674 myMesh->NbFaces ( ORDER_QUADRATIC ) +
6675 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
6676 theSeparateCornersAndMedium = false;
6678 TIDSortedNodeSet& corners = theNodes;
6679 TIDSortedNodeSet medium;
6681 if ( theNodes.empty() ) // get all nodes in the mesh
6683 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
6684 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
6685 if ( theSeparateCornersAndMedium )
6686 while ( nIt->more() )
6688 const SMDS_MeshNode* n = nIt->next();
6689 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
6690 nodeSet->insert( nodeSet->end(), n );
6693 while ( nIt->more() )
6694 theNodes.insert( theNodes.end(), nIt->next() );
6696 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
6698 TIDSortedNodeSet::iterator nIt = corners.begin();
6699 while ( nIt != corners.end() )
6700 if ( SMESH_MesherHelper::IsMedium( *nIt ))
6702 medium.insert( medium.end(), *nIt );
6703 corners.erase( nIt++ );
6711 if ( !corners.empty() )
6712 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
6713 if ( !medium.empty() )
6714 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
6717 //=======================================================================
6718 //function : SimplifyFace
6719 //purpose : split a chain of nodes into several closed chains
6720 //=======================================================================
6722 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6723 vector<const SMDS_MeshNode *>& poly_nodes,
6724 vector<int>& quantities) const
6726 int nbNodes = faceNodes.size();
6727 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
6731 size_t prevNbQuant = quantities.size();
6733 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
6734 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
6735 map< const SMDS_MeshNode*, int >::iterator nInd;
6737 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
6738 simpleNodes.push_back( faceNodes[0] );
6739 for ( int iCur = 1; iCur < nbNodes; iCur++ )
6741 if ( faceNodes[ iCur ] != simpleNodes.back() )
6743 int index = simpleNodes.size();
6744 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
6745 int prevIndex = nInd->second;
6746 if ( prevIndex < index )
6749 int loopLen = index - prevIndex;
6752 // store the sub-loop
6753 quantities.push_back( loopLen );
6754 for ( int i = prevIndex; i < index; i++ )
6755 poly_nodes.push_back( simpleNodes[ i ]);
6757 simpleNodes.resize( prevIndex+1 );
6761 simpleNodes.push_back( faceNodes[ iCur ]);
6766 if ( simpleNodes.size() > 2 )
6768 quantities.push_back( simpleNodes.size() );
6769 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
6772 return quantities.size() - prevNbQuant;
6775 //=======================================================================
6776 //function : MergeNodes
6777 //purpose : In each group, the cdr of nodes are substituted by the first one
6779 //=======================================================================
6781 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
6782 const bool theAvoidMakingHoles)
6786 SMESHDS_Mesh* mesh = GetMeshDS();
6788 TNodeNodeMap nodeNodeMap; // node to replace - new node
6789 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6790 list< int > rmElemIds, rmNodeIds;
6791 vector< ElemFeatures > newElemDefs;
6793 // Fill nodeNodeMap and elems
6795 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6796 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
6798 list<const SMDS_MeshNode*>& nodes = *grIt;
6799 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6800 const SMDS_MeshNode* nToKeep = *nIt;
6801 for ( ++nIt; nIt != nodes.end(); nIt++ )
6803 const SMDS_MeshNode* nToRemove = *nIt;
6804 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
6805 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6806 while ( invElemIt->more() ) {
6807 const SMDS_MeshElement* elem = invElemIt->next();
6813 // Apply recursive replacements (BUG 0020185)
6814 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
6815 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
6817 const SMDS_MeshNode* nToKeep = nnIt->second;
6818 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
6819 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
6821 nToKeep = nnIt_i->second;
6822 nnIt->second = nToKeep;
6823 nnIt_i = nodeNodeMap.find( nToKeep );
6827 if ( theAvoidMakingHoles )
6829 // find elements whose topology changes
6831 vector<const SMDS_MeshElement*> pbElems;
6832 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6833 for ( ; eIt != elems.end(); ++eIt )
6835 const SMDS_MeshElement* elem = *eIt;
6836 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6837 while ( itN->more() )
6839 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6840 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6841 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
6843 // several nodes of elem stick
6844 pbElems.push_back( elem );
6849 // exclude from merge nodes causing spoiling element
6850 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
6852 bool nodesExcluded = false;
6853 for ( size_t i = 0; i < pbElems.size(); ++i )
6855 size_t prevNbMergeNodes = nodeNodeMap.size();
6856 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
6857 prevNbMergeNodes < nodeNodeMap.size() )
6858 nodesExcluded = true;
6860 if ( !nodesExcluded )
6865 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
6867 const SMDS_MeshNode* nToRemove = nnIt->first;
6868 const SMDS_MeshNode* nToKeep = nnIt->second;
6869 if ( nToRemove != nToKeep )
6871 rmNodeIds.push_back( nToRemove->GetID() );
6872 AddToSameGroups( nToKeep, nToRemove, mesh );
6873 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
6874 // w/o creating node in place of merged ones.
6875 SMDS_PositionPtr pos = nToRemove->GetPosition();
6876 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6877 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6878 sm->SetIsAlwaysComputed( true );
6882 // Change element nodes or remove an element
6884 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6885 for ( ; eIt != elems.end(); eIt++ )
6887 const SMDS_MeshElement* elem = *eIt;
6888 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
6890 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
6892 rmElemIds.push_back( elem->GetID() );
6894 for ( size_t i = 0; i < newElemDefs.size(); ++i )
6896 if ( i > 0 || !mesh->ChangeElementNodes( elem,
6897 & newElemDefs[i].myNodes[0],
6898 newElemDefs[i].myNodes.size() ))
6902 newElemDefs[i].SetID( elem->GetID() );
6903 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6904 if ( !keepElem ) rmElemIds.pop_back();
6908 newElemDefs[i].SetID( -1 );
6910 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
6911 if ( sm && newElem )
6912 sm->AddElement( newElem );
6913 if ( elem != newElem )
6914 ReplaceElemInGroups( elem, newElem, mesh );
6919 // Remove bad elements, then equal nodes (order important)
6920 Remove( rmElemIds, /*isNodes=*/false );
6921 Remove( rmNodeIds, /*isNodes=*/true );
6926 //=======================================================================
6927 //function : applyMerge
6928 //purpose : Compute new connectivity of an element after merging nodes
6929 // \param [in] elems - the element
6930 // \param [out] newElemDefs - definition(s) of result element(s)
6931 // \param [inout] nodeNodeMap - nodes to merge
6932 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
6933 // after merging (but not degenerated), removes nodes causing
6934 // the invalidity from \a nodeNodeMap.
6935 // \return bool - true if the element should be removed
6936 //=======================================================================
6938 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
6939 vector< ElemFeatures >& newElemDefs,
6940 TNodeNodeMap& nodeNodeMap,
6941 const bool avoidMakingHoles )
6943 bool toRemove = false; // to remove elem
6944 int nbResElems = 1; // nb new elements
6946 newElemDefs.resize(nbResElems);
6947 newElemDefs[0].Init( elem );
6948 newElemDefs[0].myNodes.clear();
6950 set<const SMDS_MeshNode*> nodeSet;
6951 vector< const SMDS_MeshNode*> curNodes;
6952 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
6955 const int nbNodes = elem->NbNodes();
6956 SMDSAbs_EntityType entity = elem->GetEntityType();
6958 curNodes.resize( nbNodes );
6959 uniqueNodes.resize( nbNodes );
6960 iRepl.resize( nbNodes );
6961 int iUnique = 0, iCur = 0, nbRepl = 0;
6963 // Get new seq of nodes
6965 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6966 while ( itN->more() )
6968 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6970 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6971 if ( nnIt != nodeNodeMap.end() ) {
6974 curNodes[ iCur ] = n;
6975 bool isUnique = nodeSet.insert( n ).second;
6977 uniqueNodes[ iUnique++ ] = n;
6979 iRepl[ nbRepl++ ] = iCur;
6983 // Analyse element topology after replacement
6985 int nbUniqueNodes = nodeSet.size();
6986 if ( nbNodes != nbUniqueNodes ) // some nodes stick
6991 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
6993 // if corner nodes stick, remove medium nodes between them from uniqueNodes
6994 int nbCorners = nbNodes / 2;
6995 for ( int iCur = 0; iCur < nbCorners; ++iCur )
6997 int iNext = ( iCur + 1 ) % nbCorners;
6998 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7000 int iMedium = iCur + nbCorners;
7001 vector< const SMDS_MeshNode* >::iterator i =
7002 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7004 curNodes[ iMedium ]);
7005 if ( i != uniqueNodes.end() )
7008 for ( ; i+1 != uniqueNodes.end(); ++i )
7017 case SMDSEntity_Polygon:
7018 case SMDSEntity_Quad_Polygon: // Polygon
7020 ElemFeatures* elemType = & newElemDefs[0];
7021 const bool isQuad = elemType->myIsQuad;
7023 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7024 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7026 // a polygon can divide into several elements
7027 vector<const SMDS_MeshNode *> polygons_nodes;
7028 vector<int> quantities;
7029 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7030 newElemDefs.resize( nbResElems );
7031 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7033 ElemFeatures* elemType = & newElemDefs[iface];
7034 if ( iface ) elemType->Init( elem );
7036 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7037 int nbNewNodes = quantities[iface];
7038 face_nodes.assign( polygons_nodes.begin() + inode,
7039 polygons_nodes.begin() + inode + nbNewNodes );
7040 inode += nbNewNodes;
7041 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7043 bool isValid = ( nbNewNodes % 2 == 0 );
7044 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7045 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7046 elemType->SetQuad( isValid );
7047 if ( isValid ) // put medium nodes after corners
7048 SMDS_MeshCell::applyInterlaceRev
7049 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7050 nbNewNodes ), face_nodes );
7052 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7054 nbUniqueNodes = newElemDefs[0].myNodes.size();
7058 case SMDSEntity_Polyhedra: // Polyhedral volume
7060 if ( nbUniqueNodes >= 4 )
7062 // each face has to be analyzed in order to check volume validity
7063 if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7065 int nbFaces = aPolyedre->NbFaces();
7067 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7068 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7069 vector<const SMDS_MeshNode *> faceNodes;
7073 for (int iface = 1; iface <= nbFaces; iface++)
7075 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7076 faceNodes.resize( nbFaceNodes );
7077 for (int inode = 1; inode <= nbFaceNodes; inode++)
7079 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7080 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7081 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7082 faceNode = (*nnIt).second;
7083 faceNodes[inode - 1] = faceNode;
7085 SimplifyFace(faceNodes, poly_nodes, quantities);
7088 if ( quantities.size() > 3 )
7090 // TODO: remove coincident faces
7092 nbUniqueNodes = newElemDefs[0].myNodes.size();
7100 // TODO not all the possible cases are solved. Find something more generic?
7101 case SMDSEntity_Edge: //////// EDGE
7102 case SMDSEntity_Triangle: //// TRIANGLE
7103 case SMDSEntity_Quad_Triangle:
7104 case SMDSEntity_Tetra:
7105 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7109 case SMDSEntity_Quad_Edge:
7113 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7115 if ( nbUniqueNodes < 3 )
7117 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7118 toRemove = true; // opposite nodes stick
7123 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7132 if ( nbUniqueNodes == 6 &&
7134 ( nbRepl == 1 || iRepl[1] >= 4 ))
7140 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7149 if ( nbUniqueNodes == 7 &&
7151 ( nbRepl == 1 || iRepl[1] != 8 ))
7157 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7159 if ( nbUniqueNodes == 4 ) {
7160 // ---------------------------------> tetrahedron
7161 if ( curNodes[3] == curNodes[4] &&
7162 curNodes[3] == curNodes[5] ) {
7166 else if ( curNodes[0] == curNodes[1] &&
7167 curNodes[0] == curNodes[2] ) {
7168 // bottom nodes stick: set a top before
7169 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7170 uniqueNodes[ 0 ] = curNodes [ 5 ];
7171 uniqueNodes[ 1 ] = curNodes [ 4 ];
7172 uniqueNodes[ 2 ] = curNodes [ 3 ];
7175 else if (( curNodes[0] == curNodes[3] ) +
7176 ( curNodes[1] == curNodes[4] ) +
7177 ( curNodes[2] == curNodes[5] ) == 2 ) {
7178 // a lateral face turns into a line
7182 else if ( nbUniqueNodes == 5 ) {
7183 // PENTAHEDRON --------------------> pyramid
7184 if ( curNodes[0] == curNodes[3] )
7186 uniqueNodes[ 0 ] = curNodes[ 1 ];
7187 uniqueNodes[ 1 ] = curNodes[ 4 ];
7188 uniqueNodes[ 2 ] = curNodes[ 5 ];
7189 uniqueNodes[ 3 ] = curNodes[ 2 ];
7190 uniqueNodes[ 4 ] = curNodes[ 0 ];
7193 if ( curNodes[1] == curNodes[4] )
7195 uniqueNodes[ 0 ] = curNodes[ 0 ];
7196 uniqueNodes[ 1 ] = curNodes[ 2 ];
7197 uniqueNodes[ 2 ] = curNodes[ 5 ];
7198 uniqueNodes[ 3 ] = curNodes[ 3 ];
7199 uniqueNodes[ 4 ] = curNodes[ 1 ];
7202 if ( curNodes[2] == curNodes[5] )
7204 uniqueNodes[ 0 ] = curNodes[ 0 ];
7205 uniqueNodes[ 1 ] = curNodes[ 3 ];
7206 uniqueNodes[ 2 ] = curNodes[ 4 ];
7207 uniqueNodes[ 3 ] = curNodes[ 1 ];
7208 uniqueNodes[ 4 ] = curNodes[ 2 ];
7214 case SMDSEntity_Hexa:
7216 //////////////////////////////////// HEXAHEDRON
7217 SMDS_VolumeTool hexa (elem);
7218 hexa.SetExternalNormal();
7219 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7220 //////////////////////// HEX ---> tetrahedron
7221 for ( int iFace = 0; iFace < 6; iFace++ ) {
7222 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7223 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7224 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7225 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7226 // one face turns into a point ...
7227 int pickInd = ind[ 0 ];
7228 int iOppFace = hexa.GetOppFaceIndex( iFace );
7229 ind = hexa.GetFaceNodesIndices( iOppFace );
7231 uniqueNodes.clear();
7232 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7233 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7236 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7238 if ( nbStick == 1 ) {
7239 // ... and the opposite one - into a triangle.
7241 uniqueNodes.push_back( curNodes[ pickInd ]);
7248 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7249 //////////////////////// HEX ---> prism
7250 int nbTria = 0, iTria[3];
7251 const int *ind; // indices of face nodes
7252 // look for triangular faces
7253 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7254 ind = hexa.GetFaceNodesIndices( iFace );
7255 TIDSortedNodeSet faceNodes;
7256 for ( iCur = 0; iCur < 4; iCur++ )
7257 faceNodes.insert( curNodes[ind[iCur]] );
7258 if ( faceNodes.size() == 3 )
7259 iTria[ nbTria++ ] = iFace;
7261 // check if triangles are opposite
7262 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7264 // set nodes of the bottom triangle
7265 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7267 for ( iCur = 0; iCur < 4; iCur++ )
7268 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7269 indB.push_back( ind[iCur] );
7270 if ( !hexa.IsForward() )
7271 std::swap( indB[0], indB[2] );
7272 for ( iCur = 0; iCur < 3; iCur++ )
7273 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7274 // set nodes of the top triangle
7275 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7276 for ( iCur = 0; iCur < 3; ++iCur )
7277 for ( int j = 0; j < 4; ++j )
7278 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7280 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7287 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7288 //////////////////// HEXAHEDRON ---> pyramid
7289 for ( int iFace = 0; iFace < 6; iFace++ ) {
7290 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7291 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7292 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7293 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7294 // one face turns into a point ...
7295 int iOppFace = hexa.GetOppFaceIndex( iFace );
7296 ind = hexa.GetFaceNodesIndices( iOppFace );
7297 uniqueNodes.clear();
7298 for ( iCur = 0; iCur < 4; iCur++ ) {
7299 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7302 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7304 if ( uniqueNodes.size() == 4 ) {
7305 // ... and the opposite one is a quadrangle
7307 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7308 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7316 if ( toRemove && nbUniqueNodes > 4 ) {
7317 ////////////////// HEXAHEDRON ---> polyhedron
7318 hexa.SetExternalNormal();
7319 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7320 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7321 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7322 quantities.reserve( 6 ); quantities.clear();
7323 for ( int iFace = 0; iFace < 6; iFace++ )
7325 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7326 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7327 curNodes[ind[1]] == curNodes[ind[3]] )
7330 break; // opposite nodes stick
7333 for ( iCur = 0; iCur < 4; iCur++ )
7335 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7336 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7338 if ( nodeSet.size() < 3 )
7339 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7341 quantities.push_back( nodeSet.size() );
7343 if ( quantities.size() >= 4 )
7346 nbUniqueNodes = poly_nodes.size();
7347 newElemDefs[0].SetPoly(true);
7351 } // case HEXAHEDRON
7356 } // switch ( entity )
7358 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7360 // erase from nodeNodeMap nodes whose merge spoils elem
7361 vector< const SMDS_MeshNode* > noMergeNodes;
7362 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7363 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7364 nodeNodeMap.erase( noMergeNodes[i] );
7367 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7369 uniqueNodes.resize( nbUniqueNodes );
7371 if ( !toRemove && nbResElems == 0 )
7374 newElemDefs.resize( nbResElems );
7380 // ========================================================
7381 // class : ComparableElement
7382 // purpose : allow comparing elements basing on their nodes
7383 // ========================================================
7385 class ComparableElement : public boost::container::flat_set< int >
7387 typedef boost::container::flat_set< int > int_set;
7389 const SMDS_MeshElement* myElem;
7391 mutable int myGroupID;
7395 ComparableElement( const SMDS_MeshElement* theElem ):
7396 myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7398 this->reserve( theElem->NbNodes() );
7399 for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7401 int id = nodeIt->next()->GetID();
7407 const SMDS_MeshElement* GetElem() const { return myElem; }
7409 int& GroupID() const { return myGroupID; }
7410 //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7412 ComparableElement( const ComparableElement& theSource ) // move copy
7414 ComparableElement& src = const_cast< ComparableElement& >( theSource );
7415 (int_set&) (*this ) = boost::move( src );
7416 myElem = src.myElem;
7417 mySumID = src.mySumID;
7418 myGroupID = src.myGroupID;
7421 static int HashCode(const ComparableElement& se, int limit )
7423 return ::HashCode( se.mySumID, limit );
7425 static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7427 return ( se1 == se2 );
7432 //=======================================================================
7433 //function : FindEqualElements
7434 //purpose : Return list of group of elements built on the same nodes.
7435 // Search among theElements or in the whole mesh if theElements is empty
7436 //=======================================================================
7438 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet & theElements,
7439 TListOfListOfElementsID & theGroupsOfElementsID )
7443 SMDS_ElemIteratorPtr elemIt;
7444 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7445 else elemIt = SMESHUtils::elemSetIterator( theElements );
7447 typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7448 typedef std::list<int> TGroupOfElems;
7449 TMapOfElements mapOfElements;
7450 std::vector< TGroupOfElems > arrayOfGroups;
7451 TGroupOfElems groupOfElems;
7453 while ( elemIt->more() )
7455 const SMDS_MeshElement* curElem = elemIt->next();
7456 ComparableElement compElem = curElem;
7458 const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7459 if ( elemInSet.GetElem() != curElem ) // coincident elem
7461 int& iG = elemInSet.GroupID();
7464 iG = arrayOfGroups.size();
7465 arrayOfGroups.push_back( groupOfElems );
7466 arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7468 arrayOfGroups[ iG ].push_back( curElem->GetID() );
7472 groupOfElems.clear();
7473 std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7474 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7476 if ( groupIt->size() > 1 ) {
7477 //groupOfElems.sort(); -- theElements are sorted already
7478 theGroupsOfElementsID.emplace_back( *groupIt );
7483 //=======================================================================
7484 //function : MergeElements
7485 //purpose : In each given group, substitute all elements by the first one.
7486 //=======================================================================
7488 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7492 typedef list<int> TListOfIDs;
7493 TListOfIDs rmElemIds; // IDs of elems to remove
7495 SMESHDS_Mesh* aMesh = GetMeshDS();
7497 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7498 while ( groupsIt != theGroupsOfElementsID.end() ) {
7499 TListOfIDs& aGroupOfElemID = *groupsIt;
7500 aGroupOfElemID.sort();
7501 int elemIDToKeep = aGroupOfElemID.front();
7502 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7503 aGroupOfElemID.pop_front();
7504 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7505 while ( idIt != aGroupOfElemID.end() ) {
7506 int elemIDToRemove = *idIt;
7507 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7508 // add the kept element in groups of removed one (PAL15188)
7509 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7510 rmElemIds.push_back( elemIDToRemove );
7516 Remove( rmElemIds, false );
7519 //=======================================================================
7520 //function : MergeEqualElements
7521 //purpose : Remove all but one of elements built on the same nodes.
7522 //=======================================================================
7524 void SMESH_MeshEditor::MergeEqualElements()
7526 TIDSortedElemSet aMeshElements; /* empty input ==
7527 to merge equal elements in the whole mesh */
7528 TListOfListOfElementsID aGroupsOfElementsID;
7529 FindEqualElements( aMeshElements, aGroupsOfElementsID );
7530 MergeElements( aGroupsOfElementsID );
7533 //=======================================================================
7534 //function : findAdjacentFace
7536 //=======================================================================
7538 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7539 const SMDS_MeshNode* n2,
7540 const SMDS_MeshElement* elem)
7542 TIDSortedElemSet elemSet, avoidSet;
7544 avoidSet.insert ( elem );
7545 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7548 //=======================================================================
7549 //function : findSegment
7550 //purpose : Return a mesh segment by two nodes one of which can be medium
7551 //=======================================================================
7553 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7554 const SMDS_MeshNode* n2)
7556 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7557 while ( it->more() )
7559 const SMDS_MeshElement* seg = it->next();
7560 if ( seg->GetNodeIndex( n2 ) >= 0 )
7566 //=======================================================================
7567 //function : FindFreeBorder
7569 //=======================================================================
7571 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7573 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7574 const SMDS_MeshNode* theSecondNode,
7575 const SMDS_MeshNode* theLastNode,
7576 list< const SMDS_MeshNode* > & theNodes,
7577 list< const SMDS_MeshElement* >& theFaces)
7579 if ( !theFirstNode || !theSecondNode )
7581 // find border face between theFirstNode and theSecondNode
7582 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7586 theFaces.push_back( curElem );
7587 theNodes.push_back( theFirstNode );
7588 theNodes.push_back( theSecondNode );
7590 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7591 TIDSortedElemSet foundElems;
7592 bool needTheLast = ( theLastNode != 0 );
7594 while ( nStart != theLastNode ) {
7595 if ( nStart == theFirstNode )
7596 return !needTheLast;
7598 // find all free border faces sharing form nStart
7600 list< const SMDS_MeshElement* > curElemList;
7601 list< const SMDS_MeshNode* > nStartList;
7602 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7603 while ( invElemIt->more() ) {
7604 const SMDS_MeshElement* e = invElemIt->next();
7605 if ( e == curElem || foundElems.insert( e ).second ) {
7607 int iNode = 0, nbNodes = e->NbNodes();
7608 vector<const SMDS_MeshNode*> nodes( nbNodes+1 );
7609 nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7610 SMDS_MeshElement::iterator() );
7611 nodes.push_back( nodes[ 0 ]);
7614 for ( iNode = 0; iNode < nbNodes; iNode++ )
7615 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7616 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7617 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7619 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7620 curElemList.push_back( e );
7624 // analyse the found
7626 int nbNewBorders = curElemList.size();
7627 if ( nbNewBorders == 0 ) {
7628 // no free border furthermore
7629 return !needTheLast;
7631 else if ( nbNewBorders == 1 ) {
7632 // one more element found
7634 nStart = nStartList.front();
7635 curElem = curElemList.front();
7636 theFaces.push_back( curElem );
7637 theNodes.push_back( nStart );
7640 // several continuations found
7641 list< const SMDS_MeshElement* >::iterator curElemIt;
7642 list< const SMDS_MeshNode* >::iterator nStartIt;
7643 // check if one of them reached the last node
7644 if ( needTheLast ) {
7645 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7646 curElemIt!= curElemList.end();
7647 curElemIt++, nStartIt++ )
7648 if ( *nStartIt == theLastNode ) {
7649 theFaces.push_back( *curElemIt );
7650 theNodes.push_back( *nStartIt );
7654 // find the best free border by the continuations
7655 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7656 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7657 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7658 curElemIt!= curElemList.end();
7659 curElemIt++, nStartIt++ )
7661 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7662 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7663 // find one more free border
7664 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7668 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7669 // choice: clear a worse one
7670 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7671 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7672 contNodes[ iWorse ].clear();
7673 contFaces[ iWorse ].clear();
7676 if ( contNodes[0].empty() && contNodes[1].empty() )
7679 // push_back the best free border
7680 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7681 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7682 theNodes.pop_back(); // remove nIgnore
7683 theNodes.pop_back(); // remove nStart
7684 theFaces.pop_back(); // remove curElem
7685 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
7686 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
7687 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
7688 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
7691 } // several continuations found
7692 } // while ( nStart != theLastNode )
7697 //=======================================================================
7698 //function : CheckFreeBorderNodes
7699 //purpose : Return true if the tree nodes are on a free border
7700 //=======================================================================
7702 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7703 const SMDS_MeshNode* theNode2,
7704 const SMDS_MeshNode* theNode3)
7706 list< const SMDS_MeshNode* > nodes;
7707 list< const SMDS_MeshElement* > faces;
7708 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7711 //=======================================================================
7712 //function : SewFreeBorder
7714 //warning : for border-to-side sewing theSideSecondNode is considered as
7715 // the last side node and theSideThirdNode is not used
7716 //=======================================================================
7718 SMESH_MeshEditor::Sew_Error
7719 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7720 const SMDS_MeshNode* theBordSecondNode,
7721 const SMDS_MeshNode* theBordLastNode,
7722 const SMDS_MeshNode* theSideFirstNode,
7723 const SMDS_MeshNode* theSideSecondNode,
7724 const SMDS_MeshNode* theSideThirdNode,
7725 const bool theSideIsFreeBorder,
7726 const bool toCreatePolygons,
7727 const bool toCreatePolyedrs)
7731 Sew_Error aResult = SEW_OK;
7733 // ====================================
7734 // find side nodes and elements
7735 // ====================================
7737 list< const SMDS_MeshNode* > nSide[ 2 ];
7738 list< const SMDS_MeshElement* > eSide[ 2 ];
7739 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7740 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7744 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7745 nSide[0], eSide[0])) {
7746 MESSAGE(" Free Border 1 not found " );
7747 aResult = SEW_BORDER1_NOT_FOUND;
7749 if (theSideIsFreeBorder) {
7752 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7753 nSide[1], eSide[1])) {
7754 MESSAGE(" Free Border 2 not found " );
7755 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7758 if ( aResult != SEW_OK )
7761 if (!theSideIsFreeBorder) {
7765 // -------------------------------------------------------------------------
7767 // 1. If nodes to merge are not coincident, move nodes of the free border
7768 // from the coord sys defined by the direction from the first to last
7769 // nodes of the border to the correspondent sys of the side 2
7770 // 2. On the side 2, find the links most co-directed with the correspondent
7771 // links of the free border
7772 // -------------------------------------------------------------------------
7774 // 1. Since sewing may break if there are volumes to split on the side 2,
7775 // we won't move nodes but just compute new coordinates for them
7776 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7777 TNodeXYZMap nBordXYZ;
7778 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7779 list< const SMDS_MeshNode* >::iterator nBordIt;
7781 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7782 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7783 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7784 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7785 double tol2 = 1.e-8;
7786 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7787 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7788 // Need node movement.
7790 // find X and Z axes to create trsf
7791 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7793 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7795 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7798 gp_Ax3 toBordAx( Pb1, Zb, X );
7799 gp_Ax3 fromSideAx( Ps1, Zs, X );
7800 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7802 gp_Trsf toBordSys, fromSide2Sys;
7803 toBordSys.SetTransformation( toBordAx );
7804 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7805 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7808 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7809 const SMDS_MeshNode* n = *nBordIt;
7810 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7811 toBordSys.Transforms( xyz );
7812 fromSide2Sys.Transforms( xyz );
7813 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7817 // just insert nodes XYZ in the nBordXYZ map
7818 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7819 const SMDS_MeshNode* n = *nBordIt;
7820 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7824 // 2. On the side 2, find the links most co-directed with the correspondent
7825 // links of the free border
7827 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7828 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7829 sideNodes.push_back( theSideFirstNode );
7831 bool hasVolumes = false;
7832 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7833 set<long> foundSideLinkIDs, checkedLinkIDs;
7834 SMDS_VolumeTool volume;
7835 //const SMDS_MeshNode* faceNodes[ 4 ];
7837 const SMDS_MeshNode* sideNode;
7838 const SMDS_MeshElement* sideElem = 0;
7839 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7840 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7841 nBordIt = bordNodes.begin();
7843 // border node position and border link direction to compare with
7844 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7845 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7846 // choose next side node by link direction or by closeness to
7847 // the current border node:
7848 bool searchByDir = ( *nBordIt != theBordLastNode );
7850 // find the next node on the Side 2
7852 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7854 checkedLinkIDs.clear();
7855 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7857 // loop on inverse elements of current node (prevSideNode) on the Side 2
7858 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7859 while ( invElemIt->more() )
7861 const SMDS_MeshElement* elem = invElemIt->next();
7862 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7863 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7864 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7865 bool isVolume = volume.Set( elem );
7866 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7867 if ( isVolume ) // --volume
7869 else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7870 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7871 SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
7872 while ( nIt->more() ) {
7873 nodes[ iNode ] = cast2Node( nIt->next() );
7874 if ( nodes[ iNode++ ] == prevSideNode )
7875 iPrevNode = iNode - 1;
7877 // there are 2 links to check
7882 // loop on links, to be precise, on the second node of links
7883 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7884 const SMDS_MeshNode* n = nodes[ iNode ];
7886 if ( !volume.IsLinked( n, prevSideNode ))
7890 if ( iNode ) // a node before prevSideNode
7891 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7892 else // a node after prevSideNode
7893 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7895 // check if this link was already used
7896 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7897 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7898 if (!isJustChecked &&
7899 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7901 // test a link geometrically
7902 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7903 bool linkIsBetter = false;
7904 double dot = 0.0, dist = 0.0;
7905 if ( searchByDir ) { // choose most co-directed link
7906 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7907 linkIsBetter = ( dot > maxDot );
7909 else { // choose link with the node closest to bordPos
7910 dist = ( nextXYZ - bordPos ).SquareModulus();
7911 linkIsBetter = ( dist < minDist );
7913 if ( linkIsBetter ) {
7922 } // loop on inverse elements of prevSideNode
7925 MESSAGE(" Can't find path by links of the Side 2 ");
7926 return SEW_BAD_SIDE_NODES;
7928 sideNodes.push_back( sideNode );
7929 sideElems.push_back( sideElem );
7930 foundSideLinkIDs.insert ( linkID );
7931 prevSideNode = sideNode;
7933 if ( *nBordIt == theBordLastNode )
7934 searchByDir = false;
7936 // find the next border link to compare with
7937 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7938 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7939 // move to next border node if sideNode is before forward border node (bordPos)
7940 while ( *nBordIt != theBordLastNode && !searchByDir ) {
7941 prevBordNode = *nBordIt;
7943 bordPos = nBordXYZ[ *nBordIt ];
7944 bordDir = bordPos - nBordXYZ[ prevBordNode ];
7945 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7949 while ( sideNode != theSideSecondNode );
7951 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7952 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7953 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7955 } // end nodes search on the side 2
7957 // ============================
7958 // sew the border to the side 2
7959 // ============================
7961 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
7962 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
7964 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
7965 if ( toMergeConformal && toCreatePolygons )
7967 // do not merge quadrangles if polygons are OK (IPAL0052824)
7968 eIt[0] = eSide[0].begin();
7969 eIt[1] = eSide[1].begin();
7970 bool allQuads[2] = { true, true };
7971 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7972 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
7973 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
7975 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
7978 TListOfListOfNodes nodeGroupsToMerge;
7979 if (( toMergeConformal ) ||
7980 ( theSideIsFreeBorder && !theSideThirdNode )) {
7982 // all nodes are to be merged
7984 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
7985 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
7986 nIt[0]++, nIt[1]++ )
7988 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
7989 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
7990 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
7995 // insert new nodes into the border and the side to get equal nb of segments
7997 // get normalized parameters of nodes on the borders
7998 vector< double > param[ 2 ];
7999 param[0].resize( maxNbNodes );
8000 param[1].resize( maxNbNodes );
8002 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8003 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8004 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8005 const SMDS_MeshNode* nPrev = *nIt;
8006 double bordLength = 0;
8007 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8008 const SMDS_MeshNode* nCur = *nIt;
8009 gp_XYZ segment (nCur->X() - nPrev->X(),
8010 nCur->Y() - nPrev->Y(),
8011 nCur->Z() - nPrev->Z());
8012 double segmentLen = segment.Modulus();
8013 bordLength += segmentLen;
8014 param[ iBord ][ iNode ] = bordLength;
8017 // normalize within [0,1]
8018 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8019 param[ iBord ][ iNode ] /= bordLength;
8023 // loop on border segments
8024 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8025 int i[ 2 ] = { 0, 0 };
8026 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8027 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8029 TElemOfNodeListMap insertMap;
8030 TElemOfNodeListMap::iterator insertMapIt;
8032 // key: elem to insert nodes into
8033 // value: 2 nodes to insert between + nodes to be inserted
8035 bool next[ 2 ] = { false, false };
8037 // find min adjacent segment length after sewing
8038 double nextParam = 10., prevParam = 0;
8039 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8040 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8041 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8042 if ( i[ iBord ] > 0 )
8043 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8045 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8046 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8047 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8049 // choose to insert or to merge nodes
8050 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8051 if ( Abs( du ) <= minSegLen * 0.2 ) {
8054 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8055 const SMDS_MeshNode* n0 = *nIt[0];
8056 const SMDS_MeshNode* n1 = *nIt[1];
8057 nodeGroupsToMerge.back().push_back( n1 );
8058 nodeGroupsToMerge.back().push_back( n0 );
8059 // position of node of the border changes due to merge
8060 param[ 0 ][ i[0] ] += du;
8061 // move n1 for the sake of elem shape evaluation during insertion.
8062 // n1 will be removed by MergeNodes() anyway
8063 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8064 next[0] = next[1] = true;
8069 int intoBord = ( du < 0 ) ? 0 : 1;
8070 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8071 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8072 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8073 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8074 if ( intoBord == 1 ) {
8075 // move node of the border to be on a link of elem of the side
8076 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8077 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8078 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8079 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8080 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8082 insertMapIt = insertMap.find( elem );
8083 bool notFound = ( insertMapIt == insertMap.end() );
8084 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8086 // insert into another link of the same element:
8087 // 1. perform insertion into the other link of the elem
8088 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8089 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8090 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8091 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8092 // 2. perform insertion into the link of adjacent faces
8093 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8094 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8096 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8097 InsertNodesIntoLink( seg, n12, n22, nodeList );
8099 if (toCreatePolyedrs) {
8100 // perform insertion into the links of adjacent volumes
8101 UpdateVolumes(n12, n22, nodeList);
8103 // 3. find an element appeared on n1 and n2 after the insertion
8104 insertMap.erase( elem );
8105 elem = findAdjacentFace( n1, n2, 0 );
8107 if ( notFound || otherLink ) {
8108 // add element and nodes of the side into the insertMap
8109 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8110 (*insertMapIt).second.push_back( n1 );
8111 (*insertMapIt).second.push_back( n2 );
8113 // add node to be inserted into elem
8114 (*insertMapIt).second.push_back( nIns );
8115 next[ 1 - intoBord ] = true;
8118 // go to the next segment
8119 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8120 if ( next[ iBord ] ) {
8121 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8123 nPrev[ iBord ] = *nIt[ iBord ];
8124 nIt[ iBord ]++; i[ iBord ]++;
8128 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8130 // perform insertion of nodes into elements
8132 for (insertMapIt = insertMap.begin();
8133 insertMapIt != insertMap.end();
8136 const SMDS_MeshElement* elem = (*insertMapIt).first;
8137 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8138 if ( nodeList.size() < 3 ) continue;
8139 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8140 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8142 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8144 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8145 InsertNodesIntoLink( seg, n1, n2, nodeList );
8148 if ( !theSideIsFreeBorder ) {
8149 // look for and insert nodes into the faces adjacent to elem
8150 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8151 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8154 if (toCreatePolyedrs) {
8155 // perform insertion into the links of adjacent volumes
8156 UpdateVolumes(n1, n2, nodeList);
8159 } // end: insert new nodes
8161 MergeNodes ( nodeGroupsToMerge );
8164 // Remove coincident segments
8167 TIDSortedElemSet segments;
8168 SMESH_SequenceOfElemPtr newFaces;
8169 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8171 if ( !myLastCreatedElems[i] ) continue;
8172 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8173 segments.insert( segments.end(), myLastCreatedElems[i] );
8175 newFaces.push_back( myLastCreatedElems[i] );
8177 // get segments adjacent to merged nodes
8178 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8179 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8181 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8182 if ( nodes.front()->IsNull() ) continue;
8183 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8184 while ( segIt->more() )
8185 segments.insert( segIt->next() );
8189 TListOfListOfElementsID equalGroups;
8190 if ( !segments.empty() )
8191 FindEqualElements( segments, equalGroups );
8192 if ( !equalGroups.empty() )
8194 // remove from segments those that will be removed
8195 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8196 for ( ; itGroups != equalGroups.end(); ++itGroups )
8198 list< int >& group = *itGroups;
8199 list< int >::iterator id = group.begin();
8200 for ( ++id; id != group.end(); ++id )
8201 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8202 segments.erase( seg );
8204 // remove equal segments
8205 MergeElements( equalGroups );
8207 // restore myLastCreatedElems
8208 myLastCreatedElems = newFaces;
8209 TIDSortedElemSet::iterator seg = segments.begin();
8210 for ( ; seg != segments.end(); ++seg )
8211 myLastCreatedElems.push_back( *seg );
8217 //=======================================================================
8218 //function : InsertNodesIntoLink
8219 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8220 // and theBetweenNode2 and split theElement
8221 //=======================================================================
8223 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8224 const SMDS_MeshNode* theBetweenNode1,
8225 const SMDS_MeshNode* theBetweenNode2,
8226 list<const SMDS_MeshNode*>& theNodesToInsert,
8227 const bool toCreatePoly)
8229 if ( !theElement ) return;
8231 SMESHDS_Mesh *aMesh = GetMeshDS();
8232 vector<const SMDS_MeshElement*> newElems;
8234 if ( theElement->GetType() == SMDSAbs_Edge )
8236 theNodesToInsert.push_front( theBetweenNode1 );
8237 theNodesToInsert.push_back ( theBetweenNode2 );
8238 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8239 const SMDS_MeshNode* n1 = *n;
8240 for ( ++n; n != theNodesToInsert.end(); ++n )
8242 const SMDS_MeshNode* n2 = *n;
8243 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8244 AddToSameGroups( seg, theElement, aMesh );
8246 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8249 theNodesToInsert.pop_front();
8250 theNodesToInsert.pop_back();
8252 if ( theElement->IsQuadratic() ) // add a not split part
8254 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8255 theElement->end_nodes() );
8256 int iOther = 0, nbN = nodes.size();
8257 for ( ; iOther < nbN; ++iOther )
8258 if ( nodes[iOther] != theBetweenNode1 &&
8259 nodes[iOther] != theBetweenNode2 )
8263 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8264 AddToSameGroups( seg, theElement, aMesh );
8266 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8268 else if ( iOther == 2 )
8270 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8271 AddToSameGroups( seg, theElement, aMesh );
8273 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8276 // treat new elements
8277 for ( size_t i = 0; i < newElems.size(); ++i )
8280 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8281 myLastCreatedElems.push_back( newElems[i] );
8283 ReplaceElemInGroups( theElement, newElems, aMesh );
8284 aMesh->RemoveElement( theElement );
8287 } // if ( theElement->GetType() == SMDSAbs_Edge )
8289 const SMDS_MeshElement* theFace = theElement;
8290 if ( theFace->GetType() != SMDSAbs_Face ) return;
8292 // find indices of 2 link nodes and of the rest nodes
8293 int iNode = 0, il1, il2, i3, i4;
8294 il1 = il2 = i3 = i4 = -1;
8295 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8297 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8298 while ( nodeIt->more() ) {
8299 const SMDS_MeshNode* n = nodeIt->next();
8300 if ( n == theBetweenNode1 )
8302 else if ( n == theBetweenNode2 )
8308 nodes[ iNode++ ] = n;
8310 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8313 // arrange link nodes to go one after another regarding the face orientation
8314 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8315 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8320 aNodesToInsert.reverse();
8322 // check that not link nodes of a quadrangles are in good order
8323 int nbFaceNodes = theFace->NbNodes();
8324 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8330 if (toCreatePoly || theFace->IsPoly()) {
8333 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8335 // add nodes of face up to first node of link
8337 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8338 while ( nodeIt->more() && !isFLN ) {
8339 const SMDS_MeshNode* n = nodeIt->next();
8340 poly_nodes[iNode++] = n;
8341 isFLN = ( n == nodes[il1] );
8343 // add nodes to insert
8344 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8345 for (; nIt != aNodesToInsert.end(); nIt++) {
8346 poly_nodes[iNode++] = *nIt;
8348 // add nodes of face starting from last node of link
8349 while ( nodeIt->more() ) {
8350 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8351 poly_nodes[iNode++] = n;
8355 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8358 else if ( !theFace->IsQuadratic() )
8360 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8361 int nbLinkNodes = 2 + aNodesToInsert.size();
8362 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8363 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8364 linkNodes[ 0 ] = nodes[ il1 ];
8365 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8366 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8367 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8368 linkNodes[ iNode++ ] = *nIt;
8370 // decide how to split a quadrangle: compare possible variants
8371 // and choose which of splits to be a quadrangle
8372 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8373 if ( nbFaceNodes == 3 ) {
8374 iBestQuad = nbSplits;
8377 else if ( nbFaceNodes == 4 ) {
8378 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8379 double aBestRate = DBL_MAX;
8380 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8382 double aBadRate = 0;
8383 // evaluate elements quality
8384 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8385 if ( iSplit == iQuad ) {
8386 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8390 aBadRate += getBadRate( &quad, aCrit );
8393 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8395 nodes[ iSplit < iQuad ? i4 : i3 ]);
8396 aBadRate += getBadRate( &tria, aCrit );
8400 if ( aBadRate < aBestRate ) {
8402 aBestRate = aBadRate;
8407 // create new elements
8409 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8411 if ( iSplit == iBestQuad )
8412 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8417 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8419 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8422 const SMDS_MeshNode* newNodes[ 4 ];
8423 newNodes[ 0 ] = linkNodes[ i1 ];
8424 newNodes[ 1 ] = linkNodes[ i2 ];
8425 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8426 newNodes[ 3 ] = nodes[ i4 ];
8427 if (iSplit == iBestQuad)
8428 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8430 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8432 } // end if(!theFace->IsQuadratic())
8434 else { // theFace is quadratic
8435 // we have to split theFace on simple triangles and one simple quadrangle
8437 int nbshift = tmp*2;
8438 // shift nodes in nodes[] by nbshift
8440 for(i=0; i<nbshift; i++) {
8441 const SMDS_MeshNode* n = nodes[0];
8442 for(j=0; j<nbFaceNodes-1; j++) {
8443 nodes[j] = nodes[j+1];
8445 nodes[nbFaceNodes-1] = n;
8447 il1 = il1 - nbshift;
8448 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8449 // n0 n1 n2 n0 n1 n2
8450 // +-----+-----+ +-----+-----+
8459 // create new elements
8461 if ( nbFaceNodes == 6 ) { // quadratic triangle
8462 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8463 if ( theFace->IsMediumNode(nodes[il1]) ) {
8464 // create quadrangle
8465 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8471 // create quadrangle
8472 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8478 else { // nbFaceNodes==8 - quadratic quadrangle
8479 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8480 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8481 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8482 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8483 // create quadrangle
8484 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8490 // create quadrangle
8491 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8497 // create needed triangles using n1,n2,n3 and inserted nodes
8498 int nbn = 2 + aNodesToInsert.size();
8499 vector<const SMDS_MeshNode*> aNodes(nbn);
8500 aNodes[0 ] = nodes[n1];
8501 aNodes[nbn-1] = nodes[n2];
8502 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8503 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8504 aNodes[iNode++] = *nIt;
8506 for ( i = 1; i < nbn; i++ )
8507 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8510 // remove the old face
8511 for ( size_t i = 0; i < newElems.size(); ++i )
8514 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8515 myLastCreatedElems.push_back( newElems[i] );
8517 ReplaceElemInGroups( theFace, newElems, aMesh );
8518 aMesh->RemoveElement(theFace);
8520 } // InsertNodesIntoLink()
8522 //=======================================================================
8523 //function : UpdateVolumes
8525 //=======================================================================
8527 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8528 const SMDS_MeshNode* theBetweenNode2,
8529 list<const SMDS_MeshNode*>& theNodesToInsert)
8533 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8534 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8535 const SMDS_MeshElement* elem = invElemIt->next();
8537 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8538 SMDS_VolumeTool aVolume (elem);
8539 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8542 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8543 int iface, nbFaces = aVolume.NbFaces();
8544 vector<const SMDS_MeshNode *> poly_nodes;
8545 vector<int> quantities (nbFaces);
8547 for (iface = 0; iface < nbFaces; iface++) {
8548 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8549 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8550 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8552 for (int inode = 0; inode < nbFaceNodes; inode++) {
8553 poly_nodes.push_back(faceNodes[inode]);
8555 if (nbInserted == 0) {
8556 if (faceNodes[inode] == theBetweenNode1) {
8557 if (faceNodes[inode + 1] == theBetweenNode2) {
8558 nbInserted = theNodesToInsert.size();
8560 // add nodes to insert
8561 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8562 for (; nIt != theNodesToInsert.end(); nIt++) {
8563 poly_nodes.push_back(*nIt);
8567 else if (faceNodes[inode] == theBetweenNode2) {
8568 if (faceNodes[inode + 1] == theBetweenNode1) {
8569 nbInserted = theNodesToInsert.size();
8571 // add nodes to insert in reversed order
8572 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8574 for (; nIt != theNodesToInsert.begin(); nIt--) {
8575 poly_nodes.push_back(*nIt);
8577 poly_nodes.push_back(*nIt);
8584 quantities[iface] = nbFaceNodes + nbInserted;
8587 // Replace the volume
8588 SMESHDS_Mesh *aMesh = GetMeshDS();
8590 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8592 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8593 myLastCreatedElems.push_back( newElem );
8594 ReplaceElemInGroups( elem, newElem, aMesh );
8596 aMesh->RemoveElement( elem );
8602 //================================================================================
8604 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8606 //================================================================================
8608 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8609 vector<const SMDS_MeshNode *> & nodes,
8610 vector<int> & nbNodeInFaces )
8613 nbNodeInFaces.clear();
8614 SMDS_VolumeTool vTool ( elem );
8615 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8617 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8618 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8619 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8624 //=======================================================================
8626 * \brief Convert elements contained in a sub-mesh to quadratic
8627 * \return int - nb of checked elements
8629 //=======================================================================
8631 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8632 SMESH_MesherHelper& theHelper,
8633 const bool theForce3d)
8635 //MESSAGE("convertElemToQuadratic");
8637 if( !theSm ) return nbElem;
8639 vector<int> nbNodeInFaces;
8640 vector<const SMDS_MeshNode *> nodes;
8641 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8642 while(ElemItr->more())
8645 const SMDS_MeshElement* elem = ElemItr->next();
8646 if( !elem ) continue;
8648 // analyse a necessity of conversion
8649 const SMDSAbs_ElementType aType = elem->GetType();
8650 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8652 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8653 bool hasCentralNodes = false;
8654 if ( elem->IsQuadratic() )
8657 switch ( aGeomType ) {
8658 case SMDSEntity_Quad_Triangle:
8659 case SMDSEntity_Quad_Quadrangle:
8660 case SMDSEntity_Quad_Hexa:
8661 case SMDSEntity_Quad_Penta:
8662 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8664 case SMDSEntity_BiQuad_Triangle:
8665 case SMDSEntity_BiQuad_Quadrangle:
8666 case SMDSEntity_TriQuad_Hexa:
8667 case SMDSEntity_BiQuad_Penta:
8668 alreadyOK = theHelper.GetIsBiQuadratic();
8669 hasCentralNodes = true;
8674 // take into account already present medium nodes
8676 case SMDSAbs_Volume:
8677 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8679 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8681 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8687 // get elem data needed to re-create it
8689 const int id = elem->GetID();
8690 const int nbNodes = elem->NbCornerNodes();
8691 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8692 if ( aGeomType == SMDSEntity_Polyhedra )
8693 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8694 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8695 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8697 // remove a linear element
8698 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8700 // remove central nodes of biquadratic elements (biquad->quad conversion)
8701 if ( hasCentralNodes )
8702 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8703 if ( nodes[i]->NbInverseElements() == 0 )
8704 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8706 const SMDS_MeshElement* NewElem = 0;
8712 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8720 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8723 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8726 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8730 case SMDSAbs_Volume :
8734 case SMDSEntity_Tetra:
8735 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8737 case SMDSEntity_Pyramid:
8738 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8740 case SMDSEntity_Penta:
8741 case SMDSEntity_Quad_Penta:
8742 case SMDSEntity_BiQuad_Penta:
8743 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8745 case SMDSEntity_Hexa:
8746 case SMDSEntity_Quad_Hexa:
8747 case SMDSEntity_TriQuad_Hexa:
8748 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8749 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8751 case SMDSEntity_Hexagonal_Prism:
8753 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8760 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8761 if( NewElem && NewElem->getshapeId() < 1 )
8762 theSm->AddElement( NewElem );
8766 //=======================================================================
8767 //function : ConvertToQuadratic
8769 //=======================================================================
8771 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8773 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8774 SMESHDS_Mesh* meshDS = GetMeshDS();
8776 SMESH_MesherHelper aHelper(*myMesh);
8778 aHelper.SetIsQuadratic( true );
8779 aHelper.SetIsBiQuadratic( theToBiQuad );
8780 aHelper.SetElementsOnShape(true);
8781 aHelper.ToFixNodeParameters( true );
8783 // convert elements assigned to sub-meshes
8784 int nbCheckedElems = 0;
8785 if ( myMesh->HasShapeToMesh() )
8787 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8789 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8790 while ( smIt->more() ) {
8791 SMESH_subMesh* sm = smIt->next();
8792 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8793 aHelper.SetSubShape( sm->GetSubShape() );
8794 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8800 // convert elements NOT assigned to sub-meshes
8801 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8802 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8804 aHelper.SetElementsOnShape(false);
8805 SMESHDS_SubMesh *smDS = 0;
8808 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8809 while( aEdgeItr->more() )
8811 const SMDS_MeshEdge* edge = aEdgeItr->next();
8812 if ( !edge->IsQuadratic() )
8814 int id = edge->GetID();
8815 const SMDS_MeshNode* n1 = edge->GetNode(0);
8816 const SMDS_MeshNode* n2 = edge->GetNode(1);
8818 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8820 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8821 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8825 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8830 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8831 while( aFaceItr->more() )
8833 const SMDS_MeshFace* face = aFaceItr->next();
8834 if ( !face ) continue;
8836 const SMDSAbs_EntityType type = face->GetEntityType();
8840 case SMDSEntity_Quad_Triangle:
8841 case SMDSEntity_Quad_Quadrangle:
8842 alreadyOK = !theToBiQuad;
8843 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8845 case SMDSEntity_BiQuad_Triangle:
8846 case SMDSEntity_BiQuad_Quadrangle:
8847 alreadyOK = theToBiQuad;
8848 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8850 default: alreadyOK = false;
8855 const int id = face->GetID();
8856 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8858 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8860 SMDS_MeshFace * NewFace = 0;
8863 case SMDSEntity_Triangle:
8864 case SMDSEntity_Quad_Triangle:
8865 case SMDSEntity_BiQuad_Triangle:
8866 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8867 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8868 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8871 case SMDSEntity_Quadrangle:
8872 case SMDSEntity_Quad_Quadrangle:
8873 case SMDSEntity_BiQuad_Quadrangle:
8874 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8875 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8876 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8880 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8882 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8886 vector<int> nbNodeInFaces;
8887 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8888 while(aVolumeItr->more())
8890 const SMDS_MeshVolume* volume = aVolumeItr->next();
8891 if ( !volume ) continue;
8893 const SMDSAbs_EntityType type = volume->GetEntityType();
8894 if ( volume->IsQuadratic() )
8899 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8900 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8901 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
8902 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
8903 default: alreadyOK = true;
8907 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8911 const int id = volume->GetID();
8912 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8913 if ( type == SMDSEntity_Polyhedra )
8914 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
8915 else if ( type == SMDSEntity_Hexagonal_Prism )
8916 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8918 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8920 SMDS_MeshVolume * NewVolume = 0;
8923 case SMDSEntity_Tetra:
8924 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8926 case SMDSEntity_Hexa:
8927 case SMDSEntity_Quad_Hexa:
8928 case SMDSEntity_TriQuad_Hexa:
8929 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8930 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8931 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8932 if ( nodes[i]->NbInverseElements() == 0 )
8933 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8935 case SMDSEntity_Pyramid:
8936 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8937 nodes[3], nodes[4], id, theForce3d);
8939 case SMDSEntity_Penta:
8940 case SMDSEntity_Quad_Penta:
8941 case SMDSEntity_BiQuad_Penta:
8942 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8943 nodes[3], nodes[4], nodes[5], id, theForce3d);
8944 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
8945 if ( nodes[i]->NbInverseElements() == 0 )
8946 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8948 case SMDSEntity_Hexagonal_Prism:
8950 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8952 ReplaceElemInGroups(volume, NewVolume, meshDS);
8957 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8958 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8959 // aHelper.FixQuadraticElements(myError);
8960 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8964 //================================================================================
8966 * \brief Makes given elements quadratic
8967 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
8968 * \param theElements - elements to make quadratic
8970 //================================================================================
8972 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
8973 TIDSortedElemSet& theElements,
8974 const bool theToBiQuad)
8976 if ( theElements.empty() ) return;
8978 // we believe that all theElements are of the same type
8979 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
8981 // get all nodes shared by theElements
8982 TIDSortedNodeSet allNodes;
8983 TIDSortedElemSet::iterator eIt = theElements.begin();
8984 for ( ; eIt != theElements.end(); ++eIt )
8985 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
8987 // complete theElements with elements of lower dim whose all nodes are in allNodes
8989 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
8990 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
8991 TIDSortedNodeSet::iterator nIt = allNodes.begin();
8992 for ( ; nIt != allNodes.end(); ++nIt )
8994 const SMDS_MeshNode* n = *nIt;
8995 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
8996 while ( invIt->more() )
8998 const SMDS_MeshElement* e = invIt->next();
8999 const SMDSAbs_ElementType type = e->GetType();
9000 if ( e->IsQuadratic() )
9002 quadAdjacentElems[ type ].insert( e );
9005 switch ( e->GetEntityType() ) {
9006 case SMDSEntity_Quad_Triangle:
9007 case SMDSEntity_Quad_Quadrangle:
9008 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9009 case SMDSEntity_BiQuad_Triangle:
9010 case SMDSEntity_BiQuad_Quadrangle:
9011 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9012 default: alreadyOK = true;
9017 if ( type >= elemType )
9018 continue; // same type or more complex linear element
9020 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9021 continue; // e is already checked
9025 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9026 while ( nodeIt->more() && allIn )
9027 allIn = allNodes.count( nodeIt->next() );
9029 theElements.insert(e );
9033 SMESH_MesherHelper helper(*myMesh);
9034 helper.SetIsQuadratic( true );
9035 helper.SetIsBiQuadratic( theToBiQuad );
9037 // add links of quadratic adjacent elements to the helper
9039 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9040 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9041 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9043 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9045 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9046 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9047 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9049 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9051 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9052 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9053 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9055 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9058 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9060 SMESHDS_Mesh* meshDS = GetMeshDS();
9061 SMESHDS_SubMesh* smDS = 0;
9062 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9064 const SMDS_MeshElement* elem = *eIt;
9067 int nbCentralNodes = 0;
9068 switch ( elem->GetEntityType() ) {
9069 // linear convertible
9070 case SMDSEntity_Edge:
9071 case SMDSEntity_Triangle:
9072 case SMDSEntity_Quadrangle:
9073 case SMDSEntity_Tetra:
9074 case SMDSEntity_Pyramid:
9075 case SMDSEntity_Hexa:
9076 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9077 // quadratic that can become bi-quadratic
9078 case SMDSEntity_Quad_Triangle:
9079 case SMDSEntity_Quad_Quadrangle:
9080 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9082 case SMDSEntity_BiQuad_Triangle:
9083 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9084 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9086 default: alreadyOK = true;
9088 if ( alreadyOK ) continue;
9090 const SMDSAbs_ElementType type = elem->GetType();
9091 const int id = elem->GetID();
9092 const int nbNodes = elem->NbCornerNodes();
9093 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9095 helper.SetSubShape( elem->getshapeId() );
9097 if ( !smDS || !smDS->Contains( elem ))
9098 smDS = meshDS->MeshElements( elem->getshapeId() );
9099 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9101 SMDS_MeshElement * newElem = 0;
9104 case 4: // cases for most frequently used element types go first (for optimization)
9105 if ( type == SMDSAbs_Volume )
9106 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9108 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9111 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9112 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9115 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9118 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9121 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9122 nodes[4], id, theForce3d);
9125 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9126 nodes[4], nodes[5], id, theForce3d);
9130 ReplaceElemInGroups( elem, newElem, meshDS);
9131 if( newElem && smDS )
9132 smDS->AddElement( newElem );
9134 // remove central nodes
9135 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9136 if ( nodes[i]->NbInverseElements() == 0 )
9137 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9139 } // loop on theElements
9142 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9143 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9144 // helper.FixQuadraticElements( myError );
9145 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9149 //=======================================================================
9151 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9152 * \return int - nb of checked elements
9154 //=======================================================================
9156 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9157 SMDS_ElemIteratorPtr theItr,
9158 const int theShapeID)
9161 SMESHDS_Mesh* meshDS = GetMeshDS();
9162 ElemFeatures elemType;
9163 vector<const SMDS_MeshNode *> nodes;
9165 while( theItr->more() )
9167 const SMDS_MeshElement* elem = theItr->next();
9169 if( elem && elem->IsQuadratic())
9172 int nbCornerNodes = elem->NbCornerNodes();
9173 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9175 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9177 //remove a quadratic element
9178 if ( !theSm || !theSm->Contains( elem ))
9179 theSm = meshDS->MeshElements( elem->getshapeId() );
9180 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9182 // remove medium nodes
9183 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9184 if ( nodes[i]->NbInverseElements() == 0 )
9185 meshDS->RemoveFreeNode( nodes[i], theSm );
9187 // add a linear element
9188 nodes.resize( nbCornerNodes );
9189 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9190 ReplaceElemInGroups(elem, newElem, meshDS);
9191 if( theSm && newElem )
9192 theSm->AddElement( newElem );
9198 //=======================================================================
9199 //function : ConvertFromQuadratic
9201 //=======================================================================
9203 bool SMESH_MeshEditor::ConvertFromQuadratic()
9205 int nbCheckedElems = 0;
9206 if ( myMesh->HasShapeToMesh() )
9208 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9210 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9211 while ( smIt->more() ) {
9212 SMESH_subMesh* sm = smIt->next();
9213 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9214 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9220 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9221 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9223 SMESHDS_SubMesh *aSM = 0;
9224 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9232 //================================================================================
9234 * \brief Return true if all medium nodes of the element are in the node set
9236 //================================================================================
9238 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9240 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9241 if ( !nodeSet.count( elem->GetNode(i) ))
9247 //================================================================================
9249 * \brief Makes given elements linear
9251 //================================================================================
9253 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9255 if ( theElements.empty() ) return;
9257 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9258 set<int> mediumNodeIDs;
9259 TIDSortedElemSet::iterator eIt = theElements.begin();
9260 for ( ; eIt != theElements.end(); ++eIt )
9262 const SMDS_MeshElement* e = *eIt;
9263 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9264 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9267 // replace given elements by linear ones
9268 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9269 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9271 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9272 // except those elements sharing medium nodes of quadratic element whose medium nodes
9273 // are not all in mediumNodeIDs
9275 // get remaining medium nodes
9276 TIDSortedNodeSet mediumNodes;
9277 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9278 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9279 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9280 mediumNodes.insert( mediumNodes.end(), n );
9282 // find more quadratic elements to convert
9283 TIDSortedElemSet moreElemsToConvert;
9284 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9285 for ( ; nIt != mediumNodes.end(); ++nIt )
9287 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9288 while ( invIt->more() )
9290 const SMDS_MeshElement* e = invIt->next();
9291 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9293 // find a more complex element including e and
9294 // whose medium nodes are not in mediumNodes
9295 bool complexFound = false;
9296 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9298 SMDS_ElemIteratorPtr invIt2 =
9299 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9300 while ( invIt2->more() )
9302 const SMDS_MeshElement* eComplex = invIt2->next();
9303 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9305 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9306 if ( nbCommonNodes == e->NbNodes())
9308 complexFound = true;
9309 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9315 if ( !complexFound )
9316 moreElemsToConvert.insert( e );
9320 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9321 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9324 //=======================================================================
9325 //function : SewSideElements
9327 //=======================================================================
9329 SMESH_MeshEditor::Sew_Error
9330 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9331 TIDSortedElemSet& theSide2,
9332 const SMDS_MeshNode* theFirstNode1,
9333 const SMDS_MeshNode* theFirstNode2,
9334 const SMDS_MeshNode* theSecondNode1,
9335 const SMDS_MeshNode* theSecondNode2)
9339 if ( theSide1.size() != theSide2.size() )
9340 return SEW_DIFF_NB_OF_ELEMENTS;
9342 Sew_Error aResult = SEW_OK;
9344 // 1. Build set of faces representing each side
9345 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9346 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9348 // =======================================================================
9349 // 1. Build set of faces representing each side:
9350 // =======================================================================
9351 // a. build set of nodes belonging to faces
9352 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9353 // c. create temporary faces representing side of volumes if correspondent
9354 // face does not exist
9356 SMESHDS_Mesh* aMesh = GetMeshDS();
9357 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9358 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9359 TIDSortedElemSet faceSet1, faceSet2;
9360 set<const SMDS_MeshElement*> volSet1, volSet2;
9361 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9362 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9363 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9364 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9365 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9366 int iSide, iFace, iNode;
9368 list<const SMDS_MeshElement* > tempFaceList;
9369 for ( iSide = 0; iSide < 2; iSide++ ) {
9370 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9371 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9372 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9373 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9374 set<const SMDS_MeshElement*>::iterator vIt;
9375 TIDSortedElemSet::iterator eIt;
9376 set<const SMDS_MeshNode*>::iterator nIt;
9378 // check that given nodes belong to given elements
9379 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9380 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9381 int firstIndex = -1, secondIndex = -1;
9382 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9383 const SMDS_MeshElement* elem = *eIt;
9384 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9385 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9386 if ( firstIndex > -1 && secondIndex > -1 ) break;
9388 if ( firstIndex < 0 || secondIndex < 0 ) {
9389 // we can simply return until temporary faces created
9390 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9393 // -----------------------------------------------------------
9394 // 1a. Collect nodes of existing faces
9395 // and build set of face nodes in order to detect missing
9396 // faces corresponding to sides of volumes
9397 // -----------------------------------------------------------
9399 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9401 // loop on the given element of a side
9402 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9403 //const SMDS_MeshElement* elem = *eIt;
9404 const SMDS_MeshElement* elem = *eIt;
9405 if ( elem->GetType() == SMDSAbs_Face ) {
9406 faceSet->insert( elem );
9407 set <const SMDS_MeshNode*> faceNodeSet;
9408 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9409 while ( nodeIt->more() ) {
9410 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9411 nodeSet->insert( n );
9412 faceNodeSet.insert( n );
9414 setOfFaceNodeSet.insert( faceNodeSet );
9416 else if ( elem->GetType() == SMDSAbs_Volume )
9417 volSet->insert( elem );
9419 // ------------------------------------------------------------------------------
9420 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9421 // ------------------------------------------------------------------------------
9423 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9424 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9425 while ( fIt->more() ) { // loop on faces sharing a node
9426 const SMDS_MeshElement* f = fIt->next();
9427 if ( faceSet->find( f ) == faceSet->end() ) {
9428 // check if all nodes are in nodeSet and
9429 // complete setOfFaceNodeSet if they are
9430 set <const SMDS_MeshNode*> faceNodeSet;
9431 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9432 bool allInSet = true;
9433 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9434 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9435 if ( nodeSet->find( n ) == nodeSet->end() )
9438 faceNodeSet.insert( n );
9441 faceSet->insert( f );
9442 setOfFaceNodeSet.insert( faceNodeSet );
9448 // -------------------------------------------------------------------------
9449 // 1c. Create temporary faces representing sides of volumes if correspondent
9450 // face does not exist
9451 // -------------------------------------------------------------------------
9453 if ( !volSet->empty() ) {
9454 //int nodeSetSize = nodeSet->size();
9456 // loop on given volumes
9457 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9458 SMDS_VolumeTool vol (*vIt);
9459 // loop on volume faces: find free faces
9460 // --------------------------------------
9461 list<const SMDS_MeshElement* > freeFaceList;
9462 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9463 if ( !vol.IsFreeFace( iFace ))
9465 // check if there is already a face with same nodes in a face set
9466 const SMDS_MeshElement* aFreeFace = 0;
9467 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9468 int nbNodes = vol.NbFaceNodes( iFace );
9469 set <const SMDS_MeshNode*> faceNodeSet;
9470 vol.GetFaceNodes( iFace, faceNodeSet );
9471 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9473 // no such a face is given but it still can exist, check it
9474 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9475 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9478 // create a temporary face
9479 if ( nbNodes == 3 ) {
9480 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9481 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9483 else if ( nbNodes == 4 ) {
9484 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9485 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9488 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9489 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9490 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9493 tempFaceList.push_back( aFreeFace );
9497 freeFaceList.push_back( aFreeFace );
9499 } // loop on faces of a volume
9501 // choose one of several free faces of a volume
9502 // --------------------------------------------
9503 if ( freeFaceList.size() > 1 ) {
9504 // choose a face having max nb of nodes shared by other elems of a side
9505 int maxNbNodes = -1;
9506 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9507 while ( fIt != freeFaceList.end() ) { // loop on free faces
9508 int nbSharedNodes = 0;
9509 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9510 while ( nodeIt->more() ) { // loop on free face nodes
9511 const SMDS_MeshNode* n =
9512 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9513 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9514 while ( invElemIt->more() ) {
9515 const SMDS_MeshElement* e = invElemIt->next();
9516 nbSharedNodes += faceSet->count( e );
9517 nbSharedNodes += elemSet->count( e );
9520 if ( nbSharedNodes > maxNbNodes ) {
9521 maxNbNodes = nbSharedNodes;
9522 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9524 else if ( nbSharedNodes == maxNbNodes ) {
9528 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9531 if ( freeFaceList.size() > 1 )
9533 // could not choose one face, use another way
9534 // choose a face most close to the bary center of the opposite side
9535 gp_XYZ aBC( 0., 0., 0. );
9536 set <const SMDS_MeshNode*> addedNodes;
9537 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9538 eIt = elemSet2->begin();
9539 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9540 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9541 while ( nodeIt->more() ) { // loop on free face nodes
9542 const SMDS_MeshNode* n =
9543 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9544 if ( addedNodes.insert( n ).second )
9545 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9548 aBC /= addedNodes.size();
9549 double minDist = DBL_MAX;
9550 fIt = freeFaceList.begin();
9551 while ( fIt != freeFaceList.end() ) { // loop on free faces
9553 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9554 while ( nodeIt->more() ) { // loop on free face nodes
9555 const SMDS_MeshNode* n =
9556 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9557 gp_XYZ p( n->X(),n->Y(),n->Z() );
9558 dist += ( aBC - p ).SquareModulus();
9560 if ( dist < minDist ) {
9562 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9565 fIt = freeFaceList.erase( fIt++ );
9568 } // choose one of several free faces of a volume
9570 if ( freeFaceList.size() == 1 ) {
9571 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9572 faceSet->insert( aFreeFace );
9573 // complete a node set with nodes of a found free face
9574 // for ( iNode = 0; iNode < ; iNode++ )
9575 // nodeSet->insert( fNodes[ iNode ] );
9578 } // loop on volumes of a side
9580 // // complete a set of faces if new nodes in a nodeSet appeared
9581 // // ----------------------------------------------------------
9582 // if ( nodeSetSize != nodeSet->size() ) {
9583 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9584 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9585 // while ( fIt->more() ) { // loop on faces sharing a node
9586 // const SMDS_MeshElement* f = fIt->next();
9587 // if ( faceSet->find( f ) == faceSet->end() ) {
9588 // // check if all nodes are in nodeSet and
9589 // // complete setOfFaceNodeSet if they are
9590 // set <const SMDS_MeshNode*> faceNodeSet;
9591 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9592 // bool allInSet = true;
9593 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9594 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9595 // if ( nodeSet->find( n ) == nodeSet->end() )
9596 // allInSet = false;
9598 // faceNodeSet.insert( n );
9600 // if ( allInSet ) {
9601 // faceSet->insert( f );
9602 // setOfFaceNodeSet.insert( faceNodeSet );
9608 } // Create temporary faces, if there are volumes given
9611 if ( faceSet1.size() != faceSet2.size() ) {
9612 // delete temporary faces: they are in reverseElements of actual nodes
9613 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9614 // while ( tmpFaceIt->more() )
9615 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9616 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9617 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9618 // aMesh->RemoveElement(*tmpFaceIt);
9619 MESSAGE("Diff nb of faces");
9620 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9623 // ============================================================
9624 // 2. Find nodes to merge:
9625 // bind a node to remove to a node to put instead
9626 // ============================================================
9628 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9629 if ( theFirstNode1 != theFirstNode2 )
9630 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9631 if ( theSecondNode1 != theSecondNode2 )
9632 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9634 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9635 set< long > linkIdSet; // links to process
9636 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9638 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9639 list< NLink > linkList[2];
9640 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9641 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9642 // loop on links in linkList; find faces by links and append links
9643 // of the found faces to linkList
9644 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9645 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9647 NLink link[] = { *linkIt[0], *linkIt[1] };
9648 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9649 if ( !linkIdSet.count( linkID ) )
9652 // by links, find faces in the face sets,
9653 // and find indices of link nodes in the found faces;
9654 // in a face set, there is only one or no face sharing a link
9655 // ---------------------------------------------------------------
9657 const SMDS_MeshElement* face[] = { 0, 0 };
9658 vector<const SMDS_MeshNode*> fnodes[2];
9659 int iLinkNode[2][2];
9660 TIDSortedElemSet avoidSet;
9661 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9662 const SMDS_MeshNode* n1 = link[iSide].first;
9663 const SMDS_MeshNode* n2 = link[iSide].second;
9664 //cout << "Side " << iSide << " ";
9665 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9666 // find a face by two link nodes
9667 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9668 *faceSetPtr[ iSide ], avoidSet,
9669 &iLinkNode[iSide][0],
9670 &iLinkNode[iSide][1] );
9673 //cout << " F " << face[ iSide]->GetID() <<endl;
9674 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9675 // put face nodes to fnodes
9676 SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9677 fnodes[ iSide ].assign( nIt, nEnd );
9678 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9682 // check similarity of elements of the sides
9683 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9684 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9685 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9686 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9689 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9691 break; // do not return because it's necessary to remove tmp faces
9694 // set nodes to merge
9695 // -------------------
9697 if ( face[0] && face[1] ) {
9698 const int nbNodes = face[0]->NbNodes();
9699 if ( nbNodes != face[1]->NbNodes() ) {
9700 MESSAGE("Diff nb of face nodes");
9701 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9702 break; // do not return because it s necessary to remove tmp faces
9704 bool reverse[] = { false, false }; // order of nodes in the link
9705 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9706 // analyse link orientation in faces
9707 int i1 = iLinkNode[ iSide ][ 0 ];
9708 int i2 = iLinkNode[ iSide ][ 1 ];
9709 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9711 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9712 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9713 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9715 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9716 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9719 // add other links of the faces to linkList
9720 // -----------------------------------------
9722 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9723 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9724 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9725 if ( !iter_isnew.second ) { // already in a set: no need to process
9726 linkIdSet.erase( iter_isnew.first );
9728 else // new in set == encountered for the first time: add
9730 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9731 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9732 linkList[0].push_back ( NLink( n1, n2 ));
9733 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9738 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9741 } // loop on link lists
9743 if ( aResult == SEW_OK &&
9744 ( //linkIt[0] != linkList[0].end() ||
9745 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9746 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9747 " " << (faceSetPtr[1]->empty()));
9748 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9751 // ====================================================================
9752 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9753 // ====================================================================
9755 // delete temporary faces
9756 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9757 // while ( tmpFaceIt->more() )
9758 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9759 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9760 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9761 aMesh->RemoveElement(*tmpFaceIt);
9763 if ( aResult != SEW_OK)
9766 list< int > nodeIDsToRemove;
9767 vector< const SMDS_MeshNode*> nodes;
9768 ElemFeatures elemType;
9770 // loop on nodes replacement map
9771 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9772 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9773 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9775 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9776 nodeIDsToRemove.push_back( nToRemove->GetID() );
9777 // loop on elements sharing nToRemove
9778 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9779 while ( invElemIt->more() ) {
9780 const SMDS_MeshElement* e = invElemIt->next();
9781 // get a new suite of nodes: make replacement
9782 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9783 nodes.resize( nbNodes );
9784 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9785 while ( nIt->more() ) {
9786 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9787 nnIt = nReplaceMap.find( n );
9788 if ( nnIt != nReplaceMap.end() ) {
9794 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9795 // elemIDsToRemove.push_back( e->GetID() );
9799 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9800 aMesh->RemoveElement( e );
9802 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9804 AddToSameGroups( newElem, e, aMesh );
9805 if ( int aShapeId = e->getshapeId() )
9806 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9812 Remove( nodeIDsToRemove, true );
9817 //================================================================================
9819 * \brief Find corresponding nodes in two sets of faces
9820 * \param theSide1 - first face set
9821 * \param theSide2 - second first face
9822 * \param theFirstNode1 - a boundary node of set 1
9823 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9824 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9825 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9826 * \param nReplaceMap - output map of corresponding nodes
9827 * \return bool - is a success or not
9829 //================================================================================
9832 //#define DEBUG_MATCHING_NODES
9835 SMESH_MeshEditor::Sew_Error
9836 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9837 set<const SMDS_MeshElement*>& theSide2,
9838 const SMDS_MeshNode* theFirstNode1,
9839 const SMDS_MeshNode* theFirstNode2,
9840 const SMDS_MeshNode* theSecondNode1,
9841 const SMDS_MeshNode* theSecondNode2,
9842 TNodeNodeMap & nReplaceMap)
9844 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9846 nReplaceMap.clear();
9847 if ( theFirstNode1 != theFirstNode2 )
9848 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9849 if ( theSecondNode1 != theSecondNode2 )
9850 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9852 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9853 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9855 list< NLink > linkList[2];
9856 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9857 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9859 // loop on links in linkList; find faces by links and append links
9860 // of the found faces to linkList
9861 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9862 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9863 NLink link[] = { *linkIt[0], *linkIt[1] };
9864 if ( linkSet.find( link[0] ) == linkSet.end() )
9867 // by links, find faces in the face sets,
9868 // and find indices of link nodes in the found faces;
9869 // in a face set, there is only one or no face sharing a link
9870 // ---------------------------------------------------------------
9872 const SMDS_MeshElement* face[] = { 0, 0 };
9873 list<const SMDS_MeshNode*> notLinkNodes[2];
9874 //bool reverse[] = { false, false }; // order of notLinkNodes
9876 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9878 const SMDS_MeshNode* n1 = link[iSide].first;
9879 const SMDS_MeshNode* n2 = link[iSide].second;
9880 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9881 set< const SMDS_MeshElement* > facesOfNode1;
9882 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9884 // during a loop of the first node, we find all faces around n1,
9885 // during a loop of the second node, we find one face sharing both n1 and n2
9886 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9887 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9888 while ( fIt->more() ) { // loop on faces sharing a node
9889 const SMDS_MeshElement* f = fIt->next();
9890 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9891 ! facesOfNode1.insert( f ).second ) // f encounters twice
9893 if ( face[ iSide ] ) {
9894 MESSAGE( "2 faces per link " );
9895 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9898 faceSet->erase( f );
9900 // get not link nodes
9901 int nbN = f->NbNodes();
9902 if ( f->IsQuadratic() )
9904 nbNodes[ iSide ] = nbN;
9905 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9906 int i1 = f->GetNodeIndex( n1 );
9907 int i2 = f->GetNodeIndex( n2 );
9908 int iEnd = nbN, iBeg = -1, iDelta = 1;
9909 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9911 std::swap( iEnd, iBeg ); iDelta = -1;
9916 if ( i == iEnd ) i = iBeg + iDelta;
9917 if ( i == i1 ) break;
9918 nodes.push_back ( f->GetNode( i ) );
9924 // check similarity of elements of the sides
9925 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9926 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9927 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9928 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9931 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9935 // set nodes to merge
9936 // -------------------
9938 if ( face[0] && face[1] ) {
9939 if ( nbNodes[0] != nbNodes[1] ) {
9940 MESSAGE("Diff nb of face nodes");
9941 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9943 #ifdef DEBUG_MATCHING_NODES
9944 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9945 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9946 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9948 int nbN = nbNodes[0];
9950 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9951 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9952 for ( int i = 0 ; i < nbN - 2; ++i ) {
9953 #ifdef DEBUG_MATCHING_NODES
9954 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9956 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9960 // add other links of the face 1 to linkList
9961 // -----------------------------------------
9963 const SMDS_MeshElement* f0 = face[0];
9964 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
9965 for ( int i = 0; i < nbN; i++ )
9967 const SMDS_MeshNode* n2 = f0->GetNode( i );
9968 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
9969 linkSet.insert( SMESH_TLink( n1, n2 ));
9970 if ( !iter_isnew.second ) { // already in a set: no need to process
9971 linkSet.erase( iter_isnew.first );
9973 else // new in set == encountered for the first time: add
9975 #ifdef DEBUG_MATCHING_NODES
9976 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
9977 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
9979 linkList[0].push_back ( NLink( n1, n2 ));
9980 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9985 } // loop on link lists
9990 namespace // automatically find theAffectedElems for DoubleNodes()
9992 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
9994 //--------------------------------------------------------------------------------
9995 // Nodes shared by adjacent FissureBorder's.
9996 // 1 node if FissureBorder separates faces
9997 // 2 nodes if FissureBorder separates volumes
10000 const SMDS_MeshNode* _nodes[2];
10003 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10007 _nbNodes = bool( n1 ) + bool( n2 );
10008 if ( _nbNodes == 2 && n1 > n2 )
10009 std::swap( _nodes[0], _nodes[1] );
10011 bool operator<( const SubBorder& other ) const
10013 for ( int i = 0; i < _nbNodes; ++i )
10015 if ( _nodes[i] < other._nodes[i] ) return true;
10016 if ( _nodes[i] > other._nodes[i] ) return false;
10022 //--------------------------------------------------------------------------------
10023 // Map a SubBorder to all FissureBorder it bounds
10024 struct FissureBorder;
10025 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10026 typedef TBorderLinks::iterator TMappedSub;
10028 //--------------------------------------------------------------------------------
10030 * \brief Element border (volume facet or face edge) at a fissure
10032 struct FissureBorder
10034 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10035 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10037 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10038 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10040 FissureBorder( FissureBorder && from ) // move constructor
10042 std::swap( _nodes, from._nodes );
10043 std::swap( _sortedNodes, from._sortedNodes );
10044 _elems[0] = from._elems[0];
10045 _elems[1] = from._elems[1];
10048 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10049 std::vector< const SMDS_MeshElement* > & adjElems)
10050 : _nodes( elemToDuplicate->NbCornerNodes() )
10052 for ( size_t i = 0; i < _nodes.size(); ++i )
10053 _nodes[i] = elemToDuplicate->GetNode( i );
10055 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10056 findAdjacent( type, adjElems );
10059 FissureBorder( const SMDS_MeshNode** nodes,
10060 const size_t nbNodes,
10061 const SMDSAbs_ElementType adjElemsType,
10062 std::vector< const SMDS_MeshElement* > & adjElems)
10063 : _nodes( nodes, nodes + nbNodes )
10065 findAdjacent( adjElemsType, adjElems );
10068 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10069 std::vector< const SMDS_MeshElement* > & adjElems)
10071 _elems[0] = _elems[1] = 0;
10073 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10074 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10075 _elems[i] = adjElems[i];
10078 bool operator<( const FissureBorder& other ) const
10080 return GetSortedNodes() < other.GetSortedNodes();
10083 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10085 if ( _sortedNodes.empty() && !_nodes.empty() )
10087 FissureBorder* me = const_cast<FissureBorder*>( this );
10088 me->_sortedNodes = me->_nodes;
10089 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10091 return _sortedNodes;
10094 size_t NbSub() const
10096 return _nodes.size();
10099 SubBorder Sub(size_t i) const
10101 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10104 void AddSelfTo( TBorderLinks& borderLinks )
10106 _mappedSubs.resize( NbSub() );
10107 for ( size_t i = 0; i < NbSub(); ++i )
10109 TBorderLinks::iterator s2b =
10110 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10111 s2b->second.push_back( this );
10112 _mappedSubs[ i ] = s2b;
10121 const SMDS_MeshElement* GetMarkedElem() const
10123 if ( _nodes.empty() ) return 0; // cleared
10124 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10125 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10129 gp_XYZ GetNorm() const // normal to the border
10132 if ( _nodes.size() == 2 )
10134 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10135 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10137 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10140 gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10141 norm = bordDir ^ avgNorm;
10145 SMESH_NodeXYZ p0( _nodes[0] );
10146 SMESH_NodeXYZ p1( _nodes[1] );
10147 SMESH_NodeXYZ p2( _nodes[2] );
10148 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10150 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10156 void ChooseSide() // mark an _elem located at positive side of fissure
10158 _elems[0]->setIsMarked( true );
10159 gp_XYZ norm = GetNorm();
10160 double maxX = norm.Coord(1);
10161 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10162 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10165 _elems[0]->setIsMarked( false );
10166 _elems[1]->setIsMarked( true );
10170 }; // struct FissureBorder
10172 //--------------------------------------------------------------------------------
10174 * \brief Classifier of elements at fissure edge
10176 class FissureNormal
10178 std::vector< gp_XYZ > _normals;
10182 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10185 _normals.reserve(2);
10186 _normals.push_back( bord.GetNorm() );
10187 if ( _normals.size() == 2 )
10188 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10191 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10194 switch ( _normals.size() ) {
10197 isIn = !isOut( n, _normals[0], elem );
10202 bool in1 = !isOut( n, _normals[0], elem );
10203 bool in2 = !isOut( n, _normals[1], elem );
10204 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10211 //================================================================================
10213 * \brief Classify an element by a plane passing through a node
10215 //================================================================================
10217 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10219 SMESH_NodeXYZ p = n;
10221 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10223 SMESH_NodeXYZ pi = elem->GetNode( i );
10224 sumDot += norm * ( pi - p );
10226 return sumDot < -1e-100;
10229 //================================================================================
10231 * \brief Find FissureBorder's by nodes to duplicate
10233 //================================================================================
10235 void findFissureBorders( const TIDSortedElemSet& theNodes,
10236 std::vector< FissureBorder > & theFissureBorders )
10238 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10239 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10241 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10242 if ( n->NbInverseElements( elemType ) == 0 )
10244 elemType = SMDSAbs_Face;
10245 if ( n->NbInverseElements( elemType ) == 0 )
10248 // unmark elements touching the fissure
10249 for ( ; nIt != theNodes.end(); ++nIt )
10250 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10252 // loop on elements touching the fissure to get their borders belonging to the fissure
10253 std::set< FissureBorder > fissureBorders;
10254 std::vector< const SMDS_MeshElement* > adjElems;
10255 std::vector< const SMDS_MeshNode* > nodes;
10256 SMDS_VolumeTool volTool;
10257 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10259 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10260 while ( invIt->more() )
10262 const SMDS_MeshElement* eInv = invIt->next();
10263 if ( eInv->isMarked() ) continue;
10264 eInv->setIsMarked( true );
10266 if ( elemType == SMDSAbs_Volume )
10268 volTool.Set( eInv );
10269 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10270 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10272 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10273 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10275 bool allOnFissure = true;
10276 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10277 if (( allOnFissure = theNodes.count( nn[ iN ])))
10278 nodes.push_back( nn[ iN ]);
10279 if ( allOnFissure )
10280 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10281 elemType, adjElems )));
10284 else // elemType == SMDSAbs_Face
10286 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10287 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10288 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10290 nn[1] = eInv->GetNode( iN );
10291 onFissure1 = theNodes.count( nn[1] );
10292 if ( onFissure0 && onFissure1 )
10293 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10295 onFissure0 = onFissure1;
10301 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10302 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10303 for ( ; bord != fissureBorders.end(); ++bord )
10305 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10308 } // findFissureBorders()
10310 //================================================================================
10312 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10313 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10314 * \param [in] theNodesNot - nodes not to duplicate
10315 * \param [out] theAffectedElems - the found elements
10317 //================================================================================
10319 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10320 TIDSortedElemSet& theAffectedElems)
10322 if ( theElemsOrNodes.empty() ) return;
10324 // find FissureBorder's
10326 std::vector< FissureBorder > fissure;
10327 std::vector< const SMDS_MeshElement* > elemsByFacet;
10329 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10330 if ( (*elIt)->GetType() == SMDSAbs_Node )
10332 findFissureBorders( theElemsOrNodes, fissure );
10336 fissure.reserve( theElemsOrNodes.size() );
10337 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10338 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10340 if ( fissure.empty() )
10343 // fill borderLinks
10345 TBorderLinks borderLinks;
10347 for ( size_t i = 0; i < fissure.size(); ++i )
10349 fissure[i].AddSelfTo( borderLinks );
10352 // get theAffectedElems
10354 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10355 for ( size_t i = 0; i < fissure.size(); ++i )
10356 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10358 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10359 false, /*markElem=*/true );
10362 std::vector<const SMDS_MeshNode *> facetNodes;
10363 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10364 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10366 // choose a side of fissure
10367 fissure[0].ChooseSide();
10368 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10370 size_t nbCheckedBorders = 0;
10371 while ( nbCheckedBorders < fissure.size() )
10373 // find a FissureBorder to treat
10374 FissureBorder* bord = 0;
10375 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10376 if ( fissure[i].GetMarkedElem() )
10377 bord = & fissure[i];
10378 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10379 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10381 bord = & fissure[i];
10382 bord->ChooseSide();
10383 theAffectedElems.insert( bord->GetMarkedElem() );
10385 if ( !bord ) return;
10386 ++nbCheckedBorders;
10388 // treat FissureBorder's linked to bord
10389 fissureNodes.clear();
10390 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10391 for ( size_t i = 0; i < bord->NbSub(); ++i )
10393 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10394 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10395 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10396 const SubBorder& sb = l2b->first;
10397 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10399 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10401 for ( int j = 0; j < sb._nbNodes; ++j )
10402 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10406 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10407 // until an elem adjacent to a neighbour FissureBorder is found
10408 facetNodes.clear();
10409 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10410 facetNodes.resize( sb._nbNodes + 1 );
10414 // check if bordElem is adjacent to a neighbour FissureBorder
10415 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10417 FissureBorder* bord2 = linkedBorders[j];
10418 if ( bord2 == bord ) continue;
10419 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10422 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10427 // find the next bordElem
10428 const SMDS_MeshElement* nextBordElem = 0;
10429 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10431 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10432 if ( fissureNodes.count( n )) continue;
10434 facetNodes[ sb._nbNodes ] = n;
10435 elemsByFacet.clear();
10436 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10438 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10439 if ( elemsByFacet[ iE ] != bordElem &&
10440 !elemsByFacet[ iE ]->isMarked() )
10442 theAffectedElems.insert( elemsByFacet[ iE ]);
10443 elemsByFacet[ iE ]->setIsMarked( true );
10444 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10445 nextBordElem = elemsByFacet[ iE ];
10449 bordElem = nextBordElem;
10451 } // while ( bordElem )
10453 linkedBorders.clear(); // not to treat this link any more
10455 } // loop on SubBorder's of a FissureBorder
10459 } // loop on FissureBorder's
10462 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10464 // mark nodes of theAffectedElems
10465 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10467 // unmark nodes of the fissure
10468 elIt = theElemsOrNodes.begin();
10469 if ( (*elIt)->GetType() == SMDSAbs_Node )
10470 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10472 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10474 std::vector< gp_XYZ > normVec;
10476 // loop on nodes of the fissure, add elements having marked nodes
10477 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10479 const SMDS_MeshElement* e = (*elIt);
10480 if ( e->GetType() != SMDSAbs_Node )
10481 e->setIsMarked( true ); // avoid adding a fissure element
10483 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10485 const SMDS_MeshNode* n = e->GetNode( iN );
10486 if ( fissEdgeNodes2Norm.count( n ))
10489 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10490 while ( invIt->more() )
10492 const SMDS_MeshElement* eInv = invIt->next();
10493 if ( eInv->isMarked() ) continue;
10494 eInv->setIsMarked( true );
10496 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10497 while( nIt->more() )
10498 if ( nIt->next()->isMarked())
10500 theAffectedElems.insert( eInv );
10501 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10502 n->setIsMarked( false );
10509 // add elements on the fissure edge
10510 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10511 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10513 const SMDS_MeshNode* edgeNode = n2N->first;
10514 const FissureNormal & normals = n2N->second;
10516 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10517 while ( invIt->more() )
10519 const SMDS_MeshElement* eInv = invIt->next();
10520 if ( eInv->isMarked() ) continue;
10521 eInv->setIsMarked( true );
10523 // classify eInv using normals
10524 bool toAdd = normals.IsIn( edgeNode, eInv );
10525 if ( toAdd ) // check if all nodes lie on the fissure edge
10527 bool notOnEdge = false;
10528 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
10529 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10534 theAffectedElems.insert( eInv );
10540 } // findAffectedElems()
10543 //================================================================================
10545 * \brief Create elements equal (on same nodes) to given ones
10546 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10547 * elements of the uppest dimension are duplicated.
10549 //================================================================================
10551 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10553 ClearLastCreated();
10554 SMESHDS_Mesh* mesh = GetMeshDS();
10556 // get an element type and an iterator over elements
10558 SMDSAbs_ElementType type = SMDSAbs_All;
10559 SMDS_ElemIteratorPtr elemIt;
10560 if ( theElements.empty() )
10562 if ( mesh->NbNodes() == 0 )
10564 // get most complex type
10565 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10566 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10567 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10569 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10570 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10573 elemIt = mesh->elementsIterator( type );
10579 //type = (*theElements.begin())->GetType();
10580 elemIt = SMESHUtils::elemSetIterator( theElements );
10583 // un-mark all elements to avoid duplicating just created elements
10584 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10586 // duplicate elements
10588 ElemFeatures elemType;
10590 vector< const SMDS_MeshNode* > nodes;
10591 while ( elemIt->more() )
10593 const SMDS_MeshElement* elem = elemIt->next();
10594 if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10595 ( elem->isMarked() ))
10598 elemType.Init( elem, /*basicOnly=*/false );
10599 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10601 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10602 newElem->setIsMarked( true );
10606 //================================================================================
10608 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10609 \param theElems - the list of elements (edges or faces) to be replicated
10610 The nodes for duplication could be found from these elements
10611 \param theNodesNot - list of nodes to NOT replicate
10612 \param theAffectedElems - the list of elements (cells and edges) to which the
10613 replicated nodes should be associated to.
10614 \return TRUE if operation has been completed successfully, FALSE otherwise
10616 //================================================================================
10618 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10619 const TIDSortedElemSet& theNodesNot,
10620 const TIDSortedElemSet& theAffectedElems )
10622 ClearLastCreated();
10624 if ( theElems.size() == 0 )
10627 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10632 TNodeNodeMap anOldNodeToNewNode;
10633 // duplicate elements and nodes
10634 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10635 // replce nodes by duplications
10636 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10640 //================================================================================
10642 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10643 \param theMeshDS - mesh instance
10644 \param theElems - the elements replicated or modified (nodes should be changed)
10645 \param theNodesNot - nodes to NOT replicate
10646 \param theNodeNodeMap - relation of old node to new created node
10647 \param theIsDoubleElem - flag os to replicate element or modify
10648 \return TRUE if operation has been completed successfully, FALSE otherwise
10650 //================================================================================
10652 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10653 const TIDSortedElemSet& theElems,
10654 const TIDSortedElemSet& theNodesNot,
10655 TNodeNodeMap& theNodeNodeMap,
10656 const bool theIsDoubleElem )
10658 // iterate through element and duplicate them (by nodes duplication)
10660 std::vector<const SMDS_MeshNode*> newNodes;
10661 ElemFeatures elemType;
10663 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10664 for ( ; elemItr != theElems.end(); ++elemItr )
10666 const SMDS_MeshElement* anElem = *elemItr;
10670 // duplicate nodes to duplicate element
10671 bool isDuplicate = false;
10672 newNodes.resize( anElem->NbNodes() );
10673 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10675 while ( anIter->more() )
10677 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10678 const SMDS_MeshNode* aNewNode = aCurrNode;
10679 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10680 if ( n2n != theNodeNodeMap.end() )
10682 aNewNode = n2n->second;
10684 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10687 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10688 copyPosition( aCurrNode, aNewNode );
10689 theNodeNodeMap[ aCurrNode ] = aNewNode;
10690 myLastCreatedNodes.push_back( aNewNode );
10692 isDuplicate |= (aCurrNode != aNewNode);
10693 newNodes[ ind++ ] = aNewNode;
10695 if ( !isDuplicate )
10698 if ( theIsDoubleElem )
10699 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10701 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10708 //================================================================================
10710 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10711 \param theNodes - identifiers of nodes to be doubled
10712 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10713 nodes. If list of element identifiers is empty then nodes are doubled but
10714 they not assigned to elements
10715 \return TRUE if operation has been completed successfully, FALSE otherwise
10717 //================================================================================
10719 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10720 const std::list< int >& theListOfModifiedElems )
10722 ClearLastCreated();
10724 if ( theListOfNodes.size() == 0 )
10727 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10731 // iterate through nodes and duplicate them
10733 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10735 std::list< int >::const_iterator aNodeIter;
10736 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10738 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10744 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10747 copyPosition( aNode, aNewNode );
10748 anOldNodeToNewNode[ aNode ] = aNewNode;
10749 myLastCreatedNodes.push_back( aNewNode );
10753 // Change nodes of elements
10755 std::vector<const SMDS_MeshNode*> aNodeArr;
10757 std::list< int >::const_iterator anElemIter;
10758 for ( anElemIter = theListOfModifiedElems.begin();
10759 anElemIter != theListOfModifiedElems.end();
10762 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10766 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10767 for( size_t i = 0; i < aNodeArr.size(); ++i )
10769 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10770 anOldNodeToNewNode.find( aNodeArr[ i ]);
10771 if ( n2n != anOldNodeToNewNode.end() )
10772 aNodeArr[ i ] = n2n->second;
10774 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10782 //================================================================================
10784 \brief Check if element located inside shape
10785 \return TRUE if IN or ON shape, FALSE otherwise
10787 //================================================================================
10789 template<class Classifier>
10790 bool isInside(const SMDS_MeshElement* theElem,
10791 Classifier& theClassifier,
10792 const double theTol)
10794 gp_XYZ centerXYZ (0, 0, 0);
10795 for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10796 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10798 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10799 theClassifier.Perform(aPnt, theTol);
10800 TopAbs_State aState = theClassifier.State();
10801 return (aState == TopAbs_IN || aState == TopAbs_ON );
10804 //================================================================================
10806 * \brief Classifier of the 3D point on the TopoDS_Face
10807 * with interaface suitable for isInside()
10809 //================================================================================
10811 struct _FaceClassifier
10813 Extrema_ExtPS _extremum;
10814 BRepAdaptor_Surface _surface;
10815 TopAbs_State _state;
10817 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10819 _extremum.Initialize( _surface,
10820 _surface.FirstUParameter(), _surface.LastUParameter(),
10821 _surface.FirstVParameter(), _surface.LastVParameter(),
10822 _surface.Tolerance(), _surface.Tolerance() );
10824 void Perform(const gp_Pnt& aPnt, double theTol)
10827 _state = TopAbs_OUT;
10828 _extremum.Perform(aPnt);
10829 if ( _extremum.IsDone() )
10830 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10831 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10833 TopAbs_State State() const
10840 //================================================================================
10842 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10843 This method is the first step of DoubleNodeElemGroupsInRegion.
10844 \param theElems - list of groups of elements (edges or faces) to be replicated
10845 \param theNodesNot - list of groups of nodes not to replicated
10846 \param theShape - shape to detect affected elements (element which geometric center
10847 located on or inside shape). If the shape is null, detection is done on faces orientations
10848 (select elements with a gravity center on the side given by faces normals).
10849 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10850 The replicated nodes should be associated to affected elements.
10852 \sa DoubleNodeElemGroupsInRegion()
10854 //================================================================================
10856 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10857 const TIDSortedElemSet& theNodesNot,
10858 const TopoDS_Shape& theShape,
10859 TIDSortedElemSet& theAffectedElems)
10861 if ( theShape.IsNull() )
10863 findAffectedElems( theElems, theAffectedElems );
10867 const double aTol = Precision::Confusion();
10868 std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10869 std::unique_ptr<_FaceClassifier> aFaceClassifier;
10870 if ( theShape.ShapeType() == TopAbs_SOLID )
10872 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10873 bsc3d->PerformInfinitePoint(aTol);
10875 else if (theShape.ShapeType() == TopAbs_FACE )
10877 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10880 // iterates on indicated elements and get elements by back references from their nodes
10881 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10882 for ( ; elemItr != theElems.end(); ++elemItr )
10884 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10885 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10886 while ( nodeItr->more() )
10888 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10889 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10891 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10892 while ( backElemItr->more() )
10894 const SMDS_MeshElement* curElem = backElemItr->next();
10895 if ( curElem && theElems.find(curElem) == theElems.end() &&
10897 isInside( curElem, *bsc3d, aTol ) :
10898 isInside( curElem, *aFaceClassifier, aTol )))
10899 theAffectedElems.insert( curElem );
10907 //================================================================================
10909 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10910 \param theElems - group of of elements (edges or faces) to be replicated
10911 \param theNodesNot - group of nodes not to replicate
10912 \param theShape - shape to detect affected elements (element which geometric center
10913 located on or inside shape).
10914 The replicated nodes should be associated to affected elements.
10915 \return TRUE if operation has been completed successfully, FALSE otherwise
10917 //================================================================================
10919 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10920 const TIDSortedElemSet& theNodesNot,
10921 const TopoDS_Shape& theShape )
10923 if ( theShape.IsNull() )
10926 const double aTol = Precision::Confusion();
10927 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10928 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
10929 if ( theShape.ShapeType() == TopAbs_SOLID )
10931 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10932 bsc3d->PerformInfinitePoint(aTol);
10934 else if (theShape.ShapeType() == TopAbs_FACE )
10936 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
10939 // iterates on indicated elements and get elements by back references from their nodes
10940 TIDSortedElemSet anAffected;
10941 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10942 for ( ; elemItr != theElems.end(); ++elemItr )
10944 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10948 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10949 while ( nodeItr->more() )
10951 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10952 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10954 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10955 while ( backElemItr->more() )
10957 const SMDS_MeshElement* curElem = backElemItr->next();
10958 if ( curElem && theElems.find(curElem) == theElems.end() &&
10960 isInside( curElem, *bsc3d, aTol ) :
10961 isInside( curElem, *aFaceClassifier, aTol )))
10962 anAffected.insert( curElem );
10966 return DoubleNodes( theElems, theNodesNot, anAffected );
10970 * \brief compute an oriented angle between two planes defined by four points.
10971 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10972 * @param p0 base of the rotation axe
10973 * @param p1 extremity of the rotation axe
10974 * @param g1 belongs to the first plane
10975 * @param g2 belongs to the second plane
10977 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10979 gp_Vec vref(p0, p1);
10982 gp_Vec n1 = vref.Crossed(v1);
10983 gp_Vec n2 = vref.Crossed(v2);
10985 return n2.AngleWithRef(n1, vref);
10987 catch ( Standard_Failure ) {
10989 return Max( v1.Magnitude(), v2.Magnitude() );
10993 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10994 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10995 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10996 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10997 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10998 * 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.
10999 * 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.
11000 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11001 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11002 * \param theElems - list of groups of volumes, where a group of volume is a set of
11003 * SMDS_MeshElements sorted by Id.
11004 * \param createJointElems - if TRUE, create the elements
11005 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11006 * the boundary between \a theDomains and the rest mesh
11007 * \return TRUE if operation has been completed successfully, FALSE otherwise
11009 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11010 bool createJointElems,
11011 bool onAllBoundaries)
11013 // MESSAGE("----------------------------------------------");
11014 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11015 // MESSAGE("----------------------------------------------");
11017 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11018 meshDS->BuildDownWardConnectivity(true);
11020 SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11022 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11023 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11024 // build the list of nodes shared by 2 or more domains, with their domain indexes
11026 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11027 std::map<int,int>celldom; // cell vtkId --> domain
11028 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11029 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11030 faceDomains.clear();
11032 cellDomains.clear();
11033 nodeDomains.clear();
11034 std::map<int,int> emptyMap;
11035 std::set<int> emptySet;
11038 //MESSAGE(".. Number of domains :"<<theElems.size());
11040 TIDSortedElemSet theRestDomElems;
11041 const int iRestDom = -1;
11042 const int idom0 = onAllBoundaries ? iRestDom : 0;
11043 const int nbDomains = theElems.size();
11045 // Check if the domains do not share an element
11046 for (int idom = 0; idom < nbDomains-1; idom++)
11048 // MESSAGE("... Check of domain #" << idom);
11049 const TIDSortedElemSet& domain = theElems[idom];
11050 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11051 for (; elemItr != domain.end(); ++elemItr)
11053 const SMDS_MeshElement* anElem = *elemItr;
11054 int idombisdeb = idom + 1 ;
11055 // check if the element belongs to a domain further in the list
11056 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11058 const TIDSortedElemSet& domainbis = theElems[idombis];
11059 if ( domainbis.count( anElem ))
11061 MESSAGE(".... Domain #" << idom);
11062 MESSAGE(".... Domain #" << idombis);
11063 throw SALOME_Exception("The domains are not disjoint.");
11070 for (int idom = 0; idom < nbDomains; idom++)
11073 // --- build a map (face to duplicate --> volume to modify)
11074 // with all the faces shared by 2 domains (group of elements)
11075 // and corresponding volume of this domain, for each shared face.
11076 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11078 //MESSAGE("... Neighbors of domain #" << idom);
11079 const TIDSortedElemSet& domain = theElems[idom];
11080 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11081 for (; elemItr != domain.end(); ++elemItr)
11083 const SMDS_MeshElement* anElem = *elemItr;
11086 int vtkId = anElem->GetVtkID();
11087 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11088 int neighborsVtkIds[NBMAXNEIGHBORS];
11089 int downIds[NBMAXNEIGHBORS];
11090 unsigned char downTypes[NBMAXNEIGHBORS];
11091 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11092 for (int n = 0; n < nbNeighbors; n++)
11094 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11095 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11096 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11099 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11101 // MESSAGE("Domain " << idombis);
11102 const TIDSortedElemSet& domainbis = theElems[idombis];
11103 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11105 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11107 DownIdType face(downIds[n], downTypes[n]);
11108 if (!faceDomains[face].count(idom))
11110 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11111 celldom[vtkId] = idom;
11112 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11116 theRestDomElems.insert( elem );
11117 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11118 celldom[neighborsVtkIds[n]] = iRestDom;
11126 //MESSAGE("Number of shared faces " << faceDomains.size());
11127 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11129 // --- explore the shared faces domain by domain,
11130 // explore the nodes of the face and see if they belong to a cell in the domain,
11131 // which has only a node or an edge on the border (not a shared face)
11133 for (int idomain = idom0; idomain < nbDomains; idomain++)
11135 //MESSAGE("Domain " << idomain);
11136 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11137 itface = faceDomains.begin();
11138 for (; itface != faceDomains.end(); ++itface)
11140 const std::map<int, int>& domvol = itface->second;
11141 if (!domvol.count(idomain))
11143 DownIdType face = itface->first;
11144 //MESSAGE(" --- face " << face.cellId);
11145 std::set<int> oldNodes;
11147 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11148 std::set<int>::iterator itn = oldNodes.begin();
11149 for (; itn != oldNodes.end(); ++itn)
11152 //MESSAGE(" node " << oldId);
11153 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11154 for (int i=0; i<l.ncells; i++)
11156 int vtkId = l.cells[i];
11157 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11158 if (!domain.count(anElem))
11160 int vtkType = grid->GetCellType(vtkId);
11161 int downId = grid->CellIdToDownId(vtkId);
11164 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11165 continue; // not OK at this stage of the algorithm:
11166 //no cells created after BuildDownWardConnectivity
11168 DownIdType aCell(downId, vtkType);
11169 cellDomains[aCell][idomain] = vtkId;
11170 celldom[vtkId] = idomain;
11171 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11177 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11178 // for each shared face, get the nodes
11179 // for each node, for each domain of the face, create a clone of the node
11181 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11182 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11183 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11185 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11186 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11187 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11189 //MESSAGE(".. Duplication of the nodes");
11190 for (int idomain = idom0; idomain < nbDomains; idomain++)
11192 itface = faceDomains.begin();
11193 for (; itface != faceDomains.end(); ++itface)
11195 const std::map<int, int>& domvol = itface->second;
11196 if (!domvol.count(idomain))
11198 DownIdType face = itface->first;
11199 //MESSAGE(" --- face " << face.cellId);
11200 std::set<int> oldNodes;
11202 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11203 std::set<int>::iterator itn = oldNodes.begin();
11204 for (; itn != oldNodes.end(); ++itn)
11207 if (nodeDomains[oldId].empty())
11209 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11210 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11212 std::map<int, int>::const_iterator itdom = domvol.begin();
11213 for (; itdom != domvol.end(); ++itdom)
11215 int idom = itdom->first;
11216 //MESSAGE(" domain " << idom);
11217 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11219 if (nodeDomains[oldId].size() >= 2) // a multiple node
11221 vector<int> orderedDoms;
11222 //MESSAGE("multiple node " << oldId);
11223 if (mutipleNodes.count(oldId))
11224 orderedDoms = mutipleNodes[oldId];
11227 map<int,int>::iterator it = nodeDomains[oldId].begin();
11228 for (; it != nodeDomains[oldId].end(); ++it)
11229 orderedDoms.push_back(it->first);
11231 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11232 //stringstream txt;
11233 //for (int i=0; i<orderedDoms.size(); i++)
11234 // txt << orderedDoms[i] << " ";
11235 //MESSAGE("orderedDoms " << txt.str());
11236 mutipleNodes[oldId] = orderedDoms;
11238 double *coords = grid->GetPoint(oldId);
11239 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11240 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11241 int newId = newNode->GetVtkID();
11242 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11243 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11250 //MESSAGE(".. Creation of elements");
11251 for (int idomain = idom0; idomain < nbDomains; idomain++)
11253 itface = faceDomains.begin();
11254 for (; itface != faceDomains.end(); ++itface)
11256 std::map<int, int> domvol = itface->second;
11257 if (!domvol.count(idomain))
11259 DownIdType face = itface->first;
11260 //MESSAGE(" --- face " << face.cellId);
11261 std::set<int> oldNodes;
11263 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11264 int nbMultipleNodes = 0;
11265 std::set<int>::iterator itn = oldNodes.begin();
11266 for (; itn != oldNodes.end(); ++itn)
11269 if (mutipleNodes.count(oldId))
11272 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11274 //MESSAGE("multiple Nodes detected on a shared face");
11275 int downId = itface->first.cellId;
11276 unsigned char cellType = itface->first.cellType;
11277 // --- shared edge or shared face ?
11278 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11281 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11282 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11283 if (mutipleNodes.count(nodes[i]))
11284 if (!mutipleNodesToFace.count(nodes[i]))
11285 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11287 else // shared face (between two volumes)
11289 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11290 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11291 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11292 for (int ie =0; ie < nbEdges; ie++)
11295 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11296 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11298 vector<int> vn0 = mutipleNodes[nodes[0]];
11299 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11301 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11302 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11303 if ( vn0[i0] == vn1[i1] )
11304 doms.push_back( vn0[ i0 ]);
11305 if ( doms.size() > 2 )
11307 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11308 double *coords = grid->GetPoint(nodes[0]);
11309 gp_Pnt p0(coords[0], coords[1], coords[2]);
11310 coords = grid->GetPoint(nodes[nbNodes - 1]);
11311 gp_Pnt p1(coords[0], coords[1], coords[2]);
11313 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11314 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11315 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11316 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11317 for ( size_t id = 0; id < doms.size(); id++ )
11319 int idom = doms[id];
11320 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11321 for ( int ivol = 0; ivol < nbvol; ivol++ )
11323 int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11324 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11325 if (domain.count(elem))
11327 const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11328 domvol[idom] = (SMDS_MeshVolume*) svol;
11329 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11330 double values[3] = { 0,0,0 };
11331 vtkIdType npts = 0;
11332 vtkIdType* pts = 0;
11333 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11334 for ( vtkIdType i = 0; i < npts; ++i )
11336 double *coords = grid->GetPoint( pts[i] );
11337 for ( int j = 0; j < 3; ++j )
11338 values[j] += coords[j] / npts;
11342 gref.SetCoord( values[0], values[1], values[2] );
11343 angleDom[idom] = 0;
11347 gp_Pnt g( values[0], values[1], values[2] );
11348 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11349 //MESSAGE(" angle=" << angleDom[idom]);
11355 map<double, int> sortedDom; // sort domains by angle
11356 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11357 sortedDom[ia->second] = ia->first;
11358 vector<int> vnodes;
11360 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11362 vdom.push_back(ib->second);
11363 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11365 for (int ino = 0; ino < nbNodes; ino++)
11366 vnodes.push_back(nodes[ino]);
11367 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11376 // --- iterate on shared faces (volumes to modify, face to extrude)
11377 // get node id's of the face (id SMDS = id VTK)
11378 // create flat element with old and new nodes if requested
11380 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11381 // (domain1 X domain2) = domain1 + MAXINT*domain2
11383 std::map<int, std::map<long,int> > nodeQuadDomains;
11384 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11386 //MESSAGE(".. Creation of elements: simple junction");
11387 if (createJointElems)
11389 string joints2DName = "joints2D";
11390 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11391 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11392 string joints3DName = "joints3D";
11393 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11394 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11396 itface = faceDomains.begin();
11397 for (; itface != faceDomains.end(); ++itface)
11399 DownIdType face = itface->first;
11400 std::set<int> oldNodes;
11401 std::set<int>::iterator itn;
11403 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11405 std::map<int, int> domvol = itface->second;
11406 std::map<int, int>::iterator itdom = domvol.begin();
11407 int dom1 = itdom->first;
11408 int vtkVolId = itdom->second;
11410 int dom2 = itdom->first;
11411 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11413 stringstream grpname;
11416 grpname << dom1 << "_" << dom2;
11418 grpname << dom2 << "_" << dom1;
11419 string namegrp = grpname.str();
11420 if (!mapOfJunctionGroups.count(namegrp))
11421 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11422 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11424 sgrp->Add(vol->GetID());
11425 if (vol->GetType() == SMDSAbs_Volume)
11426 joints3DGrp->Add(vol->GetID());
11427 else if (vol->GetType() == SMDSAbs_Face)
11428 joints2DGrp->Add(vol->GetID());
11432 // --- create volumes on multiple domain intersection if requested
11433 // iterate on mutipleNodesToFace
11434 // iterate on edgesMultiDomains
11436 //MESSAGE(".. Creation of elements: multiple junction");
11437 if (createJointElems)
11439 // --- iterate on mutipleNodesToFace
11441 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11442 for (; itn != mutipleNodesToFace.end(); ++itn)
11444 int node = itn->first;
11445 vector<int> orderDom = itn->second;
11446 vector<vtkIdType> orderedNodes;
11447 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11448 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11449 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11451 stringstream grpname;
11453 grpname << 0 << "_" << 0;
11454 string namegrp = grpname.str();
11455 if (!mapOfJunctionGroups.count(namegrp))
11456 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11457 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11459 sgrp->Add(face->GetID());
11462 // --- iterate on edgesMultiDomains
11464 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11465 for (; ite != edgesMultiDomains.end(); ++ite)
11467 vector<int> nodes = ite->first;
11468 vector<int> orderDom = ite->second;
11469 vector<vtkIdType> orderedNodes;
11470 if (nodes.size() == 2)
11472 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11473 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11474 if ( orderDom.size() == 3 )
11475 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11476 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11478 for (int idom = orderDom.size()-1; idom >=0; idom--)
11479 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11480 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11482 string namegrp = "jointsMultiples";
11483 if (!mapOfJunctionGroups.count(namegrp))
11484 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11485 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11487 sgrp->Add(vol->GetID());
11491 //INFOS("Quadratic multiple joints not implemented");
11492 // TODO quadratic nodes
11497 // --- list the explicit faces and edges of the mesh that need to be modified,
11498 // i.e. faces and edges built with one or more duplicated nodes.
11499 // associate these faces or edges to their corresponding domain.
11500 // only the first domain found is kept when a face or edge is shared
11502 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11503 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11504 faceOrEdgeDom.clear();
11507 //MESSAGE(".. Modification of elements");
11508 for (int idomain = idom0; idomain < nbDomains; idomain++)
11510 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11511 for (; itnod != nodeDomains.end(); ++itnod)
11513 int oldId = itnod->first;
11514 //MESSAGE(" node " << oldId);
11515 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11516 for (int i = 0; i < l.ncells; i++)
11518 int vtkId = l.cells[i];
11519 int vtkType = grid->GetCellType(vtkId);
11520 int downId = grid->CellIdToDownId(vtkId);
11522 continue; // new cells: not to be modified
11523 DownIdType aCell(downId, vtkType);
11524 int volParents[1000];
11525 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11526 for (int j = 0; j < nbvol; j++)
11527 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11528 if (!feDom.count(vtkId))
11530 feDom[vtkId] = idomain;
11531 faceOrEdgeDom[aCell] = emptyMap;
11532 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11533 //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11534 // << " type " << vtkType << " downId " << downId);
11540 // --- iterate on shared faces (volumes to modify, face to extrude)
11541 // get node id's of the face
11542 // replace old nodes by new nodes in volumes, and update inverse connectivity
11544 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11545 for (int m=0; m<3; m++)
11547 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11548 itface = (*amap).begin();
11549 for (; itface != (*amap).end(); ++itface)
11551 DownIdType face = itface->first;
11552 std::set<int> oldNodes;
11553 std::set<int>::iterator itn;
11555 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11556 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11557 std::map<int, int> localClonedNodeIds;
11559 std::map<int, int> domvol = itface->second;
11560 std::map<int, int>::iterator itdom = domvol.begin();
11561 for (; itdom != domvol.end(); ++itdom)
11563 int idom = itdom->first;
11564 int vtkVolId = itdom->second;
11565 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11566 localClonedNodeIds.clear();
11567 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11570 if (nodeDomains[oldId].count(idom))
11572 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11573 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11576 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11581 // Remove empty groups (issue 0022812)
11582 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11583 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11585 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11586 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11589 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11590 grid->DeleteLinks();
11598 * \brief Double nodes on some external faces and create flat elements.
11599 * Flat elements are mainly used by some types of mechanic calculations.
11601 * Each group of the list must be constituted of faces.
11602 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11603 * @param theElems - list of groups of faces, where a group of faces is a set of
11604 * SMDS_MeshElements sorted by Id.
11605 * @return TRUE if operation has been completed successfully, FALSE otherwise
11607 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11609 // MESSAGE("-------------------------------------------------");
11610 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11611 // MESSAGE("-------------------------------------------------");
11613 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11615 // --- For each group of faces
11616 // duplicate the nodes, create a flat element based on the face
11617 // replace the nodes of the faces by their clones
11619 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11620 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11621 clonedNodes.clear();
11622 intermediateNodes.clear();
11623 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11624 mapOfJunctionGroups.clear();
11626 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11628 const TIDSortedElemSet& domain = theElems[idom];
11629 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11630 for ( ; elemItr != domain.end(); ++elemItr )
11632 const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11635 // MESSAGE("aFace=" << aFace->GetID());
11636 bool isQuad = aFace->IsQuadratic();
11637 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11639 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11641 SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11642 while (nodeIt->more())
11644 const SMDS_MeshNode* node = nodeIt->next();
11645 bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11647 ln2.push_back(node);
11649 ln0.push_back(node);
11651 const SMDS_MeshNode* clone = 0;
11652 if (!clonedNodes.count(node))
11654 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11655 copyPosition( node, clone );
11656 clonedNodes[node] = clone;
11659 clone = clonedNodes[node];
11662 ln3.push_back(clone);
11664 ln1.push_back(clone);
11666 const SMDS_MeshNode* inter = 0;
11667 if (isQuad && (!isMedium))
11669 if (!intermediateNodes.count(node))
11671 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11672 copyPosition( node, inter );
11673 intermediateNodes[node] = inter;
11676 inter = intermediateNodes[node];
11677 ln4.push_back(inter);
11681 // --- extrude the face
11683 vector<const SMDS_MeshNode*> ln;
11684 SMDS_MeshVolume* vol = 0;
11685 vtkIdType aType = aFace->GetVtkType();
11689 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11690 // MESSAGE("vol prism " << vol->GetID());
11691 ln.push_back(ln1[0]);
11692 ln.push_back(ln1[1]);
11693 ln.push_back(ln1[2]);
11696 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11697 // MESSAGE("vol hexa " << vol->GetID());
11698 ln.push_back(ln1[0]);
11699 ln.push_back(ln1[1]);
11700 ln.push_back(ln1[2]);
11701 ln.push_back(ln1[3]);
11703 case VTK_QUADRATIC_TRIANGLE:
11704 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11705 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11706 // MESSAGE("vol quad prism " << vol->GetID());
11707 ln.push_back(ln1[0]);
11708 ln.push_back(ln1[1]);
11709 ln.push_back(ln1[2]);
11710 ln.push_back(ln3[0]);
11711 ln.push_back(ln3[1]);
11712 ln.push_back(ln3[2]);
11714 case VTK_QUADRATIC_QUAD:
11715 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11716 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11717 // ln4[0], ln4[1], ln4[2], ln4[3]);
11718 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11719 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11720 ln4[0], ln4[1], ln4[2], ln4[3]);
11721 // MESSAGE("vol quad hexa " << vol->GetID());
11722 ln.push_back(ln1[0]);
11723 ln.push_back(ln1[1]);
11724 ln.push_back(ln1[2]);
11725 ln.push_back(ln1[3]);
11726 ln.push_back(ln3[0]);
11727 ln.push_back(ln3[1]);
11728 ln.push_back(ln3[2]);
11729 ln.push_back(ln3[3]);
11739 stringstream grpname;
11742 string namegrp = grpname.str();
11743 if (!mapOfJunctionGroups.count(namegrp))
11744 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11745 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11747 sgrp->Add(vol->GetID());
11750 // --- modify the face
11752 const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11759 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11760 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11761 * groups of faces to remove inside the object, (idem edges).
11762 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11764 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11765 const TopoDS_Shape& theShape,
11766 SMESH_NodeSearcher* theNodeSearcher,
11767 const char* groupName,
11768 std::vector<double>& nodesCoords,
11769 std::vector<std::vector<int> >& listOfListOfNodes)
11771 // MESSAGE("--------------------------------");
11772 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11773 // MESSAGE("--------------------------------");
11775 // --- zone of volumes to remove is given :
11776 // 1 either by a geom shape (one or more vertices) and a radius,
11777 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11778 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11779 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11780 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11781 // defined by it's name.
11783 SMESHDS_GroupBase* groupDS = 0;
11784 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11785 while ( groupIt->more() )
11788 SMESH_Group * group = groupIt->next();
11789 if ( !group ) continue;
11790 groupDS = group->GetGroupDS();
11791 if ( !groupDS || groupDS->IsEmpty() ) continue;
11792 std::string grpName = group->GetName();
11793 //MESSAGE("grpName=" << grpName);
11794 if (grpName == groupName)
11800 bool isNodeGroup = false;
11801 bool isNodeCoords = false;
11804 if (groupDS->GetType() != SMDSAbs_Node)
11806 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11809 if (nodesCoords.size() > 0)
11810 isNodeCoords = true; // a list o nodes given by their coordinates
11811 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11813 // --- define groups to build
11815 // --- group of SMDS volumes
11816 string grpvName = groupName;
11817 grpvName += "_vol";
11818 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11821 MESSAGE("group not created " << grpvName);
11824 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11826 // --- group of SMDS faces on the skin
11827 string grpsName = groupName;
11828 grpsName += "_skin";
11829 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11832 MESSAGE("group not created " << grpsName);
11835 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11837 // --- group of SMDS faces internal (several shapes)
11838 string grpiName = groupName;
11839 grpiName += "_internalFaces";
11840 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11843 MESSAGE("group not created " << grpiName);
11846 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11848 // --- group of SMDS faces internal (several shapes)
11849 string grpeiName = groupName;
11850 grpeiName += "_internalEdges";
11851 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11854 MESSAGE("group not created " << grpeiName);
11857 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11859 // --- build downward connectivity
11861 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11862 meshDS->BuildDownWardConnectivity(true);
11863 SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11865 // --- set of volumes detected inside
11867 std::set<int> setOfInsideVol;
11868 std::set<int> setOfVolToCheck;
11870 std::vector<gp_Pnt> gpnts;
11873 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11875 //MESSAGE("group of nodes provided");
11876 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11877 while ( elemIt->more() )
11879 const SMDS_MeshElement* elem = elemIt->next();
11882 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11885 SMDS_MeshElement* vol = 0;
11886 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11887 while (volItr->more())
11889 vol = (SMDS_MeshElement*)volItr->next();
11890 setOfInsideVol.insert(vol->GetVtkID());
11891 sgrp->Add(vol->GetID());
11895 else if (isNodeCoords)
11897 //MESSAGE("list of nodes coordinates provided");
11900 while ( i < nodesCoords.size()-2 )
11902 double x = nodesCoords[i++];
11903 double y = nodesCoords[i++];
11904 double z = nodesCoords[i++];
11905 gp_Pnt p = gp_Pnt(x, y ,z);
11906 gpnts.push_back(p);
11907 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11911 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11913 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11914 TopTools_IndexedMapOfShape vertexMap;
11915 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11916 gp_Pnt p = gp_Pnt(0,0,0);
11917 if (vertexMap.Extent() < 1)
11920 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11922 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11923 p = BRep_Tool::Pnt(vertex);
11924 gpnts.push_back(p);
11925 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11929 if (gpnts.size() > 0)
11931 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11932 //MESSAGE("startNode->nodeId " << nodeId);
11934 double radius2 = radius*radius;
11935 //MESSAGE("radius2 " << radius2);
11937 // --- volumes on start node
11939 setOfVolToCheck.clear();
11940 SMDS_MeshElement* startVol = 0;
11941 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11942 while (volItr->more())
11944 startVol = (SMDS_MeshElement*)volItr->next();
11945 setOfVolToCheck.insert(startVol->GetVtkID());
11947 if (setOfVolToCheck.empty())
11949 MESSAGE("No volumes found");
11953 // --- starting with central volumes then their neighbors, check if they are inside
11954 // or outside the domain, until no more new neighbor volume is inside.
11955 // Fill the group of inside volumes
11957 std::map<int, double> mapOfNodeDistance2;
11958 mapOfNodeDistance2.clear();
11959 std::set<int> setOfOutsideVol;
11960 while (!setOfVolToCheck.empty())
11962 std::set<int>::iterator it = setOfVolToCheck.begin();
11964 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
11965 bool volInside = false;
11966 vtkIdType npts = 0;
11967 vtkIdType* pts = 0;
11968 grid->GetCellPoints(vtkId, npts, pts);
11969 for (int i=0; i<npts; i++)
11971 double distance2 = 0;
11972 if (mapOfNodeDistance2.count(pts[i]))
11974 distance2 = mapOfNodeDistance2[pts[i]];
11975 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
11979 double *coords = grid->GetPoint(pts[i]);
11980 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11982 for ( size_t j = 0; j < gpnts.size(); j++ )
11984 double d2 = aPoint.SquareDistance( gpnts[ j ]);
11985 if (d2 < distance2)
11988 if (distance2 < radius2)
11992 mapOfNodeDistance2[pts[i]] = distance2;
11993 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
11995 if (distance2 < radius2)
11997 volInside = true; // one or more nodes inside the domain
11998 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12004 setOfInsideVol.insert(vtkId);
12005 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12006 int neighborsVtkIds[NBMAXNEIGHBORS];
12007 int downIds[NBMAXNEIGHBORS];
12008 unsigned char downTypes[NBMAXNEIGHBORS];
12009 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12010 for (int n = 0; n < nbNeighbors; n++)
12011 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12012 setOfVolToCheck.insert(neighborsVtkIds[n]);
12016 setOfOutsideVol.insert(vtkId);
12017 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12019 setOfVolToCheck.erase(vtkId);
12023 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12024 // If yes, add the volume to the inside set
12026 bool addedInside = true;
12027 std::set<int> setOfVolToReCheck;
12028 while (addedInside)
12030 //MESSAGE(" --------------------------- re check");
12031 addedInside = false;
12032 std::set<int>::iterator itv = setOfInsideVol.begin();
12033 for (; itv != setOfInsideVol.end(); ++itv)
12036 int neighborsVtkIds[NBMAXNEIGHBORS];
12037 int downIds[NBMAXNEIGHBORS];
12038 unsigned char downTypes[NBMAXNEIGHBORS];
12039 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12040 for (int n = 0; n < nbNeighbors; n++)
12041 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12042 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12044 setOfVolToCheck = setOfVolToReCheck;
12045 setOfVolToReCheck.clear();
12046 while (!setOfVolToCheck.empty())
12048 std::set<int>::iterator it = setOfVolToCheck.begin();
12050 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12052 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12053 int countInside = 0;
12054 int neighborsVtkIds[NBMAXNEIGHBORS];
12055 int downIds[NBMAXNEIGHBORS];
12056 unsigned char downTypes[NBMAXNEIGHBORS];
12057 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12058 for (int n = 0; n < nbNeighbors; n++)
12059 if (setOfInsideVol.count(neighborsVtkIds[n]))
12061 //MESSAGE("countInside " << countInside);
12062 if (countInside > 1)
12064 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12065 setOfInsideVol.insert(vtkId);
12066 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12067 addedInside = true;
12070 setOfVolToReCheck.insert(vtkId);
12072 setOfVolToCheck.erase(vtkId);
12076 // --- map of Downward faces at the boundary, inside the global volume
12077 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12078 // fill group of SMDS faces inside the volume (when several volume shapes)
12079 // fill group of SMDS faces on the skin of the global volume (if skin)
12081 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12082 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12083 std::set<int>::iterator it = setOfInsideVol.begin();
12084 for (; it != setOfInsideVol.end(); ++it)
12087 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12088 int neighborsVtkIds[NBMAXNEIGHBORS];
12089 int downIds[NBMAXNEIGHBORS];
12090 unsigned char downTypes[NBMAXNEIGHBORS];
12091 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12092 for (int n = 0; n < nbNeighbors; n++)
12094 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12095 if (neighborDim == 3)
12097 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12099 DownIdType face(downIds[n], downTypes[n]);
12100 boundaryFaces[face] = vtkId;
12102 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12103 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12104 if (vtkFaceId >= 0)
12106 sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12107 // find also the smds edges on this face
12108 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12109 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12110 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12111 for (int i = 0; i < nbEdges; i++)
12113 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12114 if (vtkEdgeId >= 0)
12115 sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12119 else if (neighborDim == 2) // skin of the volume
12121 DownIdType face(downIds[n], downTypes[n]);
12122 skinFaces[face] = vtkId;
12123 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12124 if (vtkFaceId >= 0)
12125 sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12130 // --- identify the edges constituting the wire of each subshape on the skin
12131 // define polylines with the nodes of edges, equivalent to wires
12132 // project polylines on subshapes, and partition, to get geom faces
12134 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12135 std::set<int> emptySet;
12137 std::set<int> shapeIds;
12139 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12140 while (itelem->more())
12142 const SMDS_MeshElement *elem = itelem->next();
12143 int shapeId = elem->getshapeId();
12144 int vtkId = elem->GetVtkID();
12145 if (!shapeIdToVtkIdSet.count(shapeId))
12147 shapeIdToVtkIdSet[shapeId] = emptySet;
12148 shapeIds.insert(shapeId);
12150 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12153 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12154 std::set<DownIdType, DownIdCompare> emptyEdges;
12155 emptyEdges.clear();
12157 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12158 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12160 int shapeId = itShape->first;
12161 //MESSAGE(" --- Shape ID --- "<< shapeId);
12162 shapeIdToEdges[shapeId] = emptyEdges;
12164 std::vector<int> nodesEdges;
12166 std::set<int>::iterator its = itShape->second.begin();
12167 for (; its != itShape->second.end(); ++its)
12170 //MESSAGE(" " << vtkId);
12171 int neighborsVtkIds[NBMAXNEIGHBORS];
12172 int downIds[NBMAXNEIGHBORS];
12173 unsigned char downTypes[NBMAXNEIGHBORS];
12174 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12175 for (int n = 0; n < nbNeighbors; n++)
12177 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12179 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12180 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12181 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12183 DownIdType edge(downIds[n], downTypes[n]);
12184 if (!shapeIdToEdges[shapeId].count(edge))
12186 shapeIdToEdges[shapeId].insert(edge);
12188 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12189 nodesEdges.push_back(vtkNodeId[0]);
12190 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12191 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12197 std::list<int> order;
12199 if (nodesEdges.size() > 0)
12201 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12202 nodesEdges[0] = -1;
12203 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12204 nodesEdges[1] = -1; // do not reuse this edge
12208 int nodeTofind = order.back(); // try first to push back
12210 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12211 if (nodesEdges[i] == nodeTofind)
12213 if ( i == (int) nodesEdges.size() )
12214 found = false; // no follower found on back
12217 if (i%2) // odd ==> use the previous one
12218 if (nodesEdges[i-1] < 0)
12222 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12223 nodesEdges[i-1] = -1;
12225 else // even ==> use the next one
12226 if (nodesEdges[i+1] < 0)
12230 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12231 nodesEdges[i+1] = -1;
12236 // try to push front
12238 nodeTofind = order.front(); // try to push front
12239 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12240 if ( nodesEdges[i] == nodeTofind )
12242 if ( i == (int)nodesEdges.size() )
12244 found = false; // no predecessor found on front
12247 if (i%2) // odd ==> use the previous one
12248 if (nodesEdges[i-1] < 0)
12252 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12253 nodesEdges[i-1] = -1;
12255 else // even ==> use the next one
12256 if (nodesEdges[i+1] < 0)
12260 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12261 nodesEdges[i+1] = -1;
12267 std::vector<int> nodes;
12268 nodes.push_back(shapeId);
12269 std::list<int>::iterator itl = order.begin();
12270 for (; itl != order.end(); itl++)
12272 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12273 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12275 listOfListOfNodes.push_back(nodes);
12278 // partition geom faces with blocFissure
12279 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12280 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12286 //================================================================================
12288 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12289 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12290 * \return TRUE if operation has been completed successfully, FALSE otherwise
12292 //================================================================================
12294 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12296 // iterates on volume elements and detect all free faces on them
12297 SMESHDS_Mesh* aMesh = GetMeshDS();
12301 ElemFeatures faceType( SMDSAbs_Face );
12302 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12303 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12306 const SMDS_MeshVolume* volume = vIt->next();
12307 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12308 vTool.SetExternalNormal();
12309 const int iQuad = volume->IsQuadratic();
12310 faceType.SetQuad( iQuad );
12311 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12313 if (!vTool.IsFreeFace(iface))
12316 vector<const SMDS_MeshNode *> nodes;
12317 int nbFaceNodes = vTool.NbFaceNodes(iface);
12318 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12320 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12321 nodes.push_back(faceNodes[inode]);
12323 if (iQuad) // add medium nodes
12325 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12326 nodes.push_back(faceNodes[inode]);
12327 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12328 nodes.push_back(faceNodes[8]);
12330 // add new face based on volume nodes
12331 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12333 nbExisted++; // face already exists
12337 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12342 return ( nbFree == ( nbExisted + nbCreated ));
12347 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12349 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12351 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12354 //================================================================================
12356 * \brief Creates missing boundary elements
12357 * \param elements - elements whose boundary is to be checked
12358 * \param dimension - defines type of boundary elements to create
12359 * \param group - a group to store created boundary elements in
12360 * \param targetMesh - a mesh to store created boundary elements in
12361 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12362 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12363 * boundary elements will be copied into the targetMesh
12364 * \param toAddExistingBondary - if true, not only new but also pre-existing
12365 * boundary elements will be added into the new group
12366 * \param aroundElements - if true, elements will be created on boundary of given
12367 * elements else, on boundary of the whole mesh.
12368 * \return nb of added boundary elements
12370 //================================================================================
12372 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12373 Bnd_Dimension dimension,
12374 SMESH_Group* group/*=0*/,
12375 SMESH_Mesh* targetMesh/*=0*/,
12376 bool toCopyElements/*=false*/,
12377 bool toCopyExistingBoundary/*=false*/,
12378 bool toAddExistingBondary/*= false*/,
12379 bool aroundElements/*= false*/)
12381 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12382 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12383 // hope that all elements are of the same type, do not check them all
12384 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12385 throw SALOME_Exception(LOCALIZED("wrong element type"));
12388 toCopyElements = toCopyExistingBoundary = false;
12390 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12391 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12392 int nbAddedBnd = 0;
12394 // editor adding present bnd elements and optionally holding elements to add to the group
12395 SMESH_MeshEditor* presentEditor;
12396 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12397 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12399 SMESH_MesherHelper helper( *myMesh );
12400 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12401 SMDS_VolumeTool vTool;
12402 TIDSortedElemSet avoidSet;
12403 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12406 typedef vector<const SMDS_MeshNode*> TConnectivity;
12407 TConnectivity tgtNodes;
12408 ElemFeatures elemKind( missType ), elemToCopy;
12410 vector<const SMDS_MeshElement*> presentBndElems;
12411 vector<TConnectivity> missingBndElems;
12412 vector<int> freeFacets;
12413 TConnectivity nodes, elemNodes;
12415 SMDS_ElemIteratorPtr eIt;
12416 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12417 else eIt = SMESHUtils::elemSetIterator( elements );
12419 while ( eIt->more() )
12421 const SMDS_MeshElement* elem = eIt->next();
12422 const int iQuad = elem->IsQuadratic();
12423 elemKind.SetQuad( iQuad );
12425 // ------------------------------------------------------------------------------------
12426 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12427 // ------------------------------------------------------------------------------------
12428 presentBndElems.clear();
12429 missingBndElems.clear();
12430 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12431 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12433 const SMDS_MeshElement* otherVol = 0;
12434 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12436 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12437 ( !aroundElements || elements.count( otherVol )))
12439 freeFacets.push_back( iface );
12441 if ( missType == SMDSAbs_Face )
12442 vTool.SetExternalNormal();
12443 for ( size_t i = 0; i < freeFacets.size(); ++i )
12445 int iface = freeFacets[i];
12446 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12447 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12448 if ( missType == SMDSAbs_Edge ) // boundary edges
12450 nodes.resize( 2+iQuad );
12451 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12453 for ( size_t j = 0; j < nodes.size(); ++j )
12454 nodes[ j ] = nn[ i+j ];
12455 if ( const SMDS_MeshElement* edge =
12456 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12457 presentBndElems.push_back( edge );
12459 missingBndElems.push_back( nodes );
12462 else // boundary face
12465 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12466 nodes.push_back( nn[inode] ); // add corner nodes
12468 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12469 nodes.push_back( nn[inode] ); // add medium nodes
12470 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12472 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12474 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12475 SMDSAbs_Face, /*noMedium=*/false ))
12476 presentBndElems.push_back( f );
12478 missingBndElems.push_back( nodes );
12480 if ( targetMesh != myMesh )
12482 // add 1D elements on face boundary to be added to a new mesh
12483 const SMDS_MeshElement* edge;
12484 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12487 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12489 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12490 if ( edge && avoidSet.insert( edge ).second )
12491 presentBndElems.push_back( edge );
12497 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12499 avoidSet.clear(), avoidSet.insert( elem );
12500 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12501 SMDS_MeshElement::iterator() );
12502 elemNodes.push_back( elemNodes[0] );
12503 nodes.resize( 2 + iQuad );
12504 const int nbLinks = elem->NbCornerNodes();
12505 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12507 nodes[0] = elemNodes[iN];
12508 nodes[1] = elemNodes[iN+1+iQuad];
12509 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12510 continue; // not free link
12512 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12513 if ( const SMDS_MeshElement* edge =
12514 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12515 presentBndElems.push_back( edge );
12517 missingBndElems.push_back( nodes );
12521 // ---------------------------------
12522 // 2. Add missing boundary elements
12523 // ---------------------------------
12524 if ( targetMesh != myMesh )
12525 // instead of making a map of nodes in this mesh and targetMesh,
12526 // we create nodes with same IDs.
12527 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12529 TConnectivity& srcNodes = missingBndElems[i];
12530 tgtNodes.resize( srcNodes.size() );
12531 for ( inode = 0; inode < srcNodes.size(); ++inode )
12532 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12533 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12535 /*noMedium=*/false))
12537 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12541 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12543 TConnectivity& nodes = missingBndElems[ i ];
12544 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12546 /*noMedium=*/false))
12548 SMDS_MeshElement* newElem =
12549 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12550 nbAddedBnd += bool( newElem );
12552 // try to set a new element to a shape
12553 if ( myMesh->HasShapeToMesh() )
12556 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12557 const size_t nbN = nodes.size() / (iQuad+1 );
12558 for ( inode = 0; inode < nbN && ok; ++inode )
12560 pair<int, TopAbs_ShapeEnum> i_stype =
12561 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12562 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12563 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12565 if ( ok && mediumShapes.size() > 1 )
12567 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12568 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12569 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12571 if (( ok = ( stype_i->first != stype_i_0.first )))
12572 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12573 aMesh->IndexToShape( stype_i_0.second ));
12576 if ( ok && mediumShapes.begin()->first == missShapeType )
12577 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12581 // ----------------------------------
12582 // 3. Copy present boundary elements
12583 // ----------------------------------
12584 if ( toCopyExistingBoundary )
12585 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12587 const SMDS_MeshElement* e = presentBndElems[i];
12588 tgtNodes.resize( e->NbNodes() );
12589 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12590 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12591 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12593 else // store present elements to add them to a group
12594 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12596 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12599 } // loop on given elements
12601 // ---------------------------------------------
12602 // 4. Fill group with boundary elements
12603 // ---------------------------------------------
12606 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12607 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12608 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12610 tgtEditor.myLastCreatedElems.clear();
12611 tgtEditor2.myLastCreatedElems.clear();
12613 // -----------------------
12614 // 5. Copy given elements
12615 // -----------------------
12616 if ( toCopyElements && targetMesh != myMesh )
12618 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12619 else eIt = SMESHUtils::elemSetIterator( elements );
12620 while (eIt->more())
12622 const SMDS_MeshElement* elem = eIt->next();
12623 tgtNodes.resize( elem->NbNodes() );
12624 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12625 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12626 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12628 tgtEditor.myLastCreatedElems.clear();
12634 //================================================================================
12636 * \brief Copy node position and set \a to node on the same geometry
12638 //================================================================================
12640 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12641 const SMDS_MeshNode* to )
12643 if ( !from || !to ) return;
12645 SMDS_PositionPtr pos = from->GetPosition();
12646 if ( !pos || from->getshapeId() < 1 ) return;
12648 switch ( pos->GetTypeOfPosition() )
12650 case SMDS_TOP_3DSPACE: break;
12652 case SMDS_TOP_FACE:
12654 SMDS_FacePositionPtr fPos = pos;
12655 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12656 fPos->GetUParameter(), fPos->GetVParameter() );
12659 case SMDS_TOP_EDGE:
12661 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12662 SMDS_EdgePositionPtr ePos = pos;
12663 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12666 case SMDS_TOP_VERTEX:
12668 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12671 case SMDS_TOP_UNSPEC: