1 // Copyright (C) 2007-2019 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
49 #include "utilities.h"
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_ListOfInteger.hxx>
66 #include <TopAbs_State.hxx>
68 #include <TopExp_Explorer.hxx>
69 #include <TopTools_ListIteratorOfListOfShape.hxx>
70 #include <TopTools_ListOfShape.hxx>
71 #include <TopTools_SequenceOfShape.hxx>
73 #include <TopoDS_Edge.hxx>
74 #include <TopoDS_Face.hxx>
75 #include <TopoDS_Solid.hxx>
81 #include <gp_Trsf.hxx>
95 #include <boost/tuple/tuple.hpp>
96 #include <boost/container/flat_set.hpp>
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
101 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
103 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
106 using namespace SMESH::Controls;
108 //=======================================================================
109 //function : SMESH_MeshEditor
111 //=======================================================================
113 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
114 :myMesh( theMesh ) // theMesh may be NULL
118 //================================================================================
120 * \brief Return mesh DS
122 //================================================================================
124 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
126 return myMesh->GetMeshDS();
130 //================================================================================
132 * \brief Clears myLastCreatedNodes and myLastCreatedElems
134 //================================================================================
136 void SMESH_MeshEditor::ClearLastCreated()
138 SMESHUtils::FreeVector( myLastCreatedElems );
139 SMESHUtils::FreeVector( myLastCreatedNodes );
142 //================================================================================
144 * \brief Initializes members by an existing element
145 * \param [in] elem - the source element
146 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
148 //================================================================================
150 SMESH_MeshEditor::ElemFeatures&
151 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
155 myType = elem->GetType();
156 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
158 myIsPoly = elem->IsPoly();
161 myIsQuad = elem->IsQuadratic();
162 if ( myType == SMDSAbs_Volume && !basicOnly )
164 myPolyhedQuantities = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
168 else if ( myType == SMDSAbs_Ball && !basicOnly )
170 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
176 //=======================================================================
180 //=======================================================================
183 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
184 const ElemFeatures& features)
186 SMDS_MeshElement* e = 0;
187 int nbnode = node.size();
188 SMESHDS_Mesh* mesh = GetMeshDS();
189 const int ID = features.myID;
191 switch ( features.myType ) {
193 if ( !features.myIsPoly ) {
195 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
196 else e = mesh->AddFace (node[0], node[1], node[2] );
198 else if (nbnode == 4) {
199 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
200 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
202 else if (nbnode == 6) {
203 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
204 node[4], node[5], ID);
205 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
208 else if (nbnode == 7) {
209 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
210 node[4], node[5], node[6], ID);
211 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
212 node[4], node[5], node[6] );
214 else if (nbnode == 8) {
215 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
216 node[4], node[5], node[6], node[7], ID);
217 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
218 node[4], node[5], node[6], node[7] );
220 else if (nbnode == 9) {
221 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
222 node[4], node[5], node[6], node[7], node[8], ID);
223 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
224 node[4], node[5], node[6], node[7], node[8] );
227 else if ( !features.myIsQuad )
229 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
230 else e = mesh->AddPolygonalFace (node );
232 else if ( nbnode % 2 == 0 ) // just a protection
234 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
235 else e = mesh->AddQuadPolygonalFace (node );
240 if ( !features.myIsPoly ) {
242 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
243 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
245 else if (nbnode == 5) {
246 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
248 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
251 else if (nbnode == 6) {
252 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
253 node[4], node[5], ID);
254 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
257 else if (nbnode == 8) {
258 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
259 node[4], node[5], node[6], node[7], ID);
260 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
261 node[4], node[5], node[6], node[7] );
263 else if (nbnode == 10) {
264 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
265 node[4], node[5], node[6], node[7],
266 node[8], node[9], ID);
267 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
268 node[4], node[5], node[6], node[7],
271 else if (nbnode == 12) {
272 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
273 node[4], node[5], node[6], node[7],
274 node[8], node[9], node[10], node[11], ID);
275 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
276 node[4], node[5], node[6], node[7],
277 node[8], node[9], node[10], node[11] );
279 else if (nbnode == 13) {
280 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
281 node[4], node[5], node[6], node[7],
282 node[8], node[9], node[10],node[11],
284 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
285 node[4], node[5], node[6], node[7],
286 node[8], node[9], node[10],node[11],
289 else if (nbnode == 15) {
290 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
291 node[4], node[5], node[6], node[7],
292 node[8], node[9], node[10],node[11],
293 node[12],node[13],node[14],ID);
294 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
295 node[4], node[5], node[6], node[7],
296 node[8], node[9], node[10],node[11],
297 node[12],node[13],node[14] );
299 else if (nbnode == 20) {
300 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
301 node[4], node[5], node[6], node[7],
302 node[8], node[9], node[10],node[11],
303 node[12],node[13],node[14],node[15],
304 node[16],node[17],node[18],node[19],ID);
305 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
306 node[4], node[5], node[6], node[7],
307 node[8], node[9], node[10],node[11],
308 node[12],node[13],node[14],node[15],
309 node[16],node[17],node[18],node[19] );
311 else if (nbnode == 27) {
312 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
313 node[4], node[5], node[6], node[7],
314 node[8], node[9], node[10],node[11],
315 node[12],node[13],node[14],node[15],
316 node[16],node[17],node[18],node[19],
317 node[20],node[21],node[22],node[23],
318 node[24],node[25],node[26], ID);
319 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
320 node[4], node[5], node[6], node[7],
321 node[8], node[9], node[10],node[11],
322 node[12],node[13],node[14],node[15],
323 node[16],node[17],node[18],node[19],
324 node[20],node[21],node[22],node[23],
325 node[24],node[25],node[26] );
328 else if ( !features.myIsQuad )
330 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
331 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
335 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
336 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
342 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
343 else e = mesh->AddEdge (node[0], node[1] );
345 else if ( nbnode == 3 ) {
346 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
347 else e = mesh->AddEdge (node[0], node[1], node[2] );
351 case SMDSAbs_0DElement:
353 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
354 else e = mesh->Add0DElement (node[0] );
359 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
360 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
364 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
365 else e = mesh->AddBall (node[0], features.myBallDiameter );
370 if ( e ) myLastCreatedElems.push_back( e );
374 //=======================================================================
378 //=======================================================================
380 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
381 const ElemFeatures& features)
383 vector<const SMDS_MeshNode*> nodes;
384 nodes.reserve( nodeIDs.size() );
385 vector<int>::const_iterator id = nodeIDs.begin();
386 while ( id != nodeIDs.end() ) {
387 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
388 nodes.push_back( node );
392 return AddElement( nodes, features );
395 //=======================================================================
397 //purpose : Remove a node or an element.
398 // Modify a compute state of sub-meshes which become empty
399 //=======================================================================
401 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
406 SMESHDS_Mesh* aMesh = GetMeshDS();
407 set< SMESH_subMesh *> smmap;
410 list<int>::const_iterator it = theIDs.begin();
411 for ( ; it != theIDs.end(); it++ ) {
412 const SMDS_MeshElement * elem;
414 elem = aMesh->FindNode( *it );
416 elem = aMesh->FindElement( *it );
420 // Notify VERTEX sub-meshes about modification
422 const SMDS_MeshNode* node = cast2Node( elem );
423 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
424 if ( int aShapeID = node->getshapeId() )
425 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
428 // Find sub-meshes to notify about modification
429 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
430 // while ( nodeIt->more() ) {
431 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
432 // const SMDS_PositionPtr& aPosition = node->GetPosition();
433 // if ( aPosition.get() ) {
434 // if ( int aShapeID = aPosition->GetShapeId() ) {
435 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
436 // smmap.insert( sm );
443 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
445 aMesh->RemoveElement( elem );
449 // Notify sub-meshes about modification
450 if ( !smmap.empty() ) {
451 set< SMESH_subMesh *>::iterator smIt;
452 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
453 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
456 // // Check if the whole mesh becomes empty
457 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
458 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
463 //================================================================================
465 * \brief Create 0D elements on all nodes of the given object.
466 * \param elements - Elements on whose nodes to create 0D elements; if empty,
467 * the all mesh is treated
468 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
469 * \param duplicateElements - to add one more 0D element to a node or not
471 //================================================================================
473 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
474 TIDSortedElemSet& all0DElems,
475 const bool duplicateElements )
477 SMDS_ElemIteratorPtr elemIt;
478 if ( elements.empty() )
480 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
484 elemIt = SMESHUtils::elemSetIterator( elements );
487 while ( elemIt->more() )
489 const SMDS_MeshElement* e = elemIt->next();
490 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
491 while ( nodeIt->more() )
493 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
494 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
495 if ( duplicateElements || !it0D->more() )
497 myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
498 all0DElems.insert( myLastCreatedElems.back() );
500 while ( it0D->more() )
501 all0DElems.insert( it0D->next() );
506 //=======================================================================
507 //function : FindShape
508 //purpose : Return an index of the shape theElem is on
509 // or zero if a shape not found
510 //=======================================================================
512 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
516 SMESHDS_Mesh * aMesh = GetMeshDS();
517 if ( aMesh->ShapeToMesh().IsNull() )
520 int aShapeID = theElem->getshapeId();
524 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
525 if ( sm->Contains( theElem ))
528 if ( theElem->GetType() == SMDSAbs_Node ) {
529 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
532 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
535 TopoDS_Shape aShape; // the shape a node of theElem is on
536 if ( theElem->GetType() != SMDSAbs_Node )
538 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
539 while ( nodeIt->more() ) {
540 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
541 if ((aShapeID = node->getshapeId()) > 0) {
542 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
543 if ( sm->Contains( theElem ))
545 if ( aShape.IsNull() )
546 aShape = aMesh->IndexToShape( aShapeID );
552 // None of nodes is on a proper shape,
553 // find the shape among ancestors of aShape on which a node is
554 if ( !aShape.IsNull() ) {
555 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
556 for ( ; ancIt.More(); ancIt.Next() ) {
557 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
558 if ( sm && sm->Contains( theElem ))
559 return aMesh->ShapeToIndex( ancIt.Value() );
564 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
565 while ( const SMESHDS_SubMesh* sm = smIt->next() )
566 if ( sm->Contains( theElem ))
573 //=======================================================================
574 //function : IsMedium
576 //=======================================================================
578 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
579 const SMDSAbs_ElementType typeToCheck)
581 bool isMedium = false;
582 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
583 while (it->more() && !isMedium ) {
584 const SMDS_MeshElement* elem = it->next();
585 isMedium = elem->IsMediumNode(node);
590 //=======================================================================
591 //function : shiftNodesQuadTria
592 //purpose : Shift nodes in the array corresponded to quadratic triangle
593 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
594 //=======================================================================
596 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
598 const SMDS_MeshNode* nd1 = aNodes[0];
599 aNodes[0] = aNodes[1];
600 aNodes[1] = aNodes[2];
602 const SMDS_MeshNode* nd2 = aNodes[3];
603 aNodes[3] = aNodes[4];
604 aNodes[4] = aNodes[5];
608 //=======================================================================
609 //function : getNodesFromTwoTria
611 //=======================================================================
613 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
614 const SMDS_MeshElement * theTria2,
615 vector< const SMDS_MeshNode*>& N1,
616 vector< const SMDS_MeshNode*>& N2)
618 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
619 if ( N1.size() < 6 ) return false;
620 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
621 if ( N2.size() < 6 ) return false;
623 int sames[3] = {-1,-1,-1};
635 if(nbsames!=2) return false;
637 shiftNodesQuadTria(N1);
639 shiftNodesQuadTria(N1);
642 i = sames[0] + sames[1] + sames[2];
644 shiftNodesQuadTria(N2);
646 // now we receive following N1 and N2 (using numeration as in the image below)
647 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
648 // i.e. first nodes from both arrays form a new diagonal
652 //=======================================================================
653 //function : InverseDiag
654 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
655 // but having other common link.
656 // Return False if args are improper
657 //=======================================================================
659 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
660 const SMDS_MeshElement * theTria2 )
664 if ( !theTria1 || !theTria2 ||
665 !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
666 !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
667 theTria1->GetType() != SMDSAbs_Face ||
668 theTria2->GetType() != SMDSAbs_Face )
671 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
672 (theTria2->GetEntityType() == SMDSEntity_Triangle))
674 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
675 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
679 // put nodes in array and find out indices of the same ones
680 const SMDS_MeshNode* aNodes [6];
681 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
683 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
684 while ( it->more() ) {
685 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
687 if ( i > 2 ) // theTria2
688 // find same node of theTria1
689 for ( int j = 0; j < 3; j++ )
690 if ( aNodes[ i ] == aNodes[ j ]) {
699 return false; // theTria1 is not a triangle
700 it = theTria2->nodesIterator();
702 if ( i == 6 && it->more() )
703 return false; // theTria2 is not a triangle
706 // find indices of 1,2 and of A,B in theTria1
707 int iA = -1, iB = 0, i1 = 0, i2 = 0;
708 for ( i = 0; i < 6; i++ ) {
709 if ( sameInd [ i ] == -1 ) {
714 if ( iA >= 0) iB = i;
718 // nodes 1 and 2 should not be the same
719 if ( aNodes[ i1 ] == aNodes[ i2 ] )
723 aNodes[ iA ] = aNodes[ i2 ];
725 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
727 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
728 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
732 } // end if(F1 && F2)
734 // check case of quadratic faces
735 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
736 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
738 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
739 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
743 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
744 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
752 vector< const SMDS_MeshNode* > N1;
753 vector< const SMDS_MeshNode* > N2;
754 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
756 // now we receive following N1 and N2 (using numeration as above image)
757 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
758 // i.e. first nodes from both arrays determ new diagonal
760 vector< const SMDS_MeshNode*> N1new( N1.size() );
761 vector< const SMDS_MeshNode*> N2new( N2.size() );
762 N1new.back() = N1.back(); // central node of biquadratic
763 N2new.back() = N2.back();
764 N1new[0] = N1[0]; N2new[0] = N1[0];
765 N1new[1] = N2[0]; N2new[1] = N1[1];
766 N1new[2] = N2[1]; N2new[2] = N2[0];
767 N1new[3] = N1[4]; N2new[3] = N1[3];
768 N1new[4] = N2[3]; N2new[4] = N2[5];
769 N1new[5] = N1[5]; N2new[5] = N1[4];
770 // change nodes in faces
771 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
772 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
774 // move the central node of biquadratic triangle
775 SMESH_MesherHelper helper( *GetMesh() );
776 for ( int is2nd = 0; is2nd < 2; ++is2nd )
778 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
779 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
780 if ( nodes.size() < 7 )
782 helper.SetSubShape( tria->getshapeId() );
783 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
787 xyz = ( SMESH_NodeXYZ( nodes[3] ) +
788 SMESH_NodeXYZ( nodes[4] ) +
789 SMESH_NodeXYZ( nodes[5] )) / 3.;
794 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
795 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
796 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
798 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
799 xyz = S->Value( uv.X(), uv.Y() );
800 xyz.Transform( loc );
801 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
802 nodes[6]->getshapeId() > 0 )
803 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
805 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
810 //=======================================================================
811 //function : findTriangles
812 //purpose : find triangles sharing theNode1-theNode2 link
813 //=======================================================================
815 static bool findTriangles(const SMDS_MeshNode * theNode1,
816 const SMDS_MeshNode * theNode2,
817 const SMDS_MeshElement*& theTria1,
818 const SMDS_MeshElement*& theTria2)
820 if ( !theNode1 || !theNode2 ) return false;
822 theTria1 = theTria2 = 0;
824 set< const SMDS_MeshElement* > emap;
825 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
827 const SMDS_MeshElement* elem = it->next();
828 if ( elem->NbCornerNodes() == 3 )
831 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
833 const SMDS_MeshElement* elem = it->next();
834 if ( emap.count( elem )) {
842 // theTria1 must be element with minimum ID
843 if ( theTria2->GetID() < theTria1->GetID() )
844 std::swap( theTria2, theTria1 );
852 //=======================================================================
853 //function : InverseDiag
854 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
855 // with ones built on the same 4 nodes but having other common link.
856 // Return false if proper faces not found
857 //=======================================================================
859 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
860 const SMDS_MeshNode * theNode2)
864 const SMDS_MeshElement *tr1, *tr2;
865 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
868 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
869 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
872 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
873 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
875 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
876 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
880 // put nodes in array
881 // and find indices of 1,2 and of A in tr1 and of B in tr2
882 int i, iA1 = 0, i1 = 0;
883 const SMDS_MeshNode* aNodes1 [3];
884 SMDS_ElemIteratorPtr it;
885 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
886 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
887 if ( aNodes1[ i ] == theNode1 )
888 iA1 = i; // node A in tr1
889 else if ( aNodes1[ i ] != theNode2 )
893 const SMDS_MeshNode* aNodes2 [3];
894 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
895 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
896 if ( aNodes2[ i ] == theNode2 )
897 iB2 = i; // node B in tr2
898 else if ( aNodes2[ i ] != theNode1 )
902 // nodes 1 and 2 should not be the same
903 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
907 aNodes1[ iA1 ] = aNodes2[ i2 ];
909 aNodes2[ iB2 ] = aNodes1[ i1 ];
911 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
912 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
917 // check case of quadratic faces
918 return InverseDiag(tr1,tr2);
921 //=======================================================================
922 //function : getQuadrangleNodes
923 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
924 // fusion of triangles tr1 and tr2 having shared link on
925 // theNode1 and theNode2
926 //=======================================================================
928 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
929 const SMDS_MeshNode * theNode1,
930 const SMDS_MeshNode * theNode2,
931 const SMDS_MeshElement * tr1,
932 const SMDS_MeshElement * tr2 )
934 if( tr1->NbNodes() != tr2->NbNodes() )
936 // find the 4-th node to insert into tr1
937 const SMDS_MeshNode* n4 = 0;
938 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
940 while ( !n4 && i<3 ) {
941 const SMDS_MeshNode * n = cast2Node( it->next() );
943 bool isDiag = ( n == theNode1 || n == theNode2 );
947 // Make an array of nodes to be in a quadrangle
948 int iNode = 0, iFirstDiag = -1;
949 it = tr1->nodesIterator();
952 const SMDS_MeshNode * n = cast2Node( it->next() );
954 bool isDiag = ( n == theNode1 || n == theNode2 );
956 if ( iFirstDiag < 0 )
958 else if ( iNode - iFirstDiag == 1 )
959 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
961 else if ( n == n4 ) {
962 return false; // tr1 and tr2 should not have all the same nodes
964 theQuadNodes[ iNode++ ] = n;
966 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
967 theQuadNodes[ iNode ] = n4;
972 //=======================================================================
973 //function : DeleteDiag
974 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
975 // with a quadrangle built on the same 4 nodes.
976 // Return false if proper faces not found
977 //=======================================================================
979 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
980 const SMDS_MeshNode * theNode2)
984 const SMDS_MeshElement *tr1, *tr2;
985 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
988 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
989 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
992 SMESHDS_Mesh * aMesh = GetMeshDS();
994 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
995 (tr2->GetEntityType() == SMDSEntity_Triangle))
997 const SMDS_MeshNode* aNodes [ 4 ];
998 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1001 const SMDS_MeshElement* newElem = 0;
1002 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1003 myLastCreatedElems.push_back(newElem);
1004 AddToSameGroups( newElem, tr1, aMesh );
1005 int aShapeId = tr1->getshapeId();
1007 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1009 aMesh->RemoveElement( tr1 );
1010 aMesh->RemoveElement( tr2 );
1015 // check case of quadratic faces
1016 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1018 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1022 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1023 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1031 vector< const SMDS_MeshNode* > N1;
1032 vector< const SMDS_MeshNode* > N2;
1033 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1035 // now we receive following N1 and N2 (using numeration as above image)
1036 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1037 // i.e. first nodes from both arrays determ new diagonal
1039 const SMDS_MeshNode* aNodes[8];
1049 const SMDS_MeshElement* newElem = 0;
1050 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1051 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1052 myLastCreatedElems.push_back(newElem);
1053 AddToSameGroups( newElem, tr1, aMesh );
1054 int aShapeId = tr1->getshapeId();
1057 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1059 aMesh->RemoveElement( tr1 );
1060 aMesh->RemoveElement( tr2 );
1062 // remove middle node (9)
1063 GetMeshDS()->RemoveNode( N1[4] );
1068 //=======================================================================
1069 //function : Reorient
1070 //purpose : Reverse theElement orientation
1071 //=======================================================================
1073 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1079 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1080 if ( !it || !it->more() )
1083 const SMDSAbs_ElementType type = theElem->GetType();
1084 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1087 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1088 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1090 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1092 MESSAGE("Warning: bad volumic element");
1095 SMDS_VolumeTool vTool( aPolyedre );
1096 const int nbFaces = vTool.NbFaces();
1097 vector<int> quantities( nbFaces );
1098 vector<const SMDS_MeshNode *> poly_nodes;
1100 // check if all facets are oriented equally
1101 bool sameOri = true;
1102 vector<int>& facetOri = quantities; // keep orientation in quantities so far
1103 for (int iface = 0; iface < nbFaces; iface++)
1105 facetOri[ iface ] = vTool.IsFaceExternal( iface );
1106 if ( facetOri[ iface ] != facetOri[ 0 ])
1110 // reverse faces of the polyhedron
1111 int neededOri = sameOri ? 1 - facetOri[0] : 1;
1112 poly_nodes.reserve( vTool.NbNodes() );
1113 for ( int iface = 0; iface < nbFaces; iface++ )
1115 int nbFaceNodes = vTool.NbFaceNodes( iface );
1116 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( iface );
1117 bool toReverse = ( facetOri[ iface ] != neededOri );
1119 quantities[ iface ] = nbFaceNodes;
1122 for ( int inode = nbFaceNodes - 1; inode >= 0; inode-- )
1123 poly_nodes.push_back( nodes[ inode ]);
1125 poly_nodes.insert( poly_nodes.end(), nodes, nodes + nbFaceNodes );
1127 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1129 else // other elements
1131 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1132 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1133 if ( interlace.empty() )
1135 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1139 SMDS_MeshCell::applyInterlace( interlace, nodes );
1141 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1146 //================================================================================
1148 * \brief Reorient faces.
1149 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1150 * \param theDirection - desired direction of normal of \a theFace
1151 * \param theFace - one of \a theFaces that should be oriented according to
1152 * \a theDirection and whose orientation defines orientation of other faces
1153 * \return number of reoriented faces.
1155 //================================================================================
1157 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1158 const gp_Dir& theDirection,
1159 const SMDS_MeshElement * theFace)
1162 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1164 if ( theFaces.empty() )
1166 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
1167 while ( fIt->more() )
1168 theFaces.insert( theFaces.end(), fIt->next() );
1171 // orient theFace according to theDirection
1173 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1174 if ( normal * theDirection.XYZ() < 0 )
1175 nbReori += Reorient( theFace );
1177 // Orient other faces
1179 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1180 TIDSortedElemSet avoidSet;
1181 set< SMESH_TLink > checkedLinks;
1182 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1184 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1185 theFaces.erase( theFace );
1186 startFaces.insert( theFace );
1188 int nodeInd1, nodeInd2;
1189 const SMDS_MeshElement* otherFace;
1190 vector< const SMDS_MeshElement* > facesNearLink;
1191 vector< std::pair< int, int > > nodeIndsOfFace;
1193 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1194 while ( !startFaces.empty() )
1196 startFace = startFaces.begin();
1197 theFace = *startFace;
1198 startFaces.erase( startFace );
1199 if ( !visitedFaces.insert( theFace ).second )
1203 avoidSet.insert(theFace);
1205 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1207 const int nbNodes = theFace->NbCornerNodes();
1208 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1210 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1211 linkIt_isNew = checkedLinks.insert( link );
1212 if ( !linkIt_isNew.second )
1214 // link has already been checked and won't be encountered more
1215 // if the group (theFaces) is manifold
1216 //checkedLinks.erase( linkIt_isNew.first );
1220 facesNearLink.clear();
1221 nodeIndsOfFace.clear();
1222 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1224 &nodeInd1, &nodeInd2 )))
1225 if ( otherFace != theFace)
1227 facesNearLink.push_back( otherFace );
1228 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1229 avoidSet.insert( otherFace );
1231 if ( facesNearLink.size() > 1 )
1233 // NON-MANIFOLD mesh shell !
1234 // select a face most co-directed with theFace,
1235 // other faces won't be visited this time
1237 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1238 double proj, maxProj = -1;
1239 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1240 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1241 if (( proj = Abs( NF * NOF )) > maxProj ) {
1243 otherFace = facesNearLink[i];
1244 nodeInd1 = nodeIndsOfFace[i].first;
1245 nodeInd2 = nodeIndsOfFace[i].second;
1248 // not to visit rejected faces
1249 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1250 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1251 visitedFaces.insert( facesNearLink[i] );
1253 else if ( facesNearLink.size() == 1 )
1255 otherFace = facesNearLink[0];
1256 nodeInd1 = nodeIndsOfFace.back().first;
1257 nodeInd2 = nodeIndsOfFace.back().second;
1259 if ( otherFace && otherFace != theFace)
1261 // link must be reverse in otherFace if orientation to otherFace
1262 // is same as that of theFace
1263 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1265 nbReori += Reorient( otherFace );
1267 startFaces.insert( otherFace );
1270 std::swap( link.first, link.second ); // reverse the link
1276 //================================================================================
1278 * \brief Reorient faces basing on orientation of adjacent volumes.
1279 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1280 * \param theVolumes - reference volumes.
1281 * \param theOutsideNormal - to orient faces to have their normal
1282 * pointing either \a outside or \a inside the adjacent volumes.
1283 * \return number of reoriented faces.
1285 //================================================================================
1287 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1288 TIDSortedElemSet & theVolumes,
1289 const bool theOutsideNormal)
1293 SMDS_ElemIteratorPtr faceIt;
1294 if ( theFaces.empty() )
1295 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1297 faceIt = SMESHUtils::elemSetIterator( theFaces );
1299 vector< const SMDS_MeshNode* > faceNodes;
1300 TIDSortedElemSet checkedVolumes;
1301 set< const SMDS_MeshNode* > faceNodesSet;
1302 SMDS_VolumeTool volumeTool;
1304 while ( faceIt->more() ) // loop on given faces
1306 const SMDS_MeshElement* face = faceIt->next();
1307 if ( face->GetType() != SMDSAbs_Face )
1310 const size_t nbCornersNodes = face->NbCornerNodes();
1311 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1313 checkedVolumes.clear();
1314 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1315 while ( vIt->more() )
1317 const SMDS_MeshElement* volume = vIt->next();
1319 if ( !checkedVolumes.insert( volume ).second )
1321 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1324 // is volume adjacent?
1325 bool allNodesCommon = true;
1326 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1327 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1328 if ( !allNodesCommon )
1331 // get nodes of a corresponding volume facet
1332 faceNodesSet.clear();
1333 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1334 volumeTool.Set( volume );
1335 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1336 if ( facetID < 0 ) continue;
1337 volumeTool.SetExternalNormal();
1338 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1340 // compare order of faceNodes and facetNodes
1341 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1343 for ( int i = 0; i < 2; ++i )
1345 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1346 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1347 if ( faceNodes[ iN ] == n )
1353 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1354 if ( isOutside != theOutsideNormal )
1355 nbReori += Reorient( face );
1357 } // loop on given faces
1362 //=======================================================================
1363 //function : getBadRate
1365 //=======================================================================
1367 static double getBadRate (const SMDS_MeshElement* theElem,
1368 SMESH::Controls::NumericalFunctorPtr& theCrit)
1370 SMESH::Controls::TSequenceOfXYZ P;
1371 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1373 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1374 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1377 //=======================================================================
1378 //function : QuadToTri
1379 //purpose : Cut quadrangles into triangles.
1380 // theCrit is used to select a diagonal to cut
1381 //=======================================================================
1383 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1384 SMESH::Controls::NumericalFunctorPtr theCrit)
1388 if ( !theCrit.get() )
1391 SMESHDS_Mesh * aMesh = GetMeshDS();
1392 Handle(Geom_Surface) surface;
1393 SMESH_MesherHelper helper( *GetMesh() );
1395 myLastCreatedElems.reserve( theElems.size() * 2 );
1397 TIDSortedElemSet::iterator itElem;
1398 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1400 const SMDS_MeshElement* elem = *itElem;
1401 if ( !elem || elem->GetType() != SMDSAbs_Face )
1403 if ( elem->NbCornerNodes() != 4 )
1406 // retrieve element nodes
1407 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1409 // compare two sets of possible triangles
1410 double aBadRate1, aBadRate2; // to what extent a set is bad
1411 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1412 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1413 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1415 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1416 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1417 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1419 const int aShapeId = FindShape( elem );
1420 const SMDS_MeshElement* newElem1 = 0;
1421 const SMDS_MeshElement* newElem2 = 0;
1423 if ( !elem->IsQuadratic() ) // split linear quadrangle
1425 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1426 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1427 if ( aBadRate1 <= aBadRate2 ) {
1428 // tr1 + tr2 is better
1429 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1430 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1433 // tr3 + tr4 is better
1434 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1435 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1438 else // split quadratic quadrangle
1440 helper.SetIsQuadratic( true );
1441 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1443 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1444 if ( aNodes.size() == 9 )
1446 helper.SetIsBiQuadratic( true );
1447 if ( aBadRate1 <= aBadRate2 )
1448 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1450 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1452 // create a new element
1453 if ( aBadRate1 <= aBadRate2 ) {
1454 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1455 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1458 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1459 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1463 // care of a new element
1465 myLastCreatedElems.push_back(newElem1);
1466 myLastCreatedElems.push_back(newElem2);
1467 AddToSameGroups( newElem1, elem, aMesh );
1468 AddToSameGroups( newElem2, elem, aMesh );
1470 // put a new triangle on the same shape
1472 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1473 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1475 aMesh->RemoveElement( elem );
1480 //=======================================================================
1482 * \brief Split each of given quadrangles into 4 triangles.
1483 * \param theElems - The faces to be split. If empty all faces are split.
1485 //=======================================================================
1487 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1490 myLastCreatedElems.reserve( theElems.size() * 4 );
1492 SMESH_MesherHelper helper( *GetMesh() );
1493 helper.SetElementsOnShape( true );
1495 SMDS_ElemIteratorPtr faceIt;
1496 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1497 else faceIt = SMESHUtils::elemSetIterator( theElems );
1500 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1502 vector< const SMDS_MeshNode* > nodes;
1503 SMESHDS_SubMesh* subMeshDS = 0;
1505 Handle(Geom_Surface) surface;
1506 TopLoc_Location loc;
1508 while ( faceIt->more() )
1510 const SMDS_MeshElement* quad = faceIt->next();
1511 if ( !quad || quad->NbCornerNodes() != 4 )
1514 // get a surface the quad is on
1516 if ( quad->getshapeId() < 1 )
1519 helper.SetSubShape( 0 );
1522 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1524 helper.SetSubShape( quad->getshapeId() );
1525 if ( !helper.GetSubShape().IsNull() &&
1526 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1528 F = TopoDS::Face( helper.GetSubShape() );
1529 surface = BRep_Tool::Surface( F, loc );
1530 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1534 helper.SetSubShape( 0 );
1539 // create a central node
1541 const SMDS_MeshNode* nCentral;
1542 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1544 if ( nodes.size() == 9 )
1546 nCentral = nodes.back();
1553 for ( ; iN < nodes.size(); ++iN )
1554 xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1556 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1557 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1559 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1560 xyz[0], xyz[1], xyz[2], xyz[3],
1561 xyz[4], xyz[5], xyz[6], xyz[7] );
1565 for ( ; iN < nodes.size(); ++iN )
1566 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1568 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1569 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1571 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1572 uv[0], uv[1], uv[2], uv[3],
1573 uv[4], uv[5], uv[6], uv[7] );
1575 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1579 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1580 uv[8].X(), uv[8].Y() );
1581 myLastCreatedNodes.push_back( nCentral );
1584 // create 4 triangles
1586 helper.SetIsQuadratic ( nodes.size() > 4 );
1587 helper.SetIsBiQuadratic( nodes.size() == 9 );
1588 if ( helper.GetIsQuadratic() )
1589 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1591 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1593 for ( int i = 0; i < 4; ++i )
1595 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1598 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1599 myLastCreatedElems.push_back( tria );
1604 //=======================================================================
1605 //function : BestSplit
1606 //purpose : Find better diagonal for cutting.
1607 //=======================================================================
1609 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1610 SMESH::Controls::NumericalFunctorPtr theCrit)
1617 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1620 if( theQuad->NbNodes()==4 ||
1621 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1623 // retrieve element nodes
1624 const SMDS_MeshNode* aNodes [4];
1625 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1627 //while (itN->more())
1629 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1631 // compare two sets of possible triangles
1632 double aBadRate1, aBadRate2; // to what extent a set is bad
1633 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1634 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1635 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1637 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1638 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1639 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1640 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1641 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1642 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1643 return 1; // diagonal 1-3
1645 return 2; // diagonal 2-4
1652 // Methods of splitting volumes into tetra
1654 const int theHexTo5_1[5*4+1] =
1656 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1658 const int theHexTo5_2[5*4+1] =
1660 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1662 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1664 const int theHexTo6_1[6*4+1] =
1666 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
1668 const int theHexTo6_2[6*4+1] =
1670 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
1672 const int theHexTo6_3[6*4+1] =
1674 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
1676 const int theHexTo6_4[6*4+1] =
1678 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
1680 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1682 const int thePyraTo2_1[2*4+1] =
1684 0, 1, 2, 4, 0, 2, 3, 4, -1
1686 const int thePyraTo2_2[2*4+1] =
1688 1, 2, 3, 4, 1, 3, 0, 4, -1
1690 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1692 const int thePentaTo3_1[3*4+1] =
1694 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1696 const int thePentaTo3_2[3*4+1] =
1698 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1700 const int thePentaTo3_3[3*4+1] =
1702 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1704 const int thePentaTo3_4[3*4+1] =
1706 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1708 const int thePentaTo3_5[3*4+1] =
1710 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1712 const int thePentaTo3_6[3*4+1] =
1714 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1716 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1717 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1719 // Methods of splitting hexahedron into prisms
1721 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1723 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
1725 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1727 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
1729 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1731 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
1734 const int theHexTo2Prisms_BT_1[6*2+1] =
1736 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1738 const int theHexTo2Prisms_BT_2[6*2+1] =
1740 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1742 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1744 const int theHexTo2Prisms_LR_1[6*2+1] =
1746 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1748 const int theHexTo2Prisms_LR_2[6*2+1] =
1750 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1752 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1754 const int theHexTo2Prisms_FB_1[6*2+1] =
1756 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1758 const int theHexTo2Prisms_FB_2[6*2+1] =
1760 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1762 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1765 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1768 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1769 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1770 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1771 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1777 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1778 bool _baryNode; //!< additional node is to be created at cell barycenter
1779 bool _ownConn; //!< to delete _connectivity in destructor
1780 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1782 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1783 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1784 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1785 bool hasFacet( const TTriangleFacet& facet ) const
1787 if ( _nbCorners == 4 )
1789 const int* tetConn = _connectivity;
1790 for ( ; tetConn[0] >= 0; tetConn += 4 )
1791 if (( facet.contains( tetConn[0] ) +
1792 facet.contains( tetConn[1] ) +
1793 facet.contains( tetConn[2] ) +
1794 facet.contains( tetConn[3] )) == 3 )
1797 else // prism, _nbCorners == 6
1799 const int* prismConn = _connectivity;
1800 for ( ; prismConn[0] >= 0; prismConn += 6 )
1802 if (( facet.contains( prismConn[0] ) &&
1803 facet.contains( prismConn[1] ) &&
1804 facet.contains( prismConn[2] ))
1806 ( facet.contains( prismConn[3] ) &&
1807 facet.contains( prismConn[4] ) &&
1808 facet.contains( prismConn[5] )))
1816 //=======================================================================
1818 * \brief return TSplitMethod for the given element to split into tetrahedra
1820 //=======================================================================
1822 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1824 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1826 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1827 // an edge and a face barycenter; tertaherdons are based on triangles and
1828 // a volume barycenter
1829 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1831 // Find out how adjacent volumes are split
1833 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1834 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1835 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1837 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1838 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1839 if ( nbNodes < 4 ) continue;
1841 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1842 const int* nInd = vol.GetFaceNodesIndices( iF );
1845 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1846 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1847 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1848 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1852 int iCom = 0; // common node of triangle faces to split into
1853 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1855 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1856 nInd[ iQ * ( (iCom+1)%nbNodes )],
1857 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1858 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1859 nInd[ iQ * ( (iCom+2)%nbNodes )],
1860 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1861 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1863 triaSplits.push_back( t012 );
1864 triaSplits.push_back( t023 );
1869 if ( !triaSplits.empty() )
1870 hasAdjacentSplits = true;
1873 // Among variants of split method select one compliant with adjacent volumes
1875 TSplitMethod method;
1876 if ( !vol.Element()->IsPoly() && !is24TetMode )
1878 int nbVariants = 2, nbTet = 0;
1879 const int** connVariants = 0;
1880 switch ( vol.Element()->GetEntityType() )
1882 case SMDSEntity_Hexa:
1883 case SMDSEntity_Quad_Hexa:
1884 case SMDSEntity_TriQuad_Hexa:
1885 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1886 connVariants = theHexTo5, nbTet = 5;
1888 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1890 case SMDSEntity_Pyramid:
1891 case SMDSEntity_Quad_Pyramid:
1892 connVariants = thePyraTo2; nbTet = 2;
1894 case SMDSEntity_Penta:
1895 case SMDSEntity_Quad_Penta:
1896 case SMDSEntity_BiQuad_Penta:
1897 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1902 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1904 // check method compliancy with adjacent tetras,
1905 // all found splits must be among facets of tetras described by this method
1906 method = TSplitMethod( nbTet, connVariants[variant] );
1907 if ( hasAdjacentSplits && method._nbSplits > 0 )
1909 bool facetCreated = true;
1910 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1912 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1913 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1914 facetCreated = method.hasFacet( *facet );
1916 if ( !facetCreated )
1917 method = TSplitMethod(0); // incompatible method
1921 if ( method._nbSplits < 1 )
1923 // No standard method is applicable, use a generic solution:
1924 // each facet of a volume is split into triangles and
1925 // each of triangles and a volume barycenter form a tetrahedron.
1927 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1929 int* connectivity = new int[ maxTetConnSize + 1 ];
1930 method._connectivity = connectivity;
1931 method._ownConn = true;
1932 method._baryNode = !isHex27; // to create central node or not
1935 int baryCenInd = vol.NbNodes() - int( isHex27 );
1936 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1938 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1939 const int* nInd = vol.GetFaceNodesIndices( iF );
1940 // find common node of triangle facets of tetra to create
1941 int iCommon = 0; // index in linear numeration
1942 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1943 if ( !triaSplits.empty() )
1946 const TTriangleFacet* facet = &triaSplits.front();
1947 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1948 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1949 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1952 else if ( nbNodes > 3 && !is24TetMode )
1954 // find the best method of splitting into triangles by aspect ratio
1955 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1956 map< double, int > badness2iCommon;
1957 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1958 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1959 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1962 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1964 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1965 nodes[ iQ*((iLast-1)%nbNodes)],
1966 nodes[ iQ*((iLast )%nbNodes)]);
1967 badness += getBadRate( &tria, aspectRatio );
1969 badness2iCommon.insert( make_pair( badness, iCommon ));
1971 // use iCommon with lowest badness
1972 iCommon = badness2iCommon.begin()->second;
1974 if ( iCommon >= nbNodes )
1975 iCommon = 0; // something wrong
1977 // fill connectivity of tetrahedra based on a current face
1978 int nbTet = nbNodes - 2;
1979 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1984 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1985 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1989 method._faceBaryNode[ iF ] = 0;
1990 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1993 for ( int i = 0; i < nbTet; ++i )
1995 int i1 = i, i2 = (i+1) % nbNodes;
1996 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1997 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1998 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1999 connectivity[ connSize++ ] = faceBaryCenInd;
2000 connectivity[ connSize++ ] = baryCenInd;
2005 for ( int i = 0; i < nbTet; ++i )
2007 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2008 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2009 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2010 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2011 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2012 connectivity[ connSize++ ] = baryCenInd;
2015 method._nbSplits += nbTet;
2017 } // loop on volume faces
2019 connectivity[ connSize++ ] = -1;
2021 } // end of generic solution
2025 //=======================================================================
2027 * \brief return TSplitMethod to split haxhedron into prisms
2029 //=======================================================================
2031 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2032 const int methodFlags,
2033 const int facetToSplit)
2035 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2037 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2039 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2041 static TSplitMethod to4methods[4]; // order BT, LR, FB
2042 if ( to4methods[iF]._nbSplits == 0 )
2046 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2047 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2048 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2051 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2052 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2053 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2056 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2057 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2058 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2060 default: return to4methods[3];
2062 to4methods[iF]._nbSplits = 4;
2063 to4methods[iF]._nbCorners = 6;
2065 return to4methods[iF];
2067 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2069 TSplitMethod method;
2071 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2073 const int nbVariants = 2, nbSplits = 2;
2074 const int** connVariants = 0;
2076 case 0: connVariants = theHexTo2Prisms_BT; break;
2077 case 1: connVariants = theHexTo2Prisms_LR; break;
2078 case 2: connVariants = theHexTo2Prisms_FB; break;
2079 default: return method;
2082 // look for prisms adjacent via facetToSplit and an opposite one
2083 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2085 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2086 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2087 if ( nbNodes != 4 ) return method;
2089 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2090 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2091 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2093 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2095 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2100 // there are adjacent prism
2101 for ( int variant = 0; variant < nbVariants; ++variant )
2103 // check method compliancy with adjacent prisms,
2104 // the found prism facets must be among facets of prisms described by current method
2105 method._nbSplits = nbSplits;
2106 method._nbCorners = 6;
2107 method._connectivity = connVariants[ variant ];
2108 if ( method.hasFacet( *t ))
2113 // No adjacent prisms. Select a variant with a best aspect ratio.
2115 double badness[2] = { 0., 0. };
2116 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2117 const SMDS_MeshNode** nodes = vol.GetNodes();
2118 for ( int variant = 0; variant < nbVariants; ++variant )
2119 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2121 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2122 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2124 method._connectivity = connVariants[ variant ];
2125 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2126 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2127 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2129 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2132 badness[ variant ] += getBadRate( &tria, aspectRatio );
2134 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2136 method._nbSplits = nbSplits;
2137 method._nbCorners = 6;
2138 method._connectivity = connVariants[ iBetter ];
2143 //================================================================================
2145 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2147 //================================================================================
2149 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2150 const SMDSAbs_GeometryType geom ) const
2152 // find the tetrahedron including the three nodes of facet
2153 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2154 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2155 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2156 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2157 while ( volIt1->more() )
2159 const SMDS_MeshElement* v = volIt1->next();
2160 if ( v->GetGeomType() != geom )
2162 const int lastCornerInd = v->NbCornerNodes() - 1;
2163 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2164 continue; // medium node not allowed
2165 const int ind2 = v->GetNodeIndex( n2 );
2166 if ( ind2 < 0 || lastCornerInd < ind2 )
2168 const int ind3 = v->GetNodeIndex( n3 );
2169 if ( ind3 < 0 || lastCornerInd < ind3 )
2176 //=======================================================================
2178 * \brief A key of a face of volume
2180 //=======================================================================
2182 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2184 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2186 TIDSortedNodeSet sortedNodes;
2187 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2188 int nbNodes = vol.NbFaceNodes( iF );
2189 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2190 for ( int i = 0; i < nbNodes; i += iQ )
2191 sortedNodes.insert( fNodes[i] );
2192 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2193 first.first = (*(n++))->GetID();
2194 first.second = (*(n++))->GetID();
2195 second.first = (*(n++))->GetID();
2196 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2201 //=======================================================================
2202 //function : SplitVolumes
2203 //purpose : Split volume elements into tetrahedra or prisms.
2204 // If facet ID < 0, element is split into tetrahedra,
2205 // else a hexahedron is split into prisms so that the given facet is
2206 // split into triangles
2207 //=======================================================================
2209 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2210 const int theMethodFlags)
2212 SMDS_VolumeTool volTool;
2213 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2214 fHelper.ToFixNodeParameters( true );
2216 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2217 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2219 SMESH_SequenceOfElemPtr newNodes, newElems;
2221 // map face of volume to it's baricenrtic node
2222 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2224 vector<const SMDS_MeshElement* > splitVols;
2226 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2227 for ( ; elem2facet != theElems.end(); ++elem2facet )
2229 const SMDS_MeshElement* elem = elem2facet->first;
2230 const int facetToSplit = elem2facet->second;
2231 if ( elem->GetType() != SMDSAbs_Volume )
2233 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2234 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2237 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2239 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2240 getTetraSplitMethod( volTool, theMethodFlags ) :
2241 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2242 if ( splitMethod._nbSplits < 1 ) continue;
2244 // find submesh to add new tetras to
2245 if ( !subMesh || !subMesh->Contains( elem ))
2247 int shapeID = FindShape( elem );
2248 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2249 subMesh = GetMeshDS()->MeshElements( shapeID );
2252 if ( elem->IsQuadratic() )
2255 // add quadratic links to the helper
2256 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2258 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2259 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2260 for ( int iN = 0; iN < nbN; iN += iQ )
2261 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2263 helper.SetIsQuadratic( true );
2268 helper.SetIsQuadratic( false );
2270 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2271 volTool.GetNodes() + elem->NbNodes() );
2272 helper.SetElementsOnShape( true );
2273 if ( splitMethod._baryNode )
2275 // make a node at barycenter
2276 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2277 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2278 nodes.push_back( gcNode );
2279 newNodes.push_back( gcNode );
2281 if ( !splitMethod._faceBaryNode.empty() )
2283 // make or find baricentric nodes of faces
2284 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2285 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2287 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2288 volFace2BaryNode.insert
2289 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2292 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2293 newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2295 nodes.push_back( iF_n->second = f_n->second );
2300 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2301 const int* volConn = splitMethod._connectivity;
2302 if ( splitMethod._nbCorners == 4 ) // tetra
2303 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2304 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2305 nodes[ volConn[1] ],
2306 nodes[ volConn[2] ],
2307 nodes[ volConn[3] ]));
2309 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2310 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2311 nodes[ volConn[1] ],
2312 nodes[ volConn[2] ],
2313 nodes[ volConn[3] ],
2314 nodes[ volConn[4] ],
2315 nodes[ volConn[5] ]));
2317 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2319 // Split faces on sides of the split volume
2321 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2322 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2324 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2325 if ( nbNodes < 4 ) continue;
2327 // find an existing face
2328 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2329 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2330 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2331 /*noMedium=*/false))
2334 helper.SetElementsOnShape( false );
2335 vector< const SMDS_MeshElement* > triangles;
2337 // find submesh to add new triangles in
2338 if ( !fSubMesh || !fSubMesh->Contains( face ))
2340 int shapeID = FindShape( face );
2341 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2343 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2344 if ( iF_n != splitMethod._faceBaryNode.end() )
2346 const SMDS_MeshNode *baryNode = iF_n->second;
2347 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2349 const SMDS_MeshNode* n1 = fNodes[iN];
2350 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2351 const SMDS_MeshNode *n3 = baryNode;
2352 if ( !volTool.IsFaceExternal( iF ))
2354 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2356 if ( fSubMesh ) // update position of the bary node on geometry
2359 subMesh->RemoveNode( baryNode );
2360 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2361 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2362 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2364 fHelper.SetSubShape( s );
2365 gp_XY uv( 1e100, 1e100 );
2367 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2368 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2371 // node is too far from the surface
2372 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2373 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2374 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2381 // among possible triangles create ones described by split method
2382 const int* nInd = volTool.GetFaceNodesIndices( iF );
2383 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2384 int iCom = 0; // common node of triangle faces to split into
2385 list< TTriangleFacet > facets;
2386 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2388 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2389 nInd[ iQ * ( (iCom+1)%nbNodes )],
2390 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2391 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2392 nInd[ iQ * ( (iCom+2)%nbNodes )],
2393 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2394 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2396 facets.push_back( t012 );
2397 facets.push_back( t023 );
2398 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2399 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2400 nInd[ iQ * ((iLast-1)%nbNodes )],
2401 nInd[ iQ * ((iLast )%nbNodes )]));
2405 list< TTriangleFacet >::iterator facet = facets.begin();
2406 if ( facet == facets.end() )
2408 for ( ; facet != facets.end(); ++facet )
2410 if ( !volTool.IsFaceExternal( iF ))
2411 swap( facet->_n2, facet->_n3 );
2412 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2413 volNodes[ facet->_n2 ],
2414 volNodes[ facet->_n3 ]));
2417 for ( size_t i = 0; i < triangles.size(); ++i )
2419 if ( !triangles[ i ]) continue;
2421 fSubMesh->AddElement( triangles[ i ]);
2422 newElems.push_back( triangles[ i ]);
2424 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2425 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2427 } // while a face based on facet nodes exists
2428 } // loop on volume faces to split them into triangles
2430 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2432 if ( geomType == SMDSEntity_TriQuad_Hexa )
2434 // remove medium nodes that could become free
2435 for ( int i = 20; i < volTool.NbNodes(); ++i )
2436 if ( volNodes[i]->NbInverseElements() == 0 )
2437 GetMeshDS()->RemoveNode( volNodes[i] );
2439 } // loop on volumes to split
2441 myLastCreatedNodes = newNodes;
2442 myLastCreatedElems = newElems;
2445 //=======================================================================
2446 //function : GetHexaFacetsToSplit
2447 //purpose : For hexahedra that will be split into prisms, finds facets to
2448 // split into triangles. Only hexahedra adjacent to the one closest
2449 // to theFacetNormal.Location() are returned.
2450 //param [in,out] theHexas - the hexahedra
2451 //param [in] theFacetNormal - facet normal
2452 //param [out] theFacets - the hexahedra and found facet IDs
2453 //=======================================================================
2455 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2456 const gp_Ax1& theFacetNormal,
2457 TFacetOfElem & theFacets)
2459 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2461 // Find a hexa closest to the location of theFacetNormal
2463 const SMDS_MeshElement* startHex;
2465 // get SMDS_ElemIteratorPtr on theHexas
2466 typedef const SMDS_MeshElement* TValue;
2467 typedef TIDSortedElemSet::iterator TSetIterator;
2468 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2469 typedef SMDS_MeshElement::GeomFilter TFilter;
2470 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2471 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2472 ( new TElemSetIter( theHexas.begin(),
2474 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2476 SMESH_ElementSearcher* searcher =
2477 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2479 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2484 throw SALOME_Exception( THIS_METHOD "startHex not found");
2487 // Select a facet of startHex by theFacetNormal
2489 SMDS_VolumeTool vTool( startHex );
2490 double norm[3], dot, maxDot = 0;
2492 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2493 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2495 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2503 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2505 // Fill theFacets starting from facetID of startHex
2507 // facets used for searching of volumes adjacent to already treated ones
2508 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2509 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2510 TFacetMap facetsToCheck;
2512 set<const SMDS_MeshNode*> facetNodes;
2513 const SMDS_MeshElement* curHex;
2515 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2519 // move in two directions from startHex via facetID
2520 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2523 int curFacet = facetID;
2524 if ( is2nd ) // do not treat startHex twice
2526 vTool.Set( curHex );
2527 if ( vTool.IsFreeFace( curFacet, &curHex ))
2533 vTool.GetFaceNodes( curFacet, facetNodes );
2534 vTool.Set( curHex );
2535 curFacet = vTool.GetFaceIndex( facetNodes );
2540 // store a facet to split
2541 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2543 theFacets.insert( make_pair( curHex, -1 ));
2546 if ( !allHex && !theHexas.count( curHex ))
2549 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2550 theFacets.insert( make_pair( curHex, curFacet ));
2551 if ( !facetIt2isNew.second )
2554 // remember not-to-split facets in facetsToCheck
2555 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2556 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2558 if ( iF == curFacet && iF == oppFacet )
2560 TVolumeFaceKey facetKey ( vTool, iF );
2561 TElemFacets elemFacet( facetIt2isNew.first, iF );
2562 pair< TFacetMap::iterator, bool > it2isnew =
2563 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2564 if ( !it2isnew.second )
2565 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2567 // pass to a volume adjacent via oppFacet
2568 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2574 // get a new curFacet
2575 vTool.GetFaceNodes( oppFacet, facetNodes );
2576 vTool.Set( curHex );
2577 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2580 } // move in two directions from startHex via facetID
2582 // Find a new startHex by facetsToCheck
2586 TFacetMap::iterator fIt = facetsToCheck.begin();
2587 while ( !startHex && fIt != facetsToCheck.end() )
2589 const TElemFacets& elemFacets = fIt->second;
2590 const SMDS_MeshElement* hex = elemFacets.first->first;
2591 int splitFacet = elemFacets.first->second;
2592 int lateralFacet = elemFacets.second;
2593 facetsToCheck.erase( fIt );
2594 fIt = facetsToCheck.begin();
2597 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2598 curHex->GetGeomType() != SMDSGeom_HEXA )
2600 if ( !allHex && !theHexas.count( curHex ))
2605 // find a facet of startHex to split
2607 set<const SMDS_MeshNode*> lateralNodes;
2608 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2609 vTool.GetFaceNodes( splitFacet, facetNodes );
2610 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2611 vTool.Set( startHex );
2612 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2614 // look for a facet of startHex having common nodes with facetNodes
2615 // but not lateralFacet
2616 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2618 if ( iF == lateralFacet )
2620 int nbCommonNodes = 0;
2621 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2622 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2623 nbCommonNodes += facetNodes.count( nn[ iN ]);
2625 if ( nbCommonNodes >= 2 )
2632 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2634 } // while ( startHex )
2641 //================================================================================
2643 * \brief Selects nodes of several elements according to a given interlace
2644 * \param [in] srcNodes - nodes to select from
2645 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2646 * \param [in] interlace - indices of nodes for all elements
2647 * \param [in] nbElems - nb of elements
2648 * \param [in] nbNodes - nb of nodes in each element
2649 * \param [in] mesh - the mesh
2650 * \param [out] elemQueue - a list to push elements found by the selected nodes
2651 * \param [in] type - type of elements to look for
2653 //================================================================================
2655 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2656 vector< const SMDS_MeshNode* >* tgtNodesVec,
2657 const int* interlace,
2660 SMESHDS_Mesh* mesh = 0,
2661 list< const SMDS_MeshElement* >* elemQueue=0,
2662 SMDSAbs_ElementType type=SMDSAbs_All)
2664 for ( int iE = 0; iE < nbElems; ++iE )
2666 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2667 const int* select = & interlace[iE*nbNodes];
2668 elemNodes.resize( nbNodes );
2669 for ( int iN = 0; iN < nbNodes; ++iN )
2670 elemNodes[iN] = srcNodes[ select[ iN ]];
2672 const SMDS_MeshElement* e;
2674 for ( int iE = 0; iE < nbElems; ++iE )
2675 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2676 elemQueue->push_back( e );
2680 //=======================================================================
2682 * Split bi-quadratic elements into linear ones without creation of additional nodes
2683 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2684 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2685 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2686 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2687 * will be split in order to keep the mesh conformal.
2688 * \param elems - elements to split
2690 //=======================================================================
2692 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2694 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2695 vector<const SMDS_MeshElement* > splitElems;
2696 list< const SMDS_MeshElement* > elemQueue;
2697 list< const SMDS_MeshElement* >::iterator elemIt;
2699 SMESHDS_Mesh * mesh = GetMeshDS();
2700 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2701 int nbElems, nbNodes;
2703 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2704 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2707 elemQueue.push_back( *elemSetIt );
2708 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2710 const SMDS_MeshElement* elem = *elemIt;
2711 switch( elem->GetEntityType() )
2713 case SMDSEntity_TriQuad_Hexa: // HEX27
2715 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2716 nbElems = nbNodes = 8;
2717 elemType = & hexaType;
2719 // get nodes for new elements
2720 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2721 { 1,9,20,8, 17,22,26,21 },
2722 { 2,10,20,9, 18,23,26,22 },
2723 { 3,11,20,10, 19,24,26,23 },
2724 { 16,21,26,24, 4,12,25,15 },
2725 { 17,22,26,21, 5,13,25,12 },
2726 { 18,23,26,22, 6,14,25,13 },
2727 { 19,24,26,23, 7,15,25,14 }};
2728 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2730 // add boundary faces to elemQueue
2731 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2732 { 4,5,6,7, 12,13,14,15, 25 },
2733 { 0,1,5,4, 8,17,12,16, 21 },
2734 { 1,2,6,5, 9,18,13,17, 22 },
2735 { 2,3,7,6, 10,19,14,18, 23 },
2736 { 3,0,4,7, 11,16,15,19, 24 }};
2737 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2739 // add boundary segments to elemQueue
2740 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2741 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2742 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2743 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2746 case SMDSEntity_BiQuad_Triangle: // TRIA7
2748 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2751 elemType = & quadType;
2753 // get nodes for new elements
2754 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2755 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2757 // add boundary segments to elemQueue
2758 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2759 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2762 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2764 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2767 elemType = & quadType;
2769 // get nodes for new elements
2770 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2771 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2773 // add boundary segments to elemQueue
2774 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2775 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2778 case SMDSEntity_Quad_Edge:
2780 if ( elemIt == elemQueue.begin() )
2781 continue; // an elem is in theElems
2782 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2785 elemType = & segType;
2787 // get nodes for new elements
2788 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2789 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2793 } // switch( elem->GetEntityType() )
2795 // Create new elements
2797 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2801 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2802 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2803 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2804 //elemType->SetID( -1 );
2806 for ( int iE = 0; iE < nbElems; ++iE )
2807 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2810 ReplaceElemInGroups( elem, splitElems, mesh );
2813 for ( size_t i = 0; i < splitElems.size(); ++i )
2814 subMesh->AddElement( splitElems[i] );
2819 //=======================================================================
2820 //function : AddToSameGroups
2821 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2822 //=======================================================================
2824 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2825 const SMDS_MeshElement* elemInGroups,
2826 SMESHDS_Mesh * aMesh)
2828 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2829 if (!groups.empty()) {
2830 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2831 for ( ; grIt != groups.end(); grIt++ ) {
2832 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2833 if ( group && group->Contains( elemInGroups ))
2834 group->SMDSGroup().Add( elemToAdd );
2840 //=======================================================================
2841 //function : RemoveElemFromGroups
2842 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2843 //=======================================================================
2844 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2845 SMESHDS_Mesh * aMesh)
2847 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2848 if (!groups.empty())
2850 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2851 for (; GrIt != groups.end(); GrIt++)
2853 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2854 if (!grp || grp->IsEmpty()) continue;
2855 grp->SMDSGroup().Remove(removeelem);
2860 //================================================================================
2862 * \brief Replace elemToRm by elemToAdd in the all groups
2864 //================================================================================
2866 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2867 const SMDS_MeshElement* elemToAdd,
2868 SMESHDS_Mesh * aMesh)
2870 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2871 if (!groups.empty()) {
2872 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2873 for ( ; grIt != groups.end(); grIt++ ) {
2874 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2875 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2876 group->SMDSGroup().Add( elemToAdd );
2881 //================================================================================
2883 * \brief Replace elemToRm by elemToAdd in the all groups
2885 //================================================================================
2887 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2888 const vector<const SMDS_MeshElement*>& elemToAdd,
2889 SMESHDS_Mesh * aMesh)
2891 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2892 if (!groups.empty())
2894 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2895 for ( ; grIt != groups.end(); grIt++ ) {
2896 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2897 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2898 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2899 group->SMDSGroup().Add( elemToAdd[ i ] );
2904 //=======================================================================
2905 //function : QuadToTri
2906 //purpose : Cut quadrangles into triangles.
2907 // theCrit is used to select a diagonal to cut
2908 //=======================================================================
2910 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2911 const bool the13Diag)
2914 myLastCreatedElems.reserve( theElems.size() * 2 );
2916 SMESHDS_Mesh * aMesh = GetMeshDS();
2917 Handle(Geom_Surface) surface;
2918 SMESH_MesherHelper helper( *GetMesh() );
2920 TIDSortedElemSet::iterator itElem;
2921 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2923 const SMDS_MeshElement* elem = *itElem;
2924 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2927 if ( elem->NbNodes() == 4 ) {
2928 // retrieve element nodes
2929 const SMDS_MeshNode* aNodes [4];
2930 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2932 while ( itN->more() )
2933 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2935 int aShapeId = FindShape( elem );
2936 const SMDS_MeshElement* newElem1 = 0;
2937 const SMDS_MeshElement* newElem2 = 0;
2939 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2940 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2943 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2944 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2946 myLastCreatedElems.push_back(newElem1);
2947 myLastCreatedElems.push_back(newElem2);
2948 // put a new triangle on the same shape and add to the same groups
2951 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2952 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2954 AddToSameGroups( newElem1, elem, aMesh );
2955 AddToSameGroups( newElem2, elem, aMesh );
2956 aMesh->RemoveElement( elem );
2959 // Quadratic quadrangle
2961 else if ( elem->NbNodes() >= 8 )
2963 // get surface elem is on
2964 int aShapeId = FindShape( elem );
2965 if ( aShapeId != helper.GetSubShapeID() ) {
2969 shape = aMesh->IndexToShape( aShapeId );
2970 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2971 TopoDS_Face face = TopoDS::Face( shape );
2972 surface = BRep_Tool::Surface( face );
2973 if ( !surface.IsNull() )
2974 helper.SetSubShape( shape );
2978 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
2979 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2980 for ( int i = 0; itN->more(); ++i )
2981 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2983 const SMDS_MeshNode* centrNode = aNodes[8];
2984 if ( centrNode == 0 )
2986 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2987 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
2989 myLastCreatedNodes.push_back(centrNode);
2992 // create a new element
2993 const SMDS_MeshElement* newElem1 = 0;
2994 const SMDS_MeshElement* newElem2 = 0;
2996 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2997 aNodes[6], aNodes[7], centrNode );
2998 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2999 centrNode, aNodes[4], aNodes[5] );
3002 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3003 aNodes[7], aNodes[4], centrNode );
3004 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3005 centrNode, aNodes[5], aNodes[6] );
3007 myLastCreatedElems.push_back(newElem1);
3008 myLastCreatedElems.push_back(newElem2);
3009 // put a new triangle on the same shape and add to the same groups
3012 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3013 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3015 AddToSameGroups( newElem1, elem, aMesh );
3016 AddToSameGroups( newElem2, elem, aMesh );
3017 aMesh->RemoveElement( elem );
3024 //=======================================================================
3025 //function : getAngle
3027 //=======================================================================
3029 double getAngle(const SMDS_MeshElement * tr1,
3030 const SMDS_MeshElement * tr2,
3031 const SMDS_MeshNode * n1,
3032 const SMDS_MeshNode * n2)
3034 double angle = 2. * M_PI; // bad angle
3037 SMESH::Controls::TSequenceOfXYZ P1, P2;
3038 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3039 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3042 if(!tr1->IsQuadratic())
3043 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3045 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3046 if ( N1.SquareMagnitude() <= gp::Resolution() )
3048 if(!tr2->IsQuadratic())
3049 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3051 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3052 if ( N2.SquareMagnitude() <= gp::Resolution() )
3055 // find the first diagonal node n1 in the triangles:
3056 // take in account a diagonal link orientation
3057 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3058 for ( int t = 0; t < 2; t++ ) {
3059 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3060 int i = 0, iDiag = -1;
3061 while ( it->more()) {
3062 const SMDS_MeshElement *n = it->next();
3063 if ( n == n1 || n == n2 ) {
3067 if ( i - iDiag == 1 )
3068 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3077 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3080 angle = N1.Angle( N2 );
3085 // =================================================
3086 // class generating a unique ID for a pair of nodes
3087 // and able to return nodes by that ID
3088 // =================================================
3092 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3093 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3096 long GetLinkID (const SMDS_MeshNode * n1,
3097 const SMDS_MeshNode * n2) const
3099 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3102 bool GetNodes (const long theLinkID,
3103 const SMDS_MeshNode* & theNode1,
3104 const SMDS_MeshNode* & theNode2) const
3106 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3107 if ( !theNode1 ) return false;
3108 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3109 if ( !theNode2 ) return false;
3115 const SMESHDS_Mesh* myMesh;
3120 //=======================================================================
3121 //function : TriToQuad
3122 //purpose : Fuse neighbour triangles into quadrangles.
3123 // theCrit is used to select a neighbour to fuse with.
3124 // theMaxAngle is a max angle between element normals at which
3125 // fusion is still performed.
3126 //=======================================================================
3128 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3129 SMESH::Controls::NumericalFunctorPtr theCrit,
3130 const double theMaxAngle)
3133 myLastCreatedElems.reserve( theElems.size() / 2 );
3135 if ( !theCrit.get() )
3138 SMESHDS_Mesh * aMesh = GetMeshDS();
3140 // Prepare data for algo: build
3141 // 1. map of elements with their linkIDs
3142 // 2. map of linkIDs with their elements
3144 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3145 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3146 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3147 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3149 TIDSortedElemSet::iterator itElem;
3150 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3152 const SMDS_MeshElement* elem = *itElem;
3153 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3154 bool IsTria = ( elem->NbCornerNodes()==3 );
3155 if (!IsTria) continue;
3157 // retrieve element nodes
3158 const SMDS_MeshNode* aNodes [4];
3159 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3162 aNodes[ i++ ] = itN->next();
3163 aNodes[ 3 ] = aNodes[ 0 ];
3166 for ( i = 0; i < 3; i++ ) {
3167 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3168 // check if elements sharing a link can be fused
3169 itLE = mapLi_listEl.find( link );
3170 if ( itLE != mapLi_listEl.end() ) {
3171 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3173 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3174 //if ( FindShape( elem ) != FindShape( elem2 ))
3175 // continue; // do not fuse triangles laying on different shapes
3176 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3177 continue; // avoid making badly shaped quads
3178 (*itLE).second.push_back( elem );
3181 mapLi_listEl[ link ].push_back( elem );
3183 mapEl_setLi [ elem ].insert( link );
3186 // Clean the maps from the links shared by a sole element, ie
3187 // links to which only one element is bound in mapLi_listEl
3189 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3190 int nbElems = (*itLE).second.size();
3191 if ( nbElems < 2 ) {
3192 const SMDS_MeshElement* elem = (*itLE).second.front();
3193 SMESH_TLink link = (*itLE).first;
3194 mapEl_setLi[ elem ].erase( link );
3195 if ( mapEl_setLi[ elem ].empty() )
3196 mapEl_setLi.erase( elem );
3200 // Algo: fuse triangles into quadrangles
3202 while ( ! mapEl_setLi.empty() ) {
3203 // Look for the start element:
3204 // the element having the least nb of shared links
3205 const SMDS_MeshElement* startElem = 0;
3207 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3208 int nbLinks = (*itEL).second.size();
3209 if ( nbLinks < minNbLinks ) {
3210 startElem = (*itEL).first;
3211 minNbLinks = nbLinks;
3212 if ( minNbLinks == 1 )
3217 // search elements to fuse starting from startElem or links of elements
3218 // fused earlyer - startLinks
3219 list< SMESH_TLink > startLinks;
3220 while ( startElem || !startLinks.empty() ) {
3221 while ( !startElem && !startLinks.empty() ) {
3222 // Get an element to start, by a link
3223 SMESH_TLink linkId = startLinks.front();
3224 startLinks.pop_front();
3225 itLE = mapLi_listEl.find( linkId );
3226 if ( itLE != mapLi_listEl.end() ) {
3227 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3228 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3229 for ( ; itE != listElem.end() ; itE++ )
3230 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3232 mapLi_listEl.erase( itLE );
3237 // Get candidates to be fused
3238 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3239 const SMESH_TLink *link12 = 0, *link13 = 0;
3241 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3242 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3243 ASSERT( !setLi.empty() );
3244 set< SMESH_TLink >::iterator itLi;
3245 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3247 const SMESH_TLink & link = (*itLi);
3248 itLE = mapLi_listEl.find( link );
3249 if ( itLE == mapLi_listEl.end() )
3252 const SMDS_MeshElement* elem = (*itLE).second.front();
3254 elem = (*itLE).second.back();
3255 mapLi_listEl.erase( itLE );
3256 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3267 // add other links of elem to list of links to re-start from
3268 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3269 set< SMESH_TLink >::iterator it;
3270 for ( it = links.begin(); it != links.end(); it++ ) {
3271 const SMESH_TLink& link2 = (*it);
3272 if ( link2 != link )
3273 startLinks.push_back( link2 );
3277 // Get nodes of possible quadrangles
3278 const SMDS_MeshNode *n12 [4], *n13 [4];
3279 bool Ok12 = false, Ok13 = false;
3280 const SMDS_MeshNode *linkNode1, *linkNode2;
3282 linkNode1 = link12->first;
3283 linkNode2 = link12->second;
3284 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3288 linkNode1 = link13->first;
3289 linkNode2 = link13->second;
3290 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3294 // Choose a pair to fuse
3295 if ( Ok12 && Ok13 ) {
3296 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3297 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3298 double aBadRate12 = getBadRate( &quad12, theCrit );
3299 double aBadRate13 = getBadRate( &quad13, theCrit );
3300 if ( aBadRate13 < aBadRate12 )
3307 // and remove fused elems and remove links from the maps
3308 mapEl_setLi.erase( tr1 );
3311 mapEl_setLi.erase( tr2 );
3312 mapLi_listEl.erase( *link12 );
3313 if ( tr1->NbNodes() == 3 )
3315 const SMDS_MeshElement* newElem = 0;
3316 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3317 myLastCreatedElems.push_back(newElem);
3318 AddToSameGroups( newElem, tr1, aMesh );
3319 int aShapeId = tr1->getshapeId();
3321 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3322 aMesh->RemoveElement( tr1 );
3323 aMesh->RemoveElement( tr2 );
3326 vector< const SMDS_MeshNode* > N1;
3327 vector< const SMDS_MeshNode* > N2;
3328 getNodesFromTwoTria(tr1,tr2,N1,N2);
3329 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3330 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3331 // i.e. first nodes from both arrays form a new diagonal
3332 const SMDS_MeshNode* aNodes[8];
3341 const SMDS_MeshElement* newElem = 0;
3342 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3343 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3344 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3346 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3347 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3348 myLastCreatedElems.push_back(newElem);
3349 AddToSameGroups( newElem, tr1, aMesh );
3350 int aShapeId = tr1->getshapeId();
3352 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3353 aMesh->RemoveElement( tr1 );
3354 aMesh->RemoveElement( tr2 );
3355 // remove middle node (9)
3356 if ( N1[4]->NbInverseElements() == 0 )
3357 aMesh->RemoveNode( N1[4] );
3358 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3359 aMesh->RemoveNode( N1[6] );
3360 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3361 aMesh->RemoveNode( N2[6] );
3366 mapEl_setLi.erase( tr3 );
3367 mapLi_listEl.erase( *link13 );
3368 if ( tr1->NbNodes() == 3 ) {
3369 const SMDS_MeshElement* newElem = 0;
3370 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3371 myLastCreatedElems.push_back(newElem);
3372 AddToSameGroups( newElem, tr1, aMesh );
3373 int aShapeId = tr1->getshapeId();
3375 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3376 aMesh->RemoveElement( tr1 );
3377 aMesh->RemoveElement( tr3 );
3380 vector< const SMDS_MeshNode* > N1;
3381 vector< const SMDS_MeshNode* > N2;
3382 getNodesFromTwoTria(tr1,tr3,N1,N2);
3383 // now we receive following N1 and N2 (using numeration as above image)
3384 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3385 // i.e. first nodes from both arrays form a new diagonal
3386 const SMDS_MeshNode* aNodes[8];
3395 const SMDS_MeshElement* newElem = 0;
3396 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3397 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3398 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3400 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3401 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3402 myLastCreatedElems.push_back(newElem);
3403 AddToSameGroups( newElem, tr1, aMesh );
3404 int aShapeId = tr1->getshapeId();
3406 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3407 aMesh->RemoveElement( tr1 );
3408 aMesh->RemoveElement( tr3 );
3409 // remove middle node (9)
3410 if ( N1[4]->NbInverseElements() == 0 )
3411 aMesh->RemoveNode( N1[4] );
3412 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3413 aMesh->RemoveNode( N1[6] );
3414 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3415 aMesh->RemoveNode( N2[6] );
3419 // Next element to fuse: the rejected one
3421 startElem = Ok12 ? tr3 : tr2;
3423 } // if ( startElem )
3424 } // while ( startElem || !startLinks.empty() )
3425 } // while ( ! mapEl_setLi.empty() )
3430 //================================================================================
3432 * \brief Return nodes linked to the given one
3433 * \param theNode - the node
3434 * \param linkedNodes - the found nodes
3435 * \param type - the type of elements to check
3437 * Medium nodes are ignored
3439 //================================================================================
3441 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3442 TIDSortedElemSet & linkedNodes,
3443 SMDSAbs_ElementType type )
3445 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3446 while ( elemIt->more() )
3448 const SMDS_MeshElement* elem = elemIt->next();
3449 if(elem->GetType() == SMDSAbs_0DElement)
3452 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3453 if ( elem->GetType() == SMDSAbs_Volume )
3455 SMDS_VolumeTool vol( elem );
3456 while ( nodeIt->more() ) {
3457 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3458 if ( theNode != n && vol.IsLinked( theNode, n ))
3459 linkedNodes.insert( n );
3464 for ( int i = 0; nodeIt->more(); ++i ) {
3465 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3466 if ( n == theNode ) {
3467 int iBefore = i - 1;
3469 if ( elem->IsQuadratic() ) {
3470 int nb = elem->NbNodes() / 2;
3471 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3472 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3474 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3475 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3482 //=======================================================================
3483 //function : laplacianSmooth
3484 //purpose : pulls theNode toward the center of surrounding nodes directly
3485 // connected to that node along an element edge
3486 //=======================================================================
3488 void laplacianSmooth(const SMDS_MeshNode* theNode,
3489 const Handle(Geom_Surface)& theSurface,
3490 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3492 // find surrounding nodes
3494 TIDSortedElemSet nodeSet;
3495 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3497 // compute new coodrs
3499 double coord[] = { 0., 0., 0. };
3500 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3501 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3502 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3503 if ( theSurface.IsNull() ) { // smooth in 3D
3504 coord[0] += node->X();
3505 coord[1] += node->Y();
3506 coord[2] += node->Z();
3508 else { // smooth in 2D
3509 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3510 gp_XY* uv = theUVMap[ node ];
3511 coord[0] += uv->X();
3512 coord[1] += uv->Y();
3515 int nbNodes = nodeSet.size();
3518 coord[0] /= nbNodes;
3519 coord[1] /= nbNodes;
3521 if ( !theSurface.IsNull() ) {
3522 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3523 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3524 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3530 coord[2] /= nbNodes;
3534 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3537 //=======================================================================
3538 //function : centroidalSmooth
3539 //purpose : pulls theNode toward the element-area-weighted centroid of the
3540 // surrounding elements
3541 //=======================================================================
3543 void centroidalSmooth(const SMDS_MeshNode* theNode,
3544 const Handle(Geom_Surface)& theSurface,
3545 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3547 gp_XYZ aNewXYZ(0.,0.,0.);
3548 SMESH::Controls::Area anAreaFunc;
3549 double totalArea = 0.;
3554 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3555 while ( elemIt->more() )
3557 const SMDS_MeshElement* elem = elemIt->next();
3560 gp_XYZ elemCenter(0.,0.,0.);
3561 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3562 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3563 int nn = elem->NbNodes();
3564 if(elem->IsQuadratic()) nn = nn/2;
3566 //while ( itN->more() ) {
3568 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3570 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3571 aNodePoints.push_back( aP );
3572 if ( !theSurface.IsNull() ) { // smooth in 2D
3573 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3574 gp_XY* uv = theUVMap[ aNode ];
3575 aP.SetCoord( uv->X(), uv->Y(), 0. );
3579 double elemArea = anAreaFunc.GetValue( aNodePoints );
3580 totalArea += elemArea;
3582 aNewXYZ += elemCenter * elemArea;
3584 aNewXYZ /= totalArea;
3585 if ( !theSurface.IsNull() ) {
3586 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3587 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3592 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3595 //=======================================================================
3596 //function : getClosestUV
3597 //purpose : return UV of closest projection
3598 //=======================================================================
3600 static bool getClosestUV (Extrema_GenExtPS& projector,
3601 const gp_Pnt& point,
3604 projector.Perform( point );
3605 if ( projector.IsDone() ) {
3606 double u, v, minVal = DBL_MAX;
3607 for ( int i = projector.NbExt(); i > 0; i-- )
3608 if ( projector.SquareDistance( i ) < minVal ) {
3609 minVal = projector.SquareDistance( i );
3610 projector.Point( i ).Parameter( u, v );
3612 result.SetCoord( u, v );
3618 //=======================================================================
3620 //purpose : Smooth theElements during theNbIterations or until a worst
3621 // element has aspect ratio <= theTgtAspectRatio.
3622 // Aspect Ratio varies in range [1.0, inf].
3623 // If theElements is empty, the whole mesh is smoothed.
3624 // theFixedNodes contains additionally fixed nodes. Nodes built
3625 // on edges and boundary nodes are always fixed.
3626 //=======================================================================
3628 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3629 set<const SMDS_MeshNode*> & theFixedNodes,
3630 const SmoothMethod theSmoothMethod,
3631 const int theNbIterations,
3632 double theTgtAspectRatio,
3637 if ( theTgtAspectRatio < 1.0 )
3638 theTgtAspectRatio = 1.0;
3640 const double disttol = 1.e-16;
3642 SMESH::Controls::AspectRatio aQualityFunc;
3644 SMESHDS_Mesh* aMesh = GetMeshDS();
3646 if ( theElems.empty() ) {
3647 // add all faces to theElems
3648 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3649 while ( fIt->more() ) {
3650 const SMDS_MeshElement* face = fIt->next();
3651 theElems.insert( theElems.end(), face );
3654 // get all face ids theElems are on
3655 set< int > faceIdSet;
3656 TIDSortedElemSet::iterator itElem;
3658 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3659 int fId = FindShape( *itElem );
3660 // check that corresponding submesh exists and a shape is face
3662 faceIdSet.find( fId ) == faceIdSet.end() &&
3663 aMesh->MeshElements( fId )) {
3664 TopoDS_Shape F = aMesh->IndexToShape( fId );
3665 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3666 faceIdSet.insert( fId );
3669 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3671 // ===============================================
3672 // smooth elements on each TopoDS_Face separately
3673 // ===============================================
3675 SMESH_MesherHelper helper( *GetMesh() );
3677 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3678 for ( ; fId != faceIdSet.rend(); ++fId )
3680 // get face surface and submesh
3681 Handle(Geom_Surface) surface;
3682 SMESHDS_SubMesh* faceSubMesh = 0;
3685 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3686 bool isUPeriodic = false, isVPeriodic = false;
3689 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3690 surface = BRep_Tool::Surface( face );
3691 faceSubMesh = aMesh->MeshElements( *fId );
3692 fToler2 = BRep_Tool::Tolerance( face );
3693 fToler2 *= fToler2 * 10.;
3694 isUPeriodic = surface->IsUPeriodic();
3695 // if ( isUPeriodic )
3696 // surface->UPeriod();
3697 isVPeriodic = surface->IsVPeriodic();
3698 // if ( isVPeriodic )
3699 // surface->VPeriod();
3700 surface->Bounds( u1, u2, v1, v2 );
3701 helper.SetSubShape( face );
3703 // ---------------------------------------------------------
3704 // for elements on a face, find movable and fixed nodes and
3705 // compute UV for them
3706 // ---------------------------------------------------------
3707 bool checkBoundaryNodes = false;
3708 bool isQuadratic = false;
3709 set<const SMDS_MeshNode*> setMovableNodes;
3710 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3711 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3712 list< const SMDS_MeshElement* > elemsOnFace;
3714 Extrema_GenExtPS projector;
3715 GeomAdaptor_Surface surfAdaptor;
3716 if ( !surface.IsNull() ) {
3717 surfAdaptor.Load( surface );
3718 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3720 int nbElemOnFace = 0;
3721 itElem = theElems.begin();
3722 // loop on not yet smoothed elements: look for elems on a face
3723 while ( itElem != theElems.end() )
3725 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3726 break; // all elements found
3728 const SMDS_MeshElement* elem = *itElem;
3729 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3730 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3734 elemsOnFace.push_back( elem );
3735 theElems.erase( itElem++ );
3739 isQuadratic = elem->IsQuadratic();
3741 // get movable nodes of elem
3742 const SMDS_MeshNode* node;
3743 SMDS_TypeOfPosition posType;
3744 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3745 int nn = 0, nbn = elem->NbNodes();
3746 if(elem->IsQuadratic())
3748 while ( nn++ < nbn ) {
3749 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3750 const SMDS_PositionPtr& pos = node->GetPosition();
3751 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3752 if (posType != SMDS_TOP_EDGE &&
3753 posType != SMDS_TOP_VERTEX &&
3754 theFixedNodes.find( node ) == theFixedNodes.end())
3756 // check if all faces around the node are on faceSubMesh
3757 // because a node on edge may be bound to face
3759 if ( faceSubMesh ) {
3760 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3761 while ( eIt->more() && all ) {
3762 const SMDS_MeshElement* e = eIt->next();
3763 all = faceSubMesh->Contains( e );
3767 setMovableNodes.insert( node );
3769 checkBoundaryNodes = true;
3771 if ( posType == SMDS_TOP_3DSPACE )
3772 checkBoundaryNodes = true;
3775 if ( surface.IsNull() )
3778 // get nodes to check UV
3779 list< const SMDS_MeshNode* > uvCheckNodes;
3780 const SMDS_MeshNode* nodeInFace = 0;
3781 itN = elem->nodesIterator();
3782 nn = 0; nbn = elem->NbNodes();
3783 if(elem->IsQuadratic())
3785 while ( nn++ < nbn ) {
3786 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3787 if ( node->GetPosition()->GetDim() == 2 )
3789 if ( uvMap.find( node ) == uvMap.end() )
3790 uvCheckNodes.push_back( node );
3791 // add nodes of elems sharing node
3792 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3793 // while ( eIt->more() ) {
3794 // const SMDS_MeshElement* e = eIt->next();
3795 // if ( e != elem ) {
3796 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3797 // while ( nIt->more() ) {
3798 // const SMDS_MeshNode* n =
3799 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3800 // if ( uvMap.find( n ) == uvMap.end() )
3801 // uvCheckNodes.push_back( n );
3807 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3808 for ( ; n != uvCheckNodes.end(); ++n ) {
3811 const SMDS_PositionPtr& pos = node->GetPosition();
3812 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3816 bool toCheck = true;
3817 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3819 // compute not existing UV
3820 bool project = ( posType == SMDS_TOP_3DSPACE );
3821 // double dist1 = DBL_MAX, dist2 = 0;
3822 // if ( posType != SMDS_TOP_3DSPACE ) {
3823 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3824 // project = dist1 > fToler2;
3826 if ( project ) { // compute new UV
3828 gp_Pnt pNode = SMESH_NodeXYZ( node );
3829 if ( !getClosestUV( projector, pNode, newUV )) {
3830 MESSAGE("Node Projection Failed " << node);
3834 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3836 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3838 // if ( posType != SMDS_TOP_3DSPACE )
3839 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3840 // if ( dist2 < dist1 )
3844 // store UV in the map
3845 listUV.push_back( uv );
3846 uvMap.insert( make_pair( node, &listUV.back() ));
3848 } // loop on not yet smoothed elements
3850 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3851 checkBoundaryNodes = true;
3853 // fix nodes on mesh boundary
3855 if ( checkBoundaryNodes ) {
3856 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3857 map< SMESH_TLink, int >::iterator link_nb;
3858 // put all elements links to linkNbMap
3859 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3860 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3861 const SMDS_MeshElement* elem = (*elemIt);
3862 int nbn = elem->NbCornerNodes();
3863 // loop on elem links: insert them in linkNbMap
3864 for ( int iN = 0; iN < nbn; ++iN ) {
3865 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3866 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3867 SMESH_TLink link( n1, n2 );
3868 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3872 // remove nodes that are in links encountered only once from setMovableNodes
3873 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3874 if ( link_nb->second == 1 ) {
3875 setMovableNodes.erase( link_nb->first.node1() );
3876 setMovableNodes.erase( link_nb->first.node2() );
3881 // -----------------------------------------------------
3882 // for nodes on seam edge, compute one more UV ( uvMap2 );
3883 // find movable nodes linked to nodes on seam and which
3884 // are to be smoothed using the second UV ( uvMap2 )
3885 // -----------------------------------------------------
3887 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3888 if ( !surface.IsNull() ) {
3889 TopExp_Explorer eExp( face, TopAbs_EDGE );
3890 for ( ; eExp.More(); eExp.Next() ) {
3891 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3892 if ( !BRep_Tool::IsClosed( edge, face ))
3894 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3895 if ( !sm ) continue;
3896 // find out which parameter varies for a node on seam
3899 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3900 if ( pcurve.IsNull() ) continue;
3901 uv1 = pcurve->Value( f );
3903 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3904 if ( pcurve.IsNull() ) continue;
3905 uv2 = pcurve->Value( f );
3906 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3908 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3909 std::swap( uv1, uv2 );
3910 // get nodes on seam and its vertices
3911 list< const SMDS_MeshNode* > seamNodes;
3912 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3913 while ( nSeamIt->more() ) {
3914 const SMDS_MeshNode* node = nSeamIt->next();
3915 if ( !isQuadratic || !IsMedium( node ))
3916 seamNodes.push_back( node );
3918 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3919 for ( ; vExp.More(); vExp.Next() ) {
3920 sm = aMesh->MeshElements( vExp.Current() );
3922 nSeamIt = sm->GetNodes();
3923 while ( nSeamIt->more() )
3924 seamNodes.push_back( nSeamIt->next() );
3927 // loop on nodes on seam
3928 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3929 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3930 const SMDS_MeshNode* nSeam = *noSeIt;
3931 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3932 if ( n_uv == uvMap.end() )
3935 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3936 // set the second UV
3937 listUV.push_back( *n_uv->second );
3938 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3939 if ( uvMap2.empty() )
3940 uvMap2 = uvMap; // copy the uvMap contents
3941 uvMap2[ nSeam ] = &listUV.back();
3943 // collect movable nodes linked to ones on seam in nodesNearSeam
3944 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3945 while ( eIt->more() ) {
3946 const SMDS_MeshElement* e = eIt->next();
3947 int nbUseMap1 = 0, nbUseMap2 = 0;
3948 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3949 int nn = 0, nbn = e->NbNodes();
3950 if(e->IsQuadratic()) nbn = nbn/2;
3951 while ( nn++ < nbn )
3953 const SMDS_MeshNode* n =
3954 static_cast<const SMDS_MeshNode*>( nIt->next() );
3956 setMovableNodes.find( n ) == setMovableNodes.end() )
3958 // add only nodes being closer to uv2 than to uv1
3959 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3960 // 0.5 * ( n->Y() + nSeam->Y() ),
3961 // 0.5 * ( n->Z() + nSeam->Z() ));
3963 // getClosestUV( projector, pMid, uv );
3964 double x = uvMap[ n ]->Coord( iPar );
3965 if ( Abs( uv1.Coord( iPar ) - x ) >
3966 Abs( uv2.Coord( iPar ) - x )) {
3967 nodesNearSeam.insert( n );
3973 // for centroidalSmooth all element nodes must
3974 // be on one side of a seam
3975 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3976 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3978 while ( nn++ < nbn ) {
3979 const SMDS_MeshNode* n =
3980 static_cast<const SMDS_MeshNode*>( nIt->next() );
3981 setMovableNodes.erase( n );
3985 } // loop on nodes on seam
3986 } // loop on edge of a face
3987 } // if ( !face.IsNull() )
3989 if ( setMovableNodes.empty() ) {
3990 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3991 continue; // goto next face
3999 double maxRatio = -1., maxDisplacement = -1.;
4000 set<const SMDS_MeshNode*>::iterator nodeToMove;
4001 for ( it = 0; it < theNbIterations; it++ ) {
4002 maxDisplacement = 0.;
4003 nodeToMove = setMovableNodes.begin();
4004 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4005 const SMDS_MeshNode* node = (*nodeToMove);
4006 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4009 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4010 if ( theSmoothMethod == LAPLACIAN )
4011 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4013 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4015 // node displacement
4016 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4017 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4018 if ( aDispl > maxDisplacement )
4019 maxDisplacement = aDispl;
4021 // no node movement => exit
4022 //if ( maxDisplacement < 1.e-16 ) {
4023 if ( maxDisplacement < disttol ) {
4024 MESSAGE("-- no node movement --");
4028 // check elements quality
4030 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4031 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4032 const SMDS_MeshElement* elem = (*elemIt);
4033 if ( !elem || elem->GetType() != SMDSAbs_Face )
4035 SMESH::Controls::TSequenceOfXYZ aPoints;
4036 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4037 double aValue = aQualityFunc.GetValue( aPoints );
4038 if ( aValue > maxRatio )
4042 if ( maxRatio <= theTgtAspectRatio ) {
4043 //MESSAGE("-- quality achieved --");
4046 if (it+1 == theNbIterations) {
4047 //MESSAGE("-- Iteration limit exceeded --");
4049 } // smoothing iterations
4051 // MESSAGE(" Face id: " << *fId <<
4052 // " Nb iterstions: " << it <<
4053 // " Displacement: " << maxDisplacement <<
4054 // " Aspect Ratio " << maxRatio);
4056 // ---------------------------------------
4057 // new nodes positions are computed,
4058 // record movement in DS and set new UV
4059 // ---------------------------------------
4060 nodeToMove = setMovableNodes.begin();
4061 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4062 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4063 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4064 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4065 if ( node_uv != uvMap.end() ) {
4066 gp_XY* uv = node_uv->second;
4068 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4072 // move medium nodes of quadratic elements
4075 vector<const SMDS_MeshNode*> nodes;
4077 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4078 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4080 const SMDS_MeshElement* QF = *elemIt;
4081 if ( QF->IsQuadratic() )
4083 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4084 SMDS_MeshElement::iterator() );
4085 nodes.push_back( nodes[0] );
4087 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4089 if ( !surface.IsNull() )
4091 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4092 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4093 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4094 xyz = surface->Value( uv.X(), uv.Y() );
4097 xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4099 if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4100 // we have to move a medium node
4101 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4107 } // loop on face ids
4113 //=======================================================================
4114 //function : isReverse
4115 //purpose : Return true if normal of prevNodes is not co-directied with
4116 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4117 // iNotSame is where prevNodes and nextNodes are different.
4118 // If result is true then future volume orientation is OK
4119 //=======================================================================
4121 bool isReverse(const SMDS_MeshElement* face,
4122 const vector<const SMDS_MeshNode*>& prevNodes,
4123 const vector<const SMDS_MeshNode*>& nextNodes,
4127 SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4128 SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4129 gp_XYZ extrDir( pN - pP ), faceNorm;
4130 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4132 return faceNorm * extrDir < 0.0;
4135 //================================================================================
4137 * \brief Assure that theElemSets[0] holds elements, not nodes
4139 //================================================================================
4141 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4143 if ( !theElemSets[0].empty() &&
4144 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4146 std::swap( theElemSets[0], theElemSets[1] );
4148 else if ( !theElemSets[1].empty() &&
4149 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4151 std::swap( theElemSets[0], theElemSets[1] );
4156 //=======================================================================
4158 * \brief Create elements by sweeping an element
4159 * \param elem - element to sweep
4160 * \param newNodesItVec - nodes generated from each node of the element
4161 * \param newElems - generated elements
4162 * \param nbSteps - number of sweeping steps
4163 * \param srcElements - to append elem for each generated element
4165 //=======================================================================
4167 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4168 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4169 list<const SMDS_MeshElement*>& newElems,
4170 const size_t nbSteps,
4171 SMESH_SequenceOfElemPtr& srcElements)
4173 SMESHDS_Mesh* aMesh = GetMeshDS();
4175 const int nbNodes = elem->NbNodes();
4176 const int nbCorners = elem->NbCornerNodes();
4177 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4178 polyhedron creation !!! */
4179 // Loop on elem nodes:
4180 // find new nodes and detect same nodes indices
4181 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4182 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4183 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4184 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4186 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4187 vector<int> sames(nbNodes);
4188 vector<bool> isSingleNode(nbNodes);
4190 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4191 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4192 const SMDS_MeshNode* node = nnIt->first;
4193 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4194 if ( listNewNodes.empty() )
4197 itNN [ iNode ] = listNewNodes.begin();
4198 prevNod[ iNode ] = node;
4199 nextNod[ iNode ] = listNewNodes.front();
4201 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4202 corner node of linear */
4203 if ( prevNod[ iNode ] != nextNod [ iNode ])
4204 nbDouble += !isSingleNode[iNode];
4206 if( iNode < nbCorners ) { // check corners only
4207 if ( prevNod[ iNode ] == nextNod [ iNode ])
4208 sames[nbSame++] = iNode;
4210 iNotSameNode = iNode;
4214 if ( nbSame == nbNodes || nbSame > 2) {
4215 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4219 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4221 // fix nodes order to have bottom normal external
4222 if ( baseType == SMDSEntity_Polygon )
4224 std::reverse( itNN.begin(), itNN.end() );
4225 std::reverse( prevNod.begin(), prevNod.end() );
4226 std::reverse( midlNod.begin(), midlNod.end() );
4227 std::reverse( nextNod.begin(), nextNod.end() );
4228 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4232 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4233 SMDS_MeshCell::applyInterlace( ind, itNN );
4234 SMDS_MeshCell::applyInterlace( ind, prevNod );
4235 SMDS_MeshCell::applyInterlace( ind, nextNod );
4236 SMDS_MeshCell::applyInterlace( ind, midlNod );
4237 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4240 sames[nbSame] = iNotSameNode;
4241 for ( int j = 0; j <= nbSame; ++j )
4242 for ( size_t i = 0; i < ind.size(); ++i )
4243 if ( ind[i] == sames[j] )
4248 iNotSameNode = sames[nbSame];
4252 else if ( elem->GetType() == SMDSAbs_Edge )
4254 // orient a new face same as adjacent one
4256 const SMDS_MeshElement* e;
4257 TIDSortedElemSet dummy;
4258 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4259 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4260 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4262 // there is an adjacent face, check order of nodes in it
4263 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4266 std::swap( itNN[0], itNN[1] );
4267 std::swap( prevNod[0], prevNod[1] );
4268 std::swap( nextNod[0], nextNod[1] );
4269 std::swap( isSingleNode[0], isSingleNode[1] );
4271 sames[0] = 1 - sames[0];
4272 iNotSameNode = 1 - iNotSameNode;
4277 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4279 iSameNode = sames[ nbSame-1 ];
4280 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4281 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4282 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4285 if ( baseType == SMDSEntity_Polygon )
4287 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4288 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4290 else if ( baseType == SMDSEntity_Quad_Polygon )
4292 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4293 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4296 // make new elements
4297 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4300 for ( iNode = 0; iNode < nbNodes; iNode++ )
4302 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4303 nextNod[ iNode ] = *itNN[ iNode ]++;
4306 SMDS_MeshElement* aNewElem = 0;
4307 /*if(!elem->IsPoly())*/ {
4308 switch ( baseType ) {
4310 case SMDSEntity_Node: { // sweep NODE
4311 if ( nbSame == 0 ) {
4312 if ( isSingleNode[0] )
4313 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4315 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4321 case SMDSEntity_Edge: { // sweep EDGE
4322 if ( nbDouble == 0 )
4324 if ( nbSame == 0 ) // ---> quadrangle
4325 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4326 nextNod[ 1 ], nextNod[ 0 ] );
4327 else // ---> triangle
4328 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4329 nextNod[ iNotSameNode ] );
4331 else // ---> polygon
4333 vector<const SMDS_MeshNode*> poly_nodes;
4334 poly_nodes.push_back( prevNod[0] );
4335 poly_nodes.push_back( prevNod[1] );
4336 if ( prevNod[1] != nextNod[1] )
4338 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4339 poly_nodes.push_back( nextNod[1] );
4341 if ( prevNod[0] != nextNod[0] )
4343 poly_nodes.push_back( nextNod[0] );
4344 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4346 switch ( poly_nodes.size() ) {
4348 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4351 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4352 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4355 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4360 case SMDSEntity_Triangle: // TRIANGLE --->
4362 if ( nbDouble > 0 ) break;
4363 if ( nbSame == 0 ) // ---> pentahedron
4364 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4365 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4367 else if ( nbSame == 1 ) // ---> pyramid
4368 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4369 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4370 nextNod[ iSameNode ]);
4372 else // 2 same nodes: ---> tetrahedron
4373 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4374 nextNod[ iNotSameNode ]);
4377 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4381 if ( nbDouble+nbSame == 2 )
4383 if(nbSame==0) { // ---> quadratic quadrangle
4384 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4385 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4387 else { //(nbSame==1) // ---> quadratic triangle
4389 return; // medium node on axis
4391 else if(sames[0]==0)
4392 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4393 prevNod[2], midlNod[1], nextNod[2] );
4395 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4396 prevNod[2], nextNod[2], midlNod[0]);
4399 else if ( nbDouble == 3 )
4401 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4402 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4403 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4410 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4411 if ( nbDouble > 0 ) break;
4413 if ( nbSame == 0 ) // ---> hexahedron
4414 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4415 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4417 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4418 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4419 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4420 nextNod[ iSameNode ]);
4421 newElems.push_back( aNewElem );
4422 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4423 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4424 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4426 else if ( nbSame == 2 ) { // ---> pentahedron
4427 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4428 // iBeforeSame is same too
4429 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4430 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4431 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4433 // iAfterSame is same too
4434 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4435 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4436 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4440 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4441 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4442 if ( nbDouble+nbSame != 3 ) break;
4444 // ---> pentahedron with 15 nodes
4445 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4446 nextNod[0], nextNod[1], nextNod[2],
4447 prevNod[3], prevNod[4], prevNod[5],
4448 nextNod[3], nextNod[4], nextNod[5],
4449 midlNod[0], midlNod[1], midlNod[2]);
4451 else if(nbSame==1) {
4452 // ---> 2d order pyramid of 13 nodes
4453 int apex = iSameNode;
4454 int i0 = ( apex + 1 ) % nbCorners;
4455 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4459 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4460 nextNod[i0], nextNod[i1], prevNod[apex],
4461 prevNod[i01], midlNod[i0],
4462 nextNod[i01], midlNod[i1],
4463 prevNod[i1a], prevNod[i0a],
4464 nextNod[i0a], nextNod[i1a]);
4466 else if(nbSame==2) {
4467 // ---> 2d order tetrahedron of 10 nodes
4468 int n1 = iNotSameNode;
4469 int n2 = ( n1 + 1 ) % nbCorners;
4470 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4474 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4475 prevNod[n12], prevNod[n23], prevNod[n31],
4476 midlNod[n1], nextNod[n12], nextNod[n31]);
4480 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4482 if ( nbDouble != 4 ) break;
4483 // ---> hexahedron with 20 nodes
4484 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4485 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4486 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4487 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4488 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4490 else if(nbSame==1) {
4491 // ---> pyramid + pentahedron - can not be created since it is needed
4492 // additional middle node at the center of face
4493 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4496 else if( nbSame == 2 ) {
4497 if ( nbDouble != 2 ) break;
4498 // ---> 2d order Pentahedron with 15 nodes
4500 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4501 // iBeforeSame is same too
4508 // iAfterSame is same too
4518 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4519 prevNod[n4], prevNod[n5], nextNod[n5],
4520 prevNod[n12], midlNod[n2], nextNod[n12],
4521 prevNod[n45], midlNod[n5], nextNod[n45],
4522 prevNod[n14], prevNod[n25], nextNod[n25]);
4526 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4528 if( nbSame == 0 && nbDouble == 9 ) {
4529 // ---> tri-quadratic hexahedron with 27 nodes
4530 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4531 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4532 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4533 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4534 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4535 prevNod[8], // bottom center
4536 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4537 nextNod[8], // top center
4538 midlNod[8]);// elem center
4546 case SMDSEntity_Polygon: { // sweep POLYGON
4548 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4549 // ---> hexagonal prism
4550 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4551 prevNod[3], prevNod[4], prevNod[5],
4552 nextNod[0], nextNod[1], nextNod[2],
4553 nextNod[3], nextNod[4], nextNod[5]);
4557 case SMDSEntity_Ball:
4562 } // switch ( baseType )
4565 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4567 if ( baseType != SMDSEntity_Polygon )
4569 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4570 SMDS_MeshCell::applyInterlace( ind, prevNod );
4571 SMDS_MeshCell::applyInterlace( ind, nextNod );
4572 SMDS_MeshCell::applyInterlace( ind, midlNod );
4573 SMDS_MeshCell::applyInterlace( ind, itNN );
4574 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4575 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4577 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4578 vector<int> quantities (nbNodes + 2);
4579 polyedre_nodes.clear();
4583 for (int inode = 0; inode < nbNodes; inode++)
4584 polyedre_nodes.push_back( prevNod[inode] );
4585 quantities.push_back( nbNodes );
4588 polyedre_nodes.push_back( nextNod[0] );
4589 for (int inode = nbNodes; inode-1; --inode )
4590 polyedre_nodes.push_back( nextNod[inode-1] );
4591 quantities.push_back( nbNodes );
4599 const int iQuad = elem->IsQuadratic();
4600 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4602 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4603 int inextface = (iface+1+iQuad) % nbNodes;
4604 int imid = (iface+1) % nbNodes;
4605 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4606 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4607 polyedre_nodes.push_back( prevNod[iface] ); // 1
4608 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4610 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4611 polyedre_nodes.push_back( nextNod[iface] ); // 2
4613 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4614 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4616 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4617 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4619 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4620 if ( nbFaceNodes > 2 )
4621 quantities.push_back( nbFaceNodes );
4622 else // degenerated face
4623 polyedre_nodes.resize( prevNbNodes );
4625 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4627 } // try to create a polyherdal prism
4630 newElems.push_back( aNewElem );
4631 myLastCreatedElems.push_back(aNewElem);
4632 srcElements.push_back( elem );
4635 // set new prev nodes
4636 for ( iNode = 0; iNode < nbNodes; iNode++ )
4637 prevNod[ iNode ] = nextNod[ iNode ];
4642 //=======================================================================
4644 * \brief Create 1D and 2D elements around swept elements
4645 * \param mapNewNodes - source nodes and ones generated from them
4646 * \param newElemsMap - source elements and ones generated from them
4647 * \param elemNewNodesMap - nodes generated from each node of each element
4648 * \param elemSet - all swept elements
4649 * \param nbSteps - number of sweeping steps
4650 * \param srcElements - to append elem for each generated element
4652 //=======================================================================
4654 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4655 TTElemOfElemListMap & newElemsMap,
4656 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4657 TIDSortedElemSet& elemSet,
4659 SMESH_SequenceOfElemPtr& srcElements)
4661 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4662 SMESHDS_Mesh* aMesh = GetMeshDS();
4664 // Find nodes belonging to only one initial element - sweep them into edges.
4666 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4667 for ( ; nList != mapNewNodes.end(); nList++ )
4669 const SMDS_MeshNode* node =
4670 static_cast<const SMDS_MeshNode*>( nList->first );
4671 if ( newElemsMap.count( node ))
4672 continue; // node was extruded into edge
4673 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4674 int nbInitElems = 0;
4675 const SMDS_MeshElement* el = 0;
4676 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4677 while ( eIt->more() && nbInitElems < 2 ) {
4678 const SMDS_MeshElement* e = eIt->next();
4679 SMDSAbs_ElementType type = e->GetType();
4680 if ( type == SMDSAbs_Volume ||
4684 if ( type > highType ) {
4691 if ( nbInitElems == 1 ) {
4692 bool NotCreateEdge = el && el->IsMediumNode(node);
4693 if(!NotCreateEdge) {
4694 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4695 list<const SMDS_MeshElement*> newEdges;
4696 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4701 // Make a ceiling for each element ie an equal element of last new nodes.
4702 // Find free links of faces - make edges and sweep them into faces.
4704 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4706 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4707 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4708 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4710 const SMDS_MeshElement* elem = itElem->first;
4711 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4713 if(itElem->second.size()==0) continue;
4715 const bool isQuadratic = elem->IsQuadratic();
4717 if ( elem->GetType() == SMDSAbs_Edge ) {
4718 // create a ceiling edge
4719 if ( !isQuadratic ) {
4720 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4721 vecNewNodes[ 1 ]->second.back())) {
4722 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4723 vecNewNodes[ 1 ]->second.back()));
4724 srcElements.push_back( elem );
4728 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4729 vecNewNodes[ 1 ]->second.back(),
4730 vecNewNodes[ 2 ]->second.back())) {
4731 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4732 vecNewNodes[ 1 ]->second.back(),
4733 vecNewNodes[ 2 ]->second.back()));
4734 srcElements.push_back( elem );
4738 if ( elem->GetType() != SMDSAbs_Face )
4741 bool hasFreeLinks = false;
4743 TIDSortedElemSet avoidSet;
4744 avoidSet.insert( elem );
4746 set<const SMDS_MeshNode*> aFaceLastNodes;
4747 int iNode, nbNodes = vecNewNodes.size();
4748 if ( !isQuadratic ) {
4749 // loop on the face nodes
4750 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4751 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4752 // look for free links of the face
4753 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4754 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4755 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4756 // check if a link n1-n2 is free
4757 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4758 hasFreeLinks = true;
4759 // make a new edge and a ceiling for a new edge
4760 const SMDS_MeshElement* edge;
4761 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4762 myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4763 srcElements.push_back( myLastCreatedElems.back() );
4765 n1 = vecNewNodes[ iNode ]->second.back();
4766 n2 = vecNewNodes[ iNext ]->second.back();
4767 if ( !aMesh->FindEdge( n1, n2 )) {
4768 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4769 srcElements.push_back( edge );
4774 else { // elem is quadratic face
4775 int nbn = nbNodes/2;
4776 for ( iNode = 0; iNode < nbn; iNode++ ) {
4777 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4778 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4779 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4780 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4781 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4782 // check if a link is free
4783 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4784 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4785 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4786 hasFreeLinks = true;
4787 // make an edge and a ceiling for a new edge
4789 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4790 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4791 srcElements.push_back( elem );
4793 n1 = vecNewNodes[ iNode ]->second.back();
4794 n2 = vecNewNodes[ iNext ]->second.back();
4795 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4796 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4797 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4798 srcElements.push_back( elem );
4802 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4803 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4807 // sweep free links into faces
4809 if ( hasFreeLinks ) {
4810 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4811 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4813 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4814 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4815 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4816 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4817 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4819 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4820 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4821 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4823 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4824 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4825 std::advance( v, volNb );
4826 // find indices of free faces of a volume and their source edges
4827 list< int > freeInd;
4828 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4829 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4830 int iF, nbF = vTool.NbFaces();
4831 for ( iF = 0; iF < nbF; iF ++ ) {
4832 if ( vTool.IsFreeFace( iF ) &&
4833 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4834 initNodeSet != faceNodeSet) // except an initial face
4836 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4838 if ( faceNodeSet == initNodeSetNoCenter )
4840 freeInd.push_back( iF );
4841 // find source edge of a free face iF
4842 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4843 vector<const SMDS_MeshNode*>::iterator lastCommom;
4844 commonNodes.resize( nbNodes, 0 );
4845 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4846 initNodeSet.begin(), initNodeSet.end(),
4847 commonNodes.begin());
4848 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4849 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4851 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4853 if ( !srcEdges.back() )
4855 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4856 << iF << " of volume #" << vTool.ID() << endl;
4861 if ( freeInd.empty() )
4864 // create wall faces for all steps;
4865 // if such a face has been already created by sweep of edge,
4866 // assure that its orientation is OK
4867 for ( int iStep = 0; iStep < nbSteps; iStep++ )
4869 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4870 vTool.SetExternalNormal();
4871 const int nextShift = vTool.IsForward() ? +1 : -1;
4872 list< int >::iterator ind = freeInd.begin();
4873 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4874 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4876 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4877 int nbn = vTool.NbFaceNodes( *ind );
4878 const SMDS_MeshElement * f = 0;
4879 if ( nbn == 3 ) ///// triangle
4881 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4883 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4885 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4887 nodes[ 1 + nextShift ] };
4889 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4891 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4895 else if ( nbn == 4 ) ///// quadrangle
4897 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4899 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4901 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4902 nodes[ 2 ], nodes[ 2+nextShift ] };
4904 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4906 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4907 newOrder[ 2 ], newOrder[ 3 ]));
4910 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4912 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4914 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4916 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4918 nodes[2 + 2*nextShift],
4919 nodes[3 - 2*nextShift],
4921 nodes[3 + 2*nextShift]};
4923 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4925 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4933 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4935 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4936 nodes[1], nodes[3], nodes[5], nodes[7] );
4938 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4940 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4941 nodes[4 - 2*nextShift],
4943 nodes[4 + 2*nextShift],
4945 nodes[5 - 2*nextShift],
4947 nodes[5 + 2*nextShift] };
4949 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4951 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4952 newOrder[ 2 ], newOrder[ 3 ],
4953 newOrder[ 4 ], newOrder[ 5 ],
4954 newOrder[ 6 ], newOrder[ 7 ]));
4957 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4959 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4960 SMDSAbs_Face, /*noMedium=*/false);
4962 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4964 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4965 nodes[4 - 2*nextShift],
4967 nodes[4 + 2*nextShift],
4969 nodes[5 - 2*nextShift],
4971 nodes[5 + 2*nextShift],
4974 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4976 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4977 newOrder[ 2 ], newOrder[ 3 ],
4978 newOrder[ 4 ], newOrder[ 5 ],
4979 newOrder[ 6 ], newOrder[ 7 ],
4983 else //////// polygon
4985 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4986 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4988 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4990 if ( !vTool.IsForward() )
4991 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4993 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4995 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
4999 while ( srcElements.size() < myLastCreatedElems.size() )
5000 srcElements.push_back( *srcEdge );
5002 } // loop on free faces
5004 // go to the next volume
5006 while ( iVol++ < nbVolumesByStep ) v++;
5009 } // loop on volumes of one step
5010 } // sweep free links into faces
5012 // Make a ceiling face with a normal external to a volume
5014 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5015 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5016 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5018 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5019 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5020 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5024 lastVol.SetExternalNormal();
5025 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5026 const int nbn = lastVol.NbFaceNodes( iF );
5027 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5028 if ( !hasFreeLinks ||
5029 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5031 const vector<int>& interlace =
5032 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5033 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5035 AddElement( nodeVec, anyFace.Init( elem ));
5037 while ( srcElements.size() < myLastCreatedElems.size() )
5038 srcElements.push_back( elem );
5041 } // loop on swept elements
5044 //=======================================================================
5045 //function : RotationSweep
5047 //=======================================================================
5049 SMESH_MeshEditor::PGroupIDs
5050 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5051 const gp_Ax1& theAxis,
5052 const double theAngle,
5053 const int theNbSteps,
5054 const double theTol,
5055 const bool theMakeGroups,
5056 const bool theMakeWalls)
5060 setElemsFirst( theElemSets );
5061 myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5062 myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5064 // source elements for each generated one
5065 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5066 srcElems.reserve( theElemSets[0].size() );
5067 srcNodes.reserve( theElemSets[1].size() );
5070 aTrsf.SetRotation( theAxis, theAngle );
5072 aTrsf2.SetRotation( theAxis, theAngle/2. );
5074 gp_Lin aLine( theAxis );
5075 double aSqTol = theTol * theTol;
5077 SMESHDS_Mesh* aMesh = GetMeshDS();
5079 TNodeOfNodeListMap mapNewNodes;
5080 TElemOfVecOfNnlmiMap mapElemNewNodes;
5081 TTElemOfElemListMap newElemsMap;
5083 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5084 myMesh->NbFaces(ORDER_QUADRATIC) +
5085 myMesh->NbVolumes(ORDER_QUADRATIC) );
5086 // loop on theElemSets
5087 TIDSortedElemSet::iterator itElem;
5088 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5090 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5091 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5092 const SMDS_MeshElement* elem = *itElem;
5093 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5095 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5096 newNodesItVec.reserve( elem->NbNodes() );
5098 // loop on elem nodes
5099 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5100 while ( itN->more() )
5102 const SMDS_MeshNode* node = cast2Node( itN->next() );
5104 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5106 aXYZ.Coord( coord[0], coord[1], coord[2] );
5107 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5109 // check if a node has been already sweeped
5110 TNodeOfNodeListMapItr nIt =
5111 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5112 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5113 if ( listNewNodes.empty() )
5115 // check if we are to create medium nodes between corner ones
5116 bool needMediumNodes = false;
5117 if ( isQuadraticMesh )
5119 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5120 while (it->more() && !needMediumNodes )
5122 const SMDS_MeshElement* invElem = it->next();
5123 if ( invElem != elem && !theElems.count( invElem )) continue;
5124 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5125 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5126 needMediumNodes = true;
5131 const SMDS_MeshNode * newNode = node;
5132 for ( int i = 0; i < theNbSteps; i++ ) {
5134 if ( needMediumNodes ) // create a medium node
5136 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5137 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5138 myLastCreatedNodes.push_back(newNode);
5139 srcNodes.push_back( node );
5140 listNewNodes.push_back( newNode );
5141 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5144 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5146 // create a corner node
5147 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5148 myLastCreatedNodes.push_back(newNode);
5149 srcNodes.push_back( node );
5150 listNewNodes.push_back( newNode );
5153 listNewNodes.push_back( newNode );
5154 // if ( needMediumNodes )
5155 // listNewNodes.push_back( newNode );
5159 newNodesItVec.push_back( nIt );
5161 // make new elements
5162 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5167 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5169 PGroupIDs newGroupIDs;
5170 if ( theMakeGroups )
5171 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5176 //=======================================================================
5177 //function : ExtrusParam
5178 //purpose : standard construction
5179 //=======================================================================
5181 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5182 const int theNbSteps,
5183 const std::list<double>& theScales,
5184 const std::list<double>& theAngles,
5185 const gp_XYZ* theBasePoint,
5187 const double theTolerance):
5189 myBaseP( Precision::Infinite(), 0, 0 ),
5190 myFlags( theFlags ),
5191 myTolerance( theTolerance ),
5192 myElemsToUse( NULL )
5194 mySteps = new TColStd_HSequenceOfReal;
5195 const double stepSize = theStep.Magnitude();
5196 for (int i=1; i<=theNbSteps; i++ )
5197 mySteps->Append( stepSize );
5199 if ( !theScales.empty() )
5201 if ( IsScaleVariation() && (int)theScales.size() < theNbSteps )
5202 linearScaleVariation( theNbSteps, const_cast< std::list<double>& >( theScales ));
5204 // add medium scales
5205 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5206 myScales.reserve( theNbSteps * 2 );
5207 myScales.push_back( 0.5 * ( *s1 + 1. ));
5208 myScales.push_back( *s1 );
5209 for ( ; s2 != theScales.end(); s1 = s2++ )
5211 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5212 myScales.push_back( *s2 );
5216 if ( !theAngles.empty() )
5218 std::list<double>& angles = const_cast< std::list<double>& >( theAngles );
5219 if ( IsAngleVariation() && (int)theAngles.size() < theNbSteps )
5220 linearAngleVariation( theNbSteps, angles );
5222 // accumulate angles
5225 std::list<double>::iterator a1 = angles.begin(), a2;
5226 for ( ; a1 != angles.end(); ++a1, ++nbAngles )
5231 while ( nbAngles++ < theNbSteps )
5232 angles.push_back( angles.back() );
5234 // add medium angles
5235 a2 = angles.begin(), a1 = a2++;
5236 myAngles.push_back( 0.5 * *a1 );
5237 myAngles.push_back( *a1 );
5238 for ( ; a2 != angles.end(); a1 = a2++ )
5240 myAngles.push_back( 0.5 * ( *a1 + *a2 ));
5241 myAngles.push_back( *a2 );
5247 myBaseP = *theBasePoint;
5250 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5251 ( theTolerance > 0 ))
5253 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5257 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5261 //=======================================================================
5262 //function : ExtrusParam
5263 //purpose : steps are given explicitly
5264 //=======================================================================
5266 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5267 Handle(TColStd_HSequenceOfReal) theSteps,
5269 const double theTolerance):
5271 mySteps( theSteps ),
5272 myFlags( theFlags ),
5273 myTolerance( theTolerance ),
5274 myElemsToUse( NULL )
5276 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5277 ( theTolerance > 0 ))
5279 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5283 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5287 //=======================================================================
5288 //function : ExtrusParam
5289 //purpose : for extrusion by normal
5290 //=======================================================================
5292 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5293 const int theNbSteps,
5297 mySteps( new TColStd_HSequenceOfReal ),
5298 myFlags( theFlags ),
5300 myElemsToUse( NULL )
5302 for (int i = 0; i < theNbSteps; i++ )
5303 mySteps->Append( theStepSize );
5307 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5311 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5315 //=======================================================================
5316 //function : ExtrusParam
5317 //purpose : for extrusion along path
5318 //=======================================================================
5320 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const std::vector< PathPoint >& thePoints,
5321 const gp_Pnt* theBasePoint,
5322 const std::list<double>& theScales,
5323 const bool theMakeGroups )
5324 : myBaseP( Precision::Infinite(), 0, 0 ),
5325 myFlags( EXTRUSION_FLAG_BOUNDARY | ( theMakeGroups ? EXTRUSION_FLAG_GROUPS : 0 )),
5326 myPathPoints( thePoints )
5330 myBaseP = theBasePoint->XYZ();
5333 if ( !theScales.empty() )
5335 // add medium scales
5336 std::list<double>::const_iterator s2 = theScales.begin(), s1 = s2++;
5337 myScales.reserve( thePoints.size() * 2 );
5338 myScales.push_back( 0.5 * ( 1. + *s1 ));
5339 myScales.push_back( *s1 );
5340 for ( ; s2 != theScales.end(); s1 = s2++ )
5342 myScales.push_back( 0.5 * ( *s1 + *s2 ));
5343 myScales.push_back( *s2 );
5347 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesAlongTrack;
5350 //=======================================================================
5351 //function : ExtrusParam::SetElementsToUse
5352 //purpose : stores elements to use for extrusion by normal, depending on
5353 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5354 // define myBaseP for scaling
5355 //=======================================================================
5357 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5358 const TIDSortedElemSet& nodes )
5360 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5362 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5364 myBaseP.SetCoord( 0.,0.,0. );
5365 TIDSortedElemSet newNodes;
5367 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5368 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5370 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5371 TIDSortedElemSet::const_iterator itElem = elements.begin();
5372 for ( ; itElem != elements.end(); itElem++ )
5374 const SMDS_MeshElement* elem = *itElem;
5375 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5376 while ( itN->more() ) {
5377 const SMDS_MeshElement* node = itN->next();
5378 if ( newNodes.insert( node ).second )
5379 myBaseP += SMESH_NodeXYZ( node );
5383 myBaseP /= newNodes.size();
5387 //=======================================================================
5388 //function : ExtrusParam::beginStepIter
5389 //purpose : prepare iteration on steps
5390 //=======================================================================
5392 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5394 myWithMediumNodes = withMediumNodes;
5398 //=======================================================================
5399 //function : ExtrusParam::moreSteps
5400 //purpose : are there more steps?
5401 //=======================================================================
5403 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5405 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5407 //=======================================================================
5408 //function : ExtrusParam::nextStep
5409 //purpose : returns the next step
5410 //=======================================================================
5412 double SMESH_MeshEditor::ExtrusParam::nextStep()
5415 if ( !myCurSteps.empty() )
5417 res = myCurSteps.back();
5418 myCurSteps.pop_back();
5420 else if ( myNextStep <= mySteps->Length() )
5422 myCurSteps.push_back( mySteps->Value( myNextStep ));
5424 if ( myWithMediumNodes )
5426 myCurSteps.back() /= 2.;
5427 myCurSteps.push_back( myCurSteps.back() );
5434 //=======================================================================
5435 //function : ExtrusParam::makeNodesByDir
5436 //purpose : create nodes for standard extrusion
5437 //=======================================================================
5439 int SMESH_MeshEditor::ExtrusParam::
5440 makeNodesByDir( SMESHDS_Mesh* mesh,
5441 const SMDS_MeshNode* srcNode,
5442 std::list<const SMDS_MeshNode*> & newNodes,
5443 const bool makeMediumNodes)
5445 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5448 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5450 p += myDir.XYZ() * nextStep();
5451 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5452 newNodes.push_back( newNode );
5455 if ( !myScales.empty() || !myAngles.empty() )
5457 gp_XYZ center = myBaseP;
5458 gp_Ax1 ratationAxis( center, myDir );
5461 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5462 size_t i = !makeMediumNodes;
5463 for ( beginStepIter( makeMediumNodes );
5465 ++nIt, i += 1 + !makeMediumNodes )
5467 center += myDir.XYZ() * nextStep();
5469 gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5471 if ( i < myScales.size() )
5473 xyz = ( myScales[i] * ( xyz - center )) + center;
5476 if ( !myAngles.empty() )
5478 rotation.SetRotation( ratationAxis, myAngles[i] );
5479 rotation.Transforms( xyz );
5483 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5491 //=======================================================================
5492 //function : ExtrusParam::makeNodesByDirAndSew
5493 //purpose : create nodes for standard extrusion with sewing
5494 //=======================================================================
5496 int SMESH_MeshEditor::ExtrusParam::
5497 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5498 const SMDS_MeshNode* srcNode,
5499 std::list<const SMDS_MeshNode*> & newNodes,
5500 const bool makeMediumNodes)
5502 gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5505 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5507 P1 += myDir.XYZ() * nextStep();
5509 // try to search in sequence of existing nodes
5510 // if myNodes.size()>0 we 'nave to use given sequence
5511 // else - use all nodes of mesh
5512 const SMDS_MeshNode * node = 0;
5513 if ( myNodes.Length() > 0 )
5515 for ( int i = 1; i <= myNodes.Length(); i++ )
5517 SMESH_NodeXYZ P2 = myNodes.Value(i);
5518 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5520 node = myNodes.Value(i);
5527 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5530 SMESH_NodeXYZ P2 = itn->next();
5531 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5540 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5542 newNodes.push_back( node );
5549 //=======================================================================
5550 //function : ExtrusParam::makeNodesByNormal2D
5551 //purpose : create nodes for extrusion using normals of faces
5552 //=======================================================================
5554 int SMESH_MeshEditor::ExtrusParam::
5555 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5556 const SMDS_MeshNode* srcNode,
5557 std::list<const SMDS_MeshNode*> & newNodes,
5558 const bool makeMediumNodes)
5560 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5562 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5564 // get normals to faces sharing srcNode
5565 vector< gp_XYZ > norms, baryCenters;
5566 gp_XYZ norm, avgNorm( 0,0,0 );
5567 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5568 while ( faceIt->more() )
5570 const SMDS_MeshElement* face = faceIt->next();
5571 if ( myElemsToUse && !myElemsToUse->count( face ))
5573 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5575 norms.push_back( norm );
5577 if ( !alongAvgNorm )
5581 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5582 bc += SMESH_NodeXYZ( nIt->next() );
5583 baryCenters.push_back( bc / nbN );
5588 if ( norms.empty() ) return 0;
5590 double normSize = avgNorm.Modulus();
5591 if ( normSize < std::numeric_limits<double>::min() )
5594 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5597 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5600 avgNorm /= normSize;
5603 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5606 double stepSize = nextStep();
5608 if ( norms.size() > 1 )
5610 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5612 // translate plane of a face
5613 baryCenters[ iF ] += norms[ iF ] * stepSize;
5615 // find point of intersection of the face plane located at baryCenters[ iF ]
5616 // and avgNorm located at pNew
5617 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5618 double dot = ( norms[ iF ] * avgNorm );
5619 if ( dot < std::numeric_limits<double>::min() )
5620 dot = stepSize * 1e-3;
5621 double step = -( norms[ iF ] * pNew + d ) / dot;
5622 pNew += step * avgNorm;
5627 pNew += stepSize * avgNorm;
5631 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5632 newNodes.push_back( newNode );
5637 //=======================================================================
5638 //function : ExtrusParam::makeNodesByNormal1D
5639 //purpose : create nodes for extrusion using normals of edges
5640 //=======================================================================
5642 int SMESH_MeshEditor::ExtrusParam::
5643 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5644 const SMDS_MeshNode* srcNode,
5645 std::list<const SMDS_MeshNode*> & newNodes,
5646 const bool makeMediumNodes)
5648 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5652 //=======================================================================
5653 //function : ExtrusParam::makeNodesAlongTrack
5654 //purpose : create nodes for extrusion along path
5655 //=======================================================================
5657 int SMESH_MeshEditor::ExtrusParam::
5658 makeNodesAlongTrack( SMESHDS_Mesh* mesh,
5659 const SMDS_MeshNode* srcNode,
5660 std::list<const SMDS_MeshNode*> & newNodes,
5661 const bool makeMediumNodes)
5663 const Standard_Real aTolAng=1.e-4;
5665 gp_Pnt aV0x = myBaseP;
5666 gp_Pnt aPN0 = SMESH_NodeXYZ( srcNode );
5668 const PathPoint& aPP0 = myPathPoints[0];
5669 gp_Pnt aP0x = aPP0.myPnt;
5670 gp_Dir aDT0x= aPP0.myTgt;
5672 std::vector< gp_Pnt > centers;
5673 centers.reserve( NbSteps() * 2 );
5675 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5677 for ( size_t j = 1; j < myPathPoints.size(); ++j )
5679 const PathPoint& aPP = myPathPoints[j];
5680 const gp_Pnt& aP1x = aPP.myPnt;
5681 const gp_Dir& aDT1x = aPP.myTgt;
5684 gp_Vec aV01x( aP0x, aP1x );
5685 aTrsf.SetTranslation( aV01x );
5686 gp_Pnt aV1x = aV0x.Transformed( aTrsf );
5687 gp_Pnt aPN1 = aPN0.Transformed( aTrsf );
5689 // rotation 1 [ T1,T0 ]
5690 Standard_Real aAngleT1T0 = -aDT1x.Angle( aDT0x );
5691 if ( fabs( aAngleT1T0 ) > aTolAng )
5693 gp_Dir aDT1T0 = aDT1x ^ aDT0x;
5694 aTrsfRotT1T0.SetRotation( gp_Ax1( aV1x, aDT1T0 ), aAngleT1T0 );
5696 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5700 if ( aPP.myAngle != 0. )
5702 aTrsfRot.SetRotation( gp_Ax1( aV1x, aDT1x ), aPP.myAngle );
5703 aPN1 = aPN1.Transformed( aTrsfRot );
5707 if ( makeMediumNodes )
5709 // create additional node
5710 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
5711 const SMDS_MeshNode* newNode = mesh->AddNode( midP.X(), midP.Y(), midP.Z() );
5712 newNodes.push_back( newNode );
5715 const SMDS_MeshNode* newNode = mesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5716 newNodes.push_back( newNode );
5718 centers.push_back( 0.5 * ( aV0x.XYZ() + aV1x.XYZ() ));
5719 centers.push_back( aV1x );
5728 if ( !myScales.empty() )
5731 std::list<const SMDS_MeshNode*>::iterator node = newNodes.begin();
5732 for ( size_t i = !makeMediumNodes;
5733 i < myScales.size() && node != newNodes.end();
5734 i += ( 1 + !makeMediumNodes ), ++node )
5736 aTrsfScale.SetScale( centers[ i ], myScales[ i ] );
5737 gp_Pnt aN = SMESH_NodeXYZ( *node );
5738 gp_Pnt aP = aN.Transformed( aTrsfScale );
5739 mesh->MoveNode( *node, aP.X(), aP.Y(), aP.Z() );
5743 return myPathPoints.size() + makeMediumNodes * ( myPathPoints.size() - 2 );
5746 //=======================================================================
5747 //function : ExtrusionSweep
5749 //=======================================================================
5751 SMESH_MeshEditor::PGroupIDs
5752 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5753 const gp_Vec& theStep,
5754 const int theNbSteps,
5755 TTElemOfElemListMap& newElemsMap,
5757 const double theTolerance)
5759 std::list<double> dummy;
5760 ExtrusParam aParams( theStep, theNbSteps, dummy, dummy, 0,
5761 theFlags, theTolerance );
5762 return ExtrusionSweep( theElems, aParams, newElemsMap );
5768 //=======================================================================
5769 //function : getOriFactor
5770 //purpose : Return -1 or 1 depending on if order of given nodes corresponds to
5771 // edge curve orientation
5772 //=======================================================================
5774 double getOriFactor( const TopoDS_Edge& edge,
5775 const SMDS_MeshNode* n1,
5776 const SMDS_MeshNode* n2,
5777 SMESH_MesherHelper& helper)
5779 double u1 = helper.GetNodeU( edge, n1, n2 );
5780 double u2 = helper.GetNodeU( edge, n2, n1 );
5781 return u1 < u2 ? 1. : -1.;
5785 //=======================================================================
5786 //function : ExtrusionSweep
5788 //=======================================================================
5790 SMESH_MeshEditor::PGroupIDs
5791 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5792 ExtrusParam& theParams,
5793 TTElemOfElemListMap& newElemsMap)
5797 setElemsFirst( theElemSets );
5798 myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5799 myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5801 // source elements for each generated one
5802 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5803 srcElems.reserve( theElemSets[0].size() );
5804 srcNodes.reserve( theElemSets[1].size() );
5806 const int nbSteps = theParams.NbSteps();
5807 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5809 TNodeOfNodeListMap mapNewNodes;
5810 TElemOfVecOfNnlmiMap mapElemNewNodes;
5812 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5813 myMesh->NbFaces(ORDER_QUADRATIC) +
5814 myMesh->NbVolumes(ORDER_QUADRATIC) );
5816 TIDSortedElemSet::iterator itElem;
5817 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5819 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5820 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5822 // check element type
5823 const SMDS_MeshElement* elem = *itElem;
5824 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5827 const size_t nbNodes = elem->NbNodes();
5828 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5829 newNodesItVec.reserve( nbNodes );
5831 // loop on elem nodes
5832 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
5833 while ( itN->more() )
5835 // check if a node has been already sweeped
5836 const SMDS_MeshNode* node = itN->next();
5837 TNodeOfNodeListMap::iterator nIt =
5838 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5839 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5840 if ( listNewNodes.empty() )
5844 // check if we are to create medium nodes between corner ones
5845 bool needMediumNodes = false;
5846 if ( isQuadraticMesh )
5848 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5849 while (it->more() && !needMediumNodes )
5851 const SMDS_MeshElement* invElem = it->next();
5852 if ( invElem != elem && !theElems.count( invElem )) continue;
5853 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5854 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5855 needMediumNodes = true;
5858 // create nodes for all steps
5859 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5861 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5862 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5864 myLastCreatedNodes.push_back( *newNodesIt );
5865 srcNodes.push_back( node );
5870 if ( theParams.ToMakeBoundary() )
5872 GetMeshDS()->Modified();
5873 throw SALOME_Exception( SMESH_Comment("Can't extrude node #") << node->GetID() );
5875 break; // newNodesItVec will be shorter than nbNodes
5878 newNodesItVec.push_back( nIt );
5880 // make new elements
5881 if ( newNodesItVec.size() == nbNodes )
5882 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5886 if ( theParams.ToMakeBoundary() ) {
5887 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5889 PGroupIDs newGroupIDs;
5890 if ( theParams.ToMakeGroups() )
5891 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5896 //=======================================================================
5897 //function : ExtrusionAlongTrack
5899 //=======================================================================
5900 SMESH_MeshEditor::Extrusion_Error
5901 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5902 SMESH_Mesh* theTrackMesh,
5903 SMDS_ElemIteratorPtr theTrackIterator,
5904 const SMDS_MeshNode* theN1,
5905 std::list<double>& theAngles,
5906 const bool theAngleVariation,
5907 std::list<double>& theScales,
5908 const bool theScaleVariation,
5909 const gp_Pnt* theRefPoint,
5910 const bool theMakeGroups)
5915 if ( theElements[0].empty() && theElements[1].empty() )
5916 return EXTR_NO_ELEMENTS;
5918 ASSERT( theTrackMesh );
5919 if ( ! theTrackIterator || !theTrackIterator->more() )
5920 return EXTR_NO_ELEMENTS;
5922 // 2. Get ordered nodes
5923 SMESH_MeshAlgos::TElemGroupVector branchEdges;
5924 SMESH_MeshAlgos::TNodeGroupVector branchNods;
5925 SMESH_MeshAlgos::Get1DBranches( theTrackIterator, branchEdges, branchNods, theN1 );
5926 if ( branchEdges.empty() )
5927 return EXTR_PATH_NOT_EDGE;
5929 if ( branchEdges.size() > 1 )
5930 return EXTR_BAD_PATH_SHAPE;
5932 std::vector< const SMDS_MeshNode* >& pathNodes = branchNods[0];
5933 std::vector< const SMDS_MeshElement* >& pathEdges = branchEdges[0];
5934 if ( pathNodes[0] != theN1 && pathNodes[1] != theN1 )
5935 return EXTR_BAD_STARTING_NODE;
5937 if ( theTrackMesh->NbEdges( ORDER_QUADRATIC ) > 0 )
5939 // add medium nodes to pathNodes
5940 std::vector< const SMDS_MeshNode* > pathNodes2;
5941 std::vector< const SMDS_MeshElement* > pathEdges2;
5942 pathNodes2.reserve( pathNodes.size() * 2 );
5943 pathEdges2.reserve( pathEdges.size() * 2 );
5944 for ( size_t i = 0; i < pathEdges.size(); ++i )
5946 pathNodes2.push_back( pathNodes[i] );
5947 pathEdges2.push_back( pathEdges[i] );
5948 if ( pathEdges[i]->IsQuadratic() )
5950 pathNodes2.push_back( pathEdges[i]->GetNode(2) );
5951 pathEdges2.push_back( pathEdges[i] );
5954 pathNodes2.push_back( pathNodes.back() );
5955 pathEdges.swap( pathEdges2 );
5956 pathNodes.swap( pathNodes2 );
5959 // 3. Get path data at pathNodes
5961 std::vector< ExtrusParam::PathPoint > points( pathNodes.size() );
5963 if ( theAngleVariation )
5964 linearAngleVariation( points.size()-1, theAngles );
5965 if ( theScaleVariation )
5966 linearScaleVariation( points.size()-1, theScales );
5968 theAngles.push_front( 0 ); // for the 1st point that is not transformed
5969 std::list<double>::iterator angle = theAngles.begin();
5971 SMESHDS_Mesh* pathMeshDS = theTrackMesh->GetMeshDS();
5973 std::map< int, double > edgeID2OriFactor; // orientation of EDGEs
5974 std::map< int, double >::iterator id2factor;
5975 SMESH_MesherHelper pathHelper( *theTrackMesh );
5976 gp_Pnt p; gp_Vec tangent;
5977 const double tol2 = gp::Resolution() * gp::Resolution();
5979 for ( size_t i = 0; i < pathNodes.size(); ++i )
5981 ExtrusParam::PathPoint & point = points[ i ];
5983 point.myPnt = SMESH_NodeXYZ( pathNodes[ i ]);
5985 if ( angle != theAngles.end() )
5986 point.myAngle = *angle++;
5988 tangent.SetCoord( 0,0,0 );
5989 const int shapeID = pathNodes[ i ]->GetShapeID();
5990 const TopoDS_Shape& shape = pathMeshDS->IndexToShape( shapeID );
5991 TopAbs_ShapeEnum shapeType = shape.IsNull() ? TopAbs_SHAPE : shape.ShapeType();
5992 switch ( shapeType )
5996 TopoDS_Edge edge = TopoDS::Edge( shape );
5997 id2factor = edgeID2OriFactor.insert( std::make_pair( shapeID, 0 )).first;
5998 if ( id2factor->second == 0 )
6000 if ( i ) id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6001 else id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6003 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6004 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6005 curve->D1( u, p, tangent );
6006 tangent *= id2factor->second;
6012 PShapeIteratorPtr shapeIt = pathHelper.GetAncestors( shape, *theTrackMesh, TopAbs_EDGE );
6013 while ( const TopoDS_Shape* edgePtr = shapeIt->next() )
6015 int edgeID = pathMeshDS->ShapeToIndex( *edgePtr );
6016 for ( int di = -1; di <= 0; ++di )
6019 if ( j < pathEdges.size() && edgeID == pathEdges[ j ]->GetShapeID() )
6021 TopoDS_Edge edge = TopoDS::Edge( *edgePtr );
6022 id2factor = edgeID2OriFactor.insert( std::make_pair( edgeID, 0 )).first;
6023 if ( id2factor->second == 0 )
6026 id2factor->second = getOriFactor( edge, pathNodes[i-1], pathNodes[i], pathHelper );
6028 id2factor->second = getOriFactor( edge, pathNodes[i], pathNodes[i+1], pathHelper );
6030 double u = pathHelper.GetNodeU( edge, pathNodes[i] ), u0, u1;
6031 Handle(Geom_Curve) curve = BRep_Tool::Curve( edge, u0, u1 );
6033 curve->D1( u, p, du );
6034 double size2 = du.SquareMagnitude();
6035 if ( du.SquareMagnitude() > tol2 )
6037 tangent += du.Divided( Sqrt( size2 )) * id2factor->second;
6049 for ( int di = -1; di <= 1; di += 2 )
6052 if ( j < pathNodes.size() )
6054 gp_Vec dir( point.myPnt, SMESH_NodeXYZ( pathNodes[ j ]));
6055 double size2 = dir.SquareMagnitude();
6057 tangent += dir.Divided( Sqrt( size2 )) * di;
6061 } // switch ( shapeType )
6063 if ( tangent.SquareMagnitude() < tol2 )
6064 return EXTR_CANT_GET_TANGENT;
6066 point.myTgt = tangent;
6068 } // loop on pathNodes
6071 ExtrusParam nodeMaker( points, theRefPoint, theScales, theMakeGroups );
6072 TTElemOfElemListMap newElemsMap;
6074 ExtrusionSweep( theElements, nodeMaker, newElemsMap );
6079 //=======================================================================
6080 //function : linearAngleVariation
6081 //purpose : spread values over nbSteps
6082 //=======================================================================
6084 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6085 list<double>& Angles)
6087 int nbAngles = Angles.size();
6088 if( nbSteps > nbAngles && nbAngles > 0 )
6090 vector<double> theAngles(nbAngles);
6091 theAngles.assign( Angles.begin(), Angles.end() );
6094 double rAn2St = double( nbAngles ) / double( nbSteps );
6095 double angPrev = 0, angle;
6096 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6098 double angCur = rAn2St * ( iSt+1 );
6099 double angCurFloor = floor( angCur );
6100 double angPrevFloor = floor( angPrev );
6101 if ( angPrevFloor == angCurFloor )
6102 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6104 int iP = int( angPrevFloor );
6105 double angPrevCeil = ceil(angPrev);
6106 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6108 int iC = int( angCurFloor );
6109 if ( iC < nbAngles )
6110 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6112 iP = int( angPrevCeil );
6114 angle += theAngles[ iC ];
6116 res.push_back(angle);
6123 //=======================================================================
6124 //function : linearScaleVariation
6125 //purpose : spread values over nbSteps
6126 //=======================================================================
6128 void SMESH_MeshEditor::linearScaleVariation(const int theNbSteps,
6129 std::list<double>& theScales)
6131 int nbScales = theScales.size();
6132 std::vector<double> myScales;
6133 myScales.reserve( theNbSteps );
6134 std::list<double>::const_iterator scale = theScales.begin();
6135 double prevScale = 1.0;
6136 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
6138 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
6139 int stDelta = Max( 1, iStep - myScales.size());
6140 double scDelta = ( *scale - prevScale ) / stDelta;
6141 for ( int iStep = 0; iStep < stDelta; ++iStep )
6143 myScales.push_back( prevScale + scDelta );
6144 prevScale = myScales.back();
6148 theScales.assign( myScales.begin(), myScales.end() );
6151 //================================================================================
6153 * \brief Move or copy theElements applying theTrsf to their nodes
6154 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6155 * \param theTrsf - transformation to apply
6156 * \param theCopy - if true, create translated copies of theElems
6157 * \param theMakeGroups - if true and theCopy, create translated groups
6158 * \param theTargetMesh - mesh to copy translated elements into
6159 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6161 //================================================================================
6163 SMESH_MeshEditor::PGroupIDs
6164 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6165 const gp_Trsf& theTrsf,
6167 const bool theMakeGroups,
6168 SMESH_Mesh* theTargetMesh)
6171 myLastCreatedElems.reserve( theElems.size() );
6173 bool needReverse = false;
6174 string groupPostfix;
6175 switch ( theTrsf.Form() ) {
6178 groupPostfix = "mirrored";
6181 groupPostfix = "mirrored";
6185 groupPostfix = "mirrored";
6188 groupPostfix = "rotated";
6190 case gp_Translation:
6191 groupPostfix = "translated";
6194 groupPostfix = "scaled";
6196 case gp_CompoundTrsf: // different scale by axis
6197 groupPostfix = "scaled";
6200 needReverse = false;
6201 groupPostfix = "transformed";
6204 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6205 SMESHDS_Mesh* aMesh = GetMeshDS();
6207 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6208 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6209 SMESH_MeshEditor::ElemFeatures elemType;
6211 // map old node to new one
6212 TNodeNodeMap nodeMap;
6214 // elements sharing moved nodes; those of them which have all
6215 // nodes mirrored but are not in theElems are to be reversed
6216 TIDSortedElemSet inverseElemSet;
6218 // source elements for each generated one
6219 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6221 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6222 TIDSortedElemSet orphanNode;
6224 if ( theElems.empty() ) // transform the whole mesh
6227 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6228 while ( eIt->more() ) theElems.insert( eIt->next() );
6230 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6231 while ( nIt->more() )
6233 const SMDS_MeshNode* node = nIt->next();
6234 if ( node->NbInverseElements() == 0)
6235 orphanNode.insert( node );
6239 // loop on elements to transform nodes : first orphan nodes then elems
6240 TIDSortedElemSet::iterator itElem;
6241 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6242 for (int i=0; i<2; i++)
6243 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6245 const SMDS_MeshElement* elem = *itElem;
6249 // loop on elem nodes
6251 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6252 while ( itN->more() )
6254 const SMDS_MeshNode* node = cast2Node( itN->next() );
6255 // check if a node has been already transformed
6256 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6257 nodeMap.insert( make_pair ( node, node ));
6258 if ( !n2n_isnew.second )
6261 node->GetXYZ( coord );
6262 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6263 if ( theTargetMesh ) {
6264 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6265 n2n_isnew.first->second = newNode;
6266 myLastCreatedNodes.push_back(newNode);
6267 srcNodes.push_back( node );
6269 else if ( theCopy ) {
6270 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6271 n2n_isnew.first->second = newNode;
6272 myLastCreatedNodes.push_back(newNode);
6273 srcNodes.push_back( node );
6276 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6277 // node position on shape becomes invalid
6278 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6279 ( SMDS_SpacePosition::originSpacePosition() );
6282 // keep inverse elements
6283 if ( !theCopy && !theTargetMesh && needReverse ) {
6284 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6285 while ( invElemIt->more() ) {
6286 const SMDS_MeshElement* iel = invElemIt->next();
6287 inverseElemSet.insert( iel );
6291 } // loop on elems in { &orphanNode, &theElems };
6293 // either create new elements or reverse mirrored ones
6294 if ( !theCopy && !needReverse && !theTargetMesh )
6297 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6299 // Replicate or reverse elements
6301 std::vector<int> iForw;
6302 vector<const SMDS_MeshNode*> nodes;
6303 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6305 const SMDS_MeshElement* elem = *itElem;
6306 if ( !elem ) continue;
6308 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6309 size_t nbNodes = elem->NbNodes();
6310 if ( geomType == SMDSGeom_NONE ) continue; // node
6312 nodes.resize( nbNodes );
6314 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6316 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6320 bool allTransformed = true;
6321 int nbFaces = aPolyedre->NbFaces();
6322 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6324 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6325 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6327 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6328 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6329 if ( nodeMapIt == nodeMap.end() )
6330 allTransformed = false; // not all nodes transformed
6332 nodes.push_back((*nodeMapIt).second);
6334 if ( needReverse && allTransformed )
6335 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6337 if ( !allTransformed )
6338 continue; // not all nodes transformed
6340 else // ----------------------- the rest element types
6342 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6343 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6344 const vector<int>& i = needReverse ? iRev : iForw;
6346 // find transformed nodes
6348 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6349 while ( itN->more() ) {
6350 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6351 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6352 if ( nodeMapIt == nodeMap.end() )
6353 break; // not all nodes transformed
6354 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6356 if ( iNode != nbNodes )
6357 continue; // not all nodes transformed
6361 // copy in this or a new mesh
6362 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6363 srcElems.push_back( elem );
6366 // reverse element as it was reversed by transformation
6368 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6371 } // loop on elements
6373 if ( editor && editor != this )
6374 myLastCreatedElems.swap( editor->myLastCreatedElems );
6376 PGroupIDs newGroupIDs;
6378 if ( ( theMakeGroups && theCopy ) ||
6379 ( theMakeGroups && theTargetMesh ) )
6380 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6385 //================================================================================
6387 * \brief Make an offset mesh from a source 2D mesh
6388 * \param [in] theElements - source faces
6389 * \param [in] theValue - offset value
6390 * \param [out] theTgtMesh - a mesh to add offset elements to
6391 * \param [in] theMakeGroups - to generate groups
6392 * \return PGroupIDs - IDs of created groups. NULL means failure
6394 //================================================================================
6396 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6397 const double theValue,
6398 SMESH_Mesh* theTgtMesh,
6399 const bool theMakeGroups,
6400 const bool theCopyElements,
6401 const bool theFixSelfIntersection)
6403 SMESHDS_Mesh* meshDS = GetMeshDS();
6404 SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6405 SMESH_MeshEditor tgtEditor( theTgtMesh );
6407 SMDS_ElemIteratorPtr eIt;
6408 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6409 else eIt = SMESHUtils::elemSetIterator( theElements );
6411 SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6412 SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6413 std::unique_ptr< SMDS_Mesh > offsetMesh
6414 ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6415 theFixSelfIntersection,
6416 new2OldFaces, new2OldNodes ));
6417 if ( offsetMesh->NbElements() == 0 )
6418 return PGroupIDs(); // MakeOffset() failed
6421 if ( theTgtMesh == myMesh && !theCopyElements )
6423 // clear the source elements
6424 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6425 else eIt = SMESHUtils::elemSetIterator( theElements );
6426 while ( eIt->more() )
6427 meshDS->RemoveFreeElement( eIt->next(), 0 );
6430 // offsetMesh->Modified();
6431 // offsetMesh->CompactMesh(); // make IDs start from 1
6433 // source elements for each generated one
6434 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6435 srcElems.reserve( new2OldFaces.size() );
6436 srcNodes.reserve( new2OldNodes.size() );
6439 myLastCreatedElems.reserve( new2OldFaces.size() );
6440 myLastCreatedNodes.reserve( new2OldNodes.size() );
6442 // copy offsetMesh to theTgtMesh
6444 int idShift = meshDS->MaxNodeID();
6445 for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6446 if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6449 if ( n->NbInverseElements() > 0 )
6452 const SMDS_MeshNode* n2 =
6453 tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6454 myLastCreatedNodes.push_back( n2 );
6455 srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6459 ElemFeatures elemType;
6460 for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6461 if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6464 elemType.myNodes.clear();
6465 for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6467 const SMDS_MeshNode* n2 = nIt->next();
6468 elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6470 tgtEditor.AddElement( elemType.myNodes, elemType );
6471 srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6474 myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6476 PGroupIDs newGroupIDs;
6477 if ( theMakeGroups )
6478 newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6480 newGroupIDs.reset( new std::list< int > );
6485 //=======================================================================
6487 * \brief Create groups of elements made during transformation
6488 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6489 * \param elemGens - elements making corresponding myLastCreatedElems
6490 * \param postfix - to push_back to names of new groups
6491 * \param targetMesh - mesh to create groups in
6492 * \param topPresent - is there are "top" elements that are created by sweeping
6494 //=======================================================================
6496 SMESH_MeshEditor::PGroupIDs
6497 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6498 const SMESH_SequenceOfElemPtr& elemGens,
6499 const std::string& postfix,
6500 SMESH_Mesh* targetMesh,
6501 const bool topPresent)
6503 PGroupIDs newGroupIDs( new list<int> );
6504 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6506 // Sort existing groups by types and collect their names
6508 // containers to store an old group and generated new ones;
6509 // 1st new group is for result elems of different type than a source one;
6510 // 2nd new group is for same type result elems ("top" group at extrusion)
6512 using boost::make_tuple;
6513 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6514 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6515 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6517 set< string > groupNames;
6519 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6520 if ( !groupIt->more() ) return newGroupIDs;
6522 int newGroupID = mesh->GetGroupIds().back()+1;
6523 while ( groupIt->more() )
6525 SMESH_Group * group = groupIt->next();
6526 if ( !group ) continue;
6527 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6528 if ( !groupDS || groupDS->IsEmpty() ) continue;
6529 groupNames.insert ( group->GetName() );
6530 groupDS->SetStoreName( group->GetName() );
6531 const SMDSAbs_ElementType type = groupDS->GetType();
6532 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6533 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6534 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6535 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6538 // Loop on nodes and elements to add them in new groups
6540 vector< const SMDS_MeshElement* > resultElems;
6541 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6543 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6544 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6545 if ( gens.size() != elems.size() )
6546 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6548 // loop on created elements
6549 for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6551 const SMDS_MeshElement* sourceElem = gens[ iElem ];
6552 if ( !sourceElem ) {
6553 MESSAGE("generateGroups(): NULL source element");
6556 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6557 if ( groupsOldNew.empty() ) { // no groups of this type at all
6558 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6559 ++iElem; // skip all elements made by sourceElem
6562 // collect all elements made by the iElem-th sourceElem
6563 resultElems.clear();
6564 if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6565 if ( resElem != sourceElem )
6566 resultElems.push_back( resElem );
6567 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6568 if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6569 if ( resElem != sourceElem )
6570 resultElems.push_back( resElem );
6572 const SMDS_MeshElement* topElem = 0;
6573 if ( isNodes ) // there must be a top element
6575 topElem = resultElems.back();
6576 resultElems.pop_back();
6580 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6581 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6582 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6584 topElem = *resElemIt;
6585 *resElemIt = 0; // erase *resElemIt
6589 // add resultElems to groups originted from ones the sourceElem belongs to
6590 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6591 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6593 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6594 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6596 // fill in a new group
6597 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6598 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6599 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6601 newGroup.Add( *resElemIt );
6603 // fill a "top" group
6606 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6607 newTopGroup.Add( topElem );
6611 } // loop on created elements
6612 }// loop on nodes and elements
6614 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6616 list<int> topGrouIds;
6617 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6619 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6620 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6621 orderedOldNewGroups[i]->get<2>() };
6622 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6624 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6625 if ( newGroupDS->IsEmpty() )
6627 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6632 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6635 const bool isTop = ( topPresent &&
6636 newGroupDS->GetType() == oldGroupDS->GetType() &&
6639 string name = oldGroupDS->GetStoreName();
6640 { // remove trailing whitespaces (issue 22599)
6641 size_t size = name.size();
6642 while ( size > 1 && isspace( name[ size-1 ]))
6644 if ( size != name.size() )
6646 name.resize( size );
6647 oldGroupDS->SetStoreName( name.c_str() );
6650 if ( !targetMesh ) {
6651 string suffix = ( isTop ? "top": postfix.c_str() );
6655 while ( !groupNames.insert( name ).second ) // name exists
6656 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6661 newGroupDS->SetStoreName( name.c_str() );
6663 // make a SMESH_Groups
6664 mesh->AddGroup( newGroupDS );
6666 topGrouIds.push_back( newGroupDS->GetID() );
6668 newGroupIDs->push_back( newGroupDS->GetID() );
6672 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6677 //================================================================================
6679 * * \brief Return list of group of nodes close to each other within theTolerance
6680 * * Search among theNodes or in the whole mesh if theNodes is empty using
6681 * * an Octree algorithm
6682 * \param [in,out] theNodes - the nodes to treat
6683 * \param [in] theTolerance - the tolerance
6684 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
6685 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
6686 * corner and medium nodes in separate groups
6688 //================================================================================
6690 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6691 const double theTolerance,
6692 TListOfListOfNodes & theGroupsOfNodes,
6693 bool theSeparateCornersAndMedium)
6697 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
6698 myMesh->NbFaces ( ORDER_QUADRATIC ) +
6699 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
6700 theSeparateCornersAndMedium = false;
6702 TIDSortedNodeSet& corners = theNodes;
6703 TIDSortedNodeSet medium;
6705 if ( theNodes.empty() ) // get all nodes in the mesh
6707 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
6708 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
6709 if ( theSeparateCornersAndMedium )
6710 while ( nIt->more() )
6712 const SMDS_MeshNode* n = nIt->next();
6713 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
6714 nodeSet->insert( nodeSet->end(), n );
6717 while ( nIt->more() )
6718 theNodes.insert( theNodes.end(), nIt->next() );
6720 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
6722 TIDSortedNodeSet::iterator nIt = corners.begin();
6723 while ( nIt != corners.end() )
6724 if ( SMESH_MesherHelper::IsMedium( *nIt ))
6726 medium.insert( medium.end(), *nIt );
6727 corners.erase( nIt++ );
6735 if ( !corners.empty() )
6736 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
6737 if ( !medium.empty() )
6738 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
6741 //=======================================================================
6742 //function : SimplifyFace
6743 //purpose : split a chain of nodes into several closed chains
6744 //=======================================================================
6746 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6747 vector<const SMDS_MeshNode *>& poly_nodes,
6748 vector<int>& quantities) const
6750 int nbNodes = faceNodes.size();
6751 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
6755 size_t prevNbQuant = quantities.size();
6757 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
6758 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
6759 map< const SMDS_MeshNode*, int >::iterator nInd;
6761 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
6762 simpleNodes.push_back( faceNodes[0] );
6763 for ( int iCur = 1; iCur < nbNodes; iCur++ )
6765 if ( faceNodes[ iCur ] != simpleNodes.back() )
6767 int index = simpleNodes.size();
6768 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
6769 int prevIndex = nInd->second;
6770 if ( prevIndex < index )
6773 int loopLen = index - prevIndex;
6776 // store the sub-loop
6777 quantities.push_back( loopLen );
6778 for ( int i = prevIndex; i < index; i++ )
6779 poly_nodes.push_back( simpleNodes[ i ]);
6781 simpleNodes.resize( prevIndex+1 );
6785 simpleNodes.push_back( faceNodes[ iCur ]);
6790 if ( simpleNodes.size() > 2 )
6792 quantities.push_back( simpleNodes.size() );
6793 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
6796 return quantities.size() - prevNbQuant;
6799 //=======================================================================
6800 //function : MergeNodes
6801 //purpose : In each group, the cdr of nodes are substituted by the first one
6803 //=======================================================================
6805 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
6806 const bool theAvoidMakingHoles)
6810 SMESHDS_Mesh* mesh = GetMeshDS();
6812 TNodeNodeMap nodeNodeMap; // node to replace - new node
6813 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6814 list< int > rmElemIds, rmNodeIds;
6815 vector< ElemFeatures > newElemDefs;
6817 // Fill nodeNodeMap and elems
6819 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6820 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
6822 list<const SMDS_MeshNode*>& nodes = *grIt;
6823 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6824 const SMDS_MeshNode* nToKeep = *nIt;
6825 for ( ++nIt; nIt != nodes.end(); nIt++ )
6827 const SMDS_MeshNode* nToRemove = *nIt;
6828 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
6829 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6830 while ( invElemIt->more() ) {
6831 const SMDS_MeshElement* elem = invElemIt->next();
6837 // Apply recursive replacements (BUG 0020185)
6838 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
6839 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
6841 const SMDS_MeshNode* nToKeep = nnIt->second;
6842 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
6843 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
6845 nToKeep = nnIt_i->second;
6846 nnIt->second = nToKeep;
6847 nnIt_i = nodeNodeMap.find( nToKeep );
6851 if ( theAvoidMakingHoles )
6853 // find elements whose topology changes
6855 vector<const SMDS_MeshElement*> pbElems;
6856 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6857 for ( ; eIt != elems.end(); ++eIt )
6859 const SMDS_MeshElement* elem = *eIt;
6860 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6861 while ( itN->more() )
6863 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6864 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6865 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
6867 // several nodes of elem stick
6868 pbElems.push_back( elem );
6873 // exclude from merge nodes causing spoiling element
6874 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
6876 bool nodesExcluded = false;
6877 for ( size_t i = 0; i < pbElems.size(); ++i )
6879 size_t prevNbMergeNodes = nodeNodeMap.size();
6880 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
6881 prevNbMergeNodes < nodeNodeMap.size() )
6882 nodesExcluded = true;
6884 if ( !nodesExcluded )
6889 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
6891 const SMDS_MeshNode* nToRemove = nnIt->first;
6892 const SMDS_MeshNode* nToKeep = nnIt->second;
6893 if ( nToRemove != nToKeep )
6895 rmNodeIds.push_back( nToRemove->GetID() );
6896 AddToSameGroups( nToKeep, nToRemove, mesh );
6897 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
6898 // w/o creating node in place of merged ones.
6899 SMDS_PositionPtr pos = nToRemove->GetPosition();
6900 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6901 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6902 sm->SetIsAlwaysComputed( true );
6906 // Change element nodes or remove an element
6908 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6909 for ( ; eIt != elems.end(); eIt++ )
6911 const SMDS_MeshElement* elem = *eIt;
6912 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
6914 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
6916 rmElemIds.push_back( elem->GetID() );
6918 for ( size_t i = 0; i < newElemDefs.size(); ++i )
6920 if ( i > 0 || !mesh->ChangeElementNodes( elem,
6921 & newElemDefs[i].myNodes[0],
6922 newElemDefs[i].myNodes.size() ))
6926 newElemDefs[i].SetID( elem->GetID() );
6927 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6928 if ( !keepElem ) rmElemIds.pop_back();
6932 newElemDefs[i].SetID( -1 );
6934 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
6935 if ( sm && newElem )
6936 sm->AddElement( newElem );
6937 if ( elem != newElem )
6938 ReplaceElemInGroups( elem, newElem, mesh );
6943 // Remove bad elements, then equal nodes (order important)
6944 Remove( rmElemIds, /*isNodes=*/false );
6945 Remove( rmNodeIds, /*isNodes=*/true );
6950 //=======================================================================
6951 //function : applyMerge
6952 //purpose : Compute new connectivity of an element after merging nodes
6953 // \param [in] elems - the element
6954 // \param [out] newElemDefs - definition(s) of result element(s)
6955 // \param [inout] nodeNodeMap - nodes to merge
6956 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
6957 // after merging (but not degenerated), removes nodes causing
6958 // the invalidity from \a nodeNodeMap.
6959 // \return bool - true if the element should be removed
6960 //=======================================================================
6962 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
6963 vector< ElemFeatures >& newElemDefs,
6964 TNodeNodeMap& nodeNodeMap,
6965 const bool avoidMakingHoles )
6967 bool toRemove = false; // to remove elem
6968 int nbResElems = 1; // nb new elements
6970 newElemDefs.resize(nbResElems);
6971 newElemDefs[0].Init( elem );
6972 newElemDefs[0].myNodes.clear();
6974 set<const SMDS_MeshNode*> nodeSet;
6975 vector< const SMDS_MeshNode*> curNodes;
6976 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
6979 const int nbNodes = elem->NbNodes();
6980 SMDSAbs_EntityType entity = elem->GetEntityType();
6982 curNodes.resize( nbNodes );
6983 uniqueNodes.resize( nbNodes );
6984 iRepl.resize( nbNodes );
6985 int iUnique = 0, iCur = 0, nbRepl = 0;
6987 // Get new seq of nodes
6989 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6990 while ( itN->more() )
6992 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
6994 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6995 if ( nnIt != nodeNodeMap.end() ) {
6998 curNodes[ iCur ] = n;
6999 bool isUnique = nodeSet.insert( n ).second;
7001 uniqueNodes[ iUnique++ ] = n;
7003 iRepl[ nbRepl++ ] = iCur;
7007 // Analyse element topology after replacement
7009 int nbUniqueNodes = nodeSet.size();
7010 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7015 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7017 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7018 int nbCorners = nbNodes / 2;
7019 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7021 int iNext = ( iCur + 1 ) % nbCorners;
7022 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7024 int iMedium = iCur + nbCorners;
7025 vector< const SMDS_MeshNode* >::iterator i =
7026 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7028 curNodes[ iMedium ]);
7029 if ( i != uniqueNodes.end() )
7032 for ( ; i+1 != uniqueNodes.end(); ++i )
7041 case SMDSEntity_Polygon:
7042 case SMDSEntity_Quad_Polygon: // Polygon
7044 ElemFeatures* elemType = & newElemDefs[0];
7045 const bool isQuad = elemType->myIsQuad;
7047 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7048 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7050 // a polygon can divide into several elements
7051 vector<const SMDS_MeshNode *> polygons_nodes;
7052 vector<int> quantities;
7053 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7054 newElemDefs.resize( nbResElems );
7055 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7057 ElemFeatures* elemType = & newElemDefs[iface];
7058 if ( iface ) elemType->Init( elem );
7060 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7061 int nbNewNodes = quantities[iface];
7062 face_nodes.assign( polygons_nodes.begin() + inode,
7063 polygons_nodes.begin() + inode + nbNewNodes );
7064 inode += nbNewNodes;
7065 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7067 bool isValid = ( nbNewNodes % 2 == 0 );
7068 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7069 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7070 elemType->SetQuad( isValid );
7071 if ( isValid ) // put medium nodes after corners
7072 SMDS_MeshCell::applyInterlaceRev
7073 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7074 nbNewNodes ), face_nodes );
7076 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7078 nbUniqueNodes = newElemDefs[0].myNodes.size();
7082 case SMDSEntity_Polyhedra: // Polyhedral volume
7084 if ( nbUniqueNodes >= 4 )
7086 // each face has to be analyzed in order to check volume validity
7087 if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7089 int nbFaces = aPolyedre->NbFaces();
7091 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7092 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7093 vector<const SMDS_MeshNode *> faceNodes;
7097 for (int iface = 1; iface <= nbFaces; iface++)
7099 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7100 faceNodes.resize( nbFaceNodes );
7101 for (int inode = 1; inode <= nbFaceNodes; inode++)
7103 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7104 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7105 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7106 faceNode = (*nnIt).second;
7107 faceNodes[inode - 1] = faceNode;
7109 SimplifyFace(faceNodes, poly_nodes, quantities);
7112 if ( quantities.size() > 3 )
7114 // TODO: remove coincident faces
7116 nbUniqueNodes = newElemDefs[0].myNodes.size();
7124 // TODO not all the possible cases are solved. Find something more generic?
7125 case SMDSEntity_Edge: //////// EDGE
7126 case SMDSEntity_Triangle: //// TRIANGLE
7127 case SMDSEntity_Quad_Triangle:
7128 case SMDSEntity_Tetra:
7129 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7133 case SMDSEntity_Quad_Edge:
7137 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7139 if ( nbUniqueNodes < 3 )
7141 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7142 toRemove = true; // opposite nodes stick
7147 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7156 if ( nbUniqueNodes == 6 &&
7158 ( nbRepl == 1 || iRepl[1] >= 4 ))
7164 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7173 if ( nbUniqueNodes == 7 &&
7175 ( nbRepl == 1 || iRepl[1] != 8 ))
7181 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7183 if ( nbUniqueNodes == 4 ) {
7184 // ---------------------------------> tetrahedron
7185 if ( curNodes[3] == curNodes[4] &&
7186 curNodes[3] == curNodes[5] ) {
7190 else if ( curNodes[0] == curNodes[1] &&
7191 curNodes[0] == curNodes[2] ) {
7192 // bottom nodes stick: set a top before
7193 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7194 uniqueNodes[ 0 ] = curNodes [ 5 ];
7195 uniqueNodes[ 1 ] = curNodes [ 4 ];
7196 uniqueNodes[ 2 ] = curNodes [ 3 ];
7199 else if (( curNodes[0] == curNodes[3] ) +
7200 ( curNodes[1] == curNodes[4] ) +
7201 ( curNodes[2] == curNodes[5] ) == 2 ) {
7202 // a lateral face turns into a line
7206 else if ( nbUniqueNodes == 5 ) {
7207 // PENTAHEDRON --------------------> pyramid
7208 if ( curNodes[0] == curNodes[3] )
7210 uniqueNodes[ 0 ] = curNodes[ 1 ];
7211 uniqueNodes[ 1 ] = curNodes[ 4 ];
7212 uniqueNodes[ 2 ] = curNodes[ 5 ];
7213 uniqueNodes[ 3 ] = curNodes[ 2 ];
7214 uniqueNodes[ 4 ] = curNodes[ 0 ];
7217 if ( curNodes[1] == curNodes[4] )
7219 uniqueNodes[ 0 ] = curNodes[ 0 ];
7220 uniqueNodes[ 1 ] = curNodes[ 2 ];
7221 uniqueNodes[ 2 ] = curNodes[ 5 ];
7222 uniqueNodes[ 3 ] = curNodes[ 3 ];
7223 uniqueNodes[ 4 ] = curNodes[ 1 ];
7226 if ( curNodes[2] == curNodes[5] )
7228 uniqueNodes[ 0 ] = curNodes[ 0 ];
7229 uniqueNodes[ 1 ] = curNodes[ 3 ];
7230 uniqueNodes[ 2 ] = curNodes[ 4 ];
7231 uniqueNodes[ 3 ] = curNodes[ 1 ];
7232 uniqueNodes[ 4 ] = curNodes[ 2 ];
7238 case SMDSEntity_Hexa:
7240 //////////////////////////////////// HEXAHEDRON
7241 SMDS_VolumeTool hexa (elem);
7242 hexa.SetExternalNormal();
7243 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7244 //////////////////////// HEX ---> tetrahedron
7245 for ( int iFace = 0; iFace < 6; iFace++ ) {
7246 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7247 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7248 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7249 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7250 // one face turns into a point ...
7251 int pickInd = ind[ 0 ];
7252 int iOppFace = hexa.GetOppFaceIndex( iFace );
7253 ind = hexa.GetFaceNodesIndices( iOppFace );
7255 uniqueNodes.clear();
7256 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7257 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7260 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7262 if ( nbStick == 1 ) {
7263 // ... and the opposite one - into a triangle.
7265 uniqueNodes.push_back( curNodes[ pickInd ]);
7272 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7273 //////////////////////// HEX ---> prism
7274 int nbTria = 0, iTria[3];
7275 const int *ind; // indices of face nodes
7276 // look for triangular faces
7277 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7278 ind = hexa.GetFaceNodesIndices( iFace );
7279 TIDSortedNodeSet faceNodes;
7280 for ( iCur = 0; iCur < 4; iCur++ )
7281 faceNodes.insert( curNodes[ind[iCur]] );
7282 if ( faceNodes.size() == 3 )
7283 iTria[ nbTria++ ] = iFace;
7285 // check if triangles are opposite
7286 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7288 // set nodes of the bottom triangle
7289 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7291 for ( iCur = 0; iCur < 4; iCur++ )
7292 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7293 indB.push_back( ind[iCur] );
7294 if ( !hexa.IsForward() )
7295 std::swap( indB[0], indB[2] );
7296 for ( iCur = 0; iCur < 3; iCur++ )
7297 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7298 // set nodes of the top triangle
7299 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7300 for ( iCur = 0; iCur < 3; ++iCur )
7301 for ( int j = 0; j < 4; ++j )
7302 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7304 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7311 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7312 //////////////////// HEXAHEDRON ---> pyramid
7313 for ( int iFace = 0; iFace < 6; iFace++ ) {
7314 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7315 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7316 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7317 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7318 // one face turns into a point ...
7319 int iOppFace = hexa.GetOppFaceIndex( iFace );
7320 ind = hexa.GetFaceNodesIndices( iOppFace );
7321 uniqueNodes.clear();
7322 for ( iCur = 0; iCur < 4; iCur++ ) {
7323 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7326 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7328 if ( uniqueNodes.size() == 4 ) {
7329 // ... and the opposite one is a quadrangle
7331 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7332 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7340 if ( toRemove && nbUniqueNodes > 4 ) {
7341 ////////////////// HEXAHEDRON ---> polyhedron
7342 hexa.SetExternalNormal();
7343 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7344 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7345 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7346 quantities.reserve( 6 ); quantities.clear();
7347 for ( int iFace = 0; iFace < 6; iFace++ )
7349 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7350 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7351 curNodes[ind[1]] == curNodes[ind[3]] )
7354 break; // opposite nodes stick
7357 for ( iCur = 0; iCur < 4; iCur++ )
7359 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7360 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7362 if ( nodeSet.size() < 3 )
7363 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7365 quantities.push_back( nodeSet.size() );
7367 if ( quantities.size() >= 4 )
7370 nbUniqueNodes = poly_nodes.size();
7371 newElemDefs[0].SetPoly(true);
7375 } // case HEXAHEDRON
7380 } // switch ( entity )
7382 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7384 // erase from nodeNodeMap nodes whose merge spoils elem
7385 vector< const SMDS_MeshNode* > noMergeNodes;
7386 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7387 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7388 nodeNodeMap.erase( noMergeNodes[i] );
7391 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7393 uniqueNodes.resize( nbUniqueNodes );
7395 if ( !toRemove && nbResElems == 0 )
7398 newElemDefs.resize( nbResElems );
7404 // ========================================================
7405 // class : ComparableElement
7406 // purpose : allow comparing elements basing on their nodes
7407 // ========================================================
7409 class ComparableElement : public boost::container::flat_set< int >
7411 typedef boost::container::flat_set< int > int_set;
7413 const SMDS_MeshElement* myElem;
7415 mutable int myGroupID;
7419 ComparableElement( const SMDS_MeshElement* theElem ):
7420 myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7422 this->reserve( theElem->NbNodes() );
7423 for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7425 int id = nodeIt->next()->GetID();
7431 const SMDS_MeshElement* GetElem() const { return myElem; }
7433 int& GroupID() const { return myGroupID; }
7434 //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7436 ComparableElement( const ComparableElement& theSource ) // move copy
7438 ComparableElement& src = const_cast< ComparableElement& >( theSource );
7439 (int_set&) (*this ) = boost::move( src );
7440 myElem = src.myElem;
7441 mySumID = src.mySumID;
7442 myGroupID = src.myGroupID;
7445 static int HashCode(const ComparableElement& se, int limit )
7447 return ::HashCode( se.mySumID, limit );
7449 static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7451 return ( se1 == se2 );
7456 //=======================================================================
7457 //function : FindEqualElements
7458 //purpose : Return list of group of elements built on the same nodes.
7459 // Search among theElements or in the whole mesh if theElements is empty
7460 //=======================================================================
7462 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet & theElements,
7463 TListOfListOfElementsID & theGroupsOfElementsID )
7467 SMDS_ElemIteratorPtr elemIt;
7468 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7469 else elemIt = SMESHUtils::elemSetIterator( theElements );
7471 typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7472 typedef std::list<int> TGroupOfElems;
7473 TMapOfElements mapOfElements;
7474 std::vector< TGroupOfElems > arrayOfGroups;
7475 TGroupOfElems groupOfElems;
7477 while ( elemIt->more() )
7479 const SMDS_MeshElement* curElem = elemIt->next();
7480 if ( curElem->IsNull() )
7482 ComparableElement compElem = curElem;
7484 const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7485 if ( elemInSet.GetElem() != curElem ) // coincident elem
7487 int& iG = elemInSet.GroupID();
7490 iG = arrayOfGroups.size();
7491 arrayOfGroups.push_back( groupOfElems );
7492 arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7494 arrayOfGroups[ iG ].push_back( curElem->GetID() );
7498 groupOfElems.clear();
7499 std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7500 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7502 if ( groupIt->size() > 1 ) {
7503 //groupOfElems.sort(); -- theElements are sorted already
7504 theGroupsOfElementsID.emplace_back( *groupIt );
7509 //=======================================================================
7510 //function : MergeElements
7511 //purpose : In each given group, substitute all elements by the first one.
7512 //=======================================================================
7514 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7518 typedef list<int> TListOfIDs;
7519 TListOfIDs rmElemIds; // IDs of elems to remove
7521 SMESHDS_Mesh* aMesh = GetMeshDS();
7523 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7524 while ( groupsIt != theGroupsOfElementsID.end() ) {
7525 TListOfIDs& aGroupOfElemID = *groupsIt;
7526 aGroupOfElemID.sort();
7527 int elemIDToKeep = aGroupOfElemID.front();
7528 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7529 aGroupOfElemID.pop_front();
7530 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7531 while ( idIt != aGroupOfElemID.end() ) {
7532 int elemIDToRemove = *idIt;
7533 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7534 // add the kept element in groups of removed one (PAL15188)
7535 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7536 rmElemIds.push_back( elemIDToRemove );
7542 Remove( rmElemIds, false );
7545 //=======================================================================
7546 //function : MergeEqualElements
7547 //purpose : Remove all but one of elements built on the same nodes.
7548 //=======================================================================
7550 void SMESH_MeshEditor::MergeEqualElements()
7552 TIDSortedElemSet aMeshElements; /* empty input ==
7553 to merge equal elements in the whole mesh */
7554 TListOfListOfElementsID aGroupsOfElementsID;
7555 FindEqualElements( aMeshElements, aGroupsOfElementsID );
7556 MergeElements( aGroupsOfElementsID );
7559 //=======================================================================
7560 //function : findAdjacentFace
7562 //=======================================================================
7564 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7565 const SMDS_MeshNode* n2,
7566 const SMDS_MeshElement* elem)
7568 TIDSortedElemSet elemSet, avoidSet;
7570 avoidSet.insert ( elem );
7571 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7574 //=======================================================================
7575 //function : findSegment
7576 //purpose : Return a mesh segment by two nodes one of which can be medium
7577 //=======================================================================
7579 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7580 const SMDS_MeshNode* n2)
7582 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7583 while ( it->more() )
7585 const SMDS_MeshElement* seg = it->next();
7586 if ( seg->GetNodeIndex( n2 ) >= 0 )
7592 //=======================================================================
7593 //function : FindFreeBorder
7595 //=======================================================================
7597 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7599 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7600 const SMDS_MeshNode* theSecondNode,
7601 const SMDS_MeshNode* theLastNode,
7602 list< const SMDS_MeshNode* > & theNodes,
7603 list< const SMDS_MeshElement* >& theFaces)
7605 if ( !theFirstNode || !theSecondNode )
7607 // find border face between theFirstNode and theSecondNode
7608 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7612 theFaces.push_back( curElem );
7613 theNodes.push_back( theFirstNode );
7614 theNodes.push_back( theSecondNode );
7616 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7617 //TIDSortedElemSet foundElems;
7618 bool needTheLast = ( theLastNode != 0 );
7620 vector<const SMDS_MeshNode*> nodes;
7622 while ( nStart != theLastNode ) {
7623 if ( nStart == theFirstNode )
7624 return !needTheLast;
7626 // find all free border faces sharing nStart
7628 list< const SMDS_MeshElement* > curElemList;
7629 list< const SMDS_MeshNode* > nStartList;
7630 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7631 while ( invElemIt->more() ) {
7632 const SMDS_MeshElement* e = invElemIt->next();
7633 //if ( e == curElem || foundElems.insert( e ).second ) // e can encounter twice in border
7636 nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7637 SMDS_MeshElement::iterator() );
7638 nodes.push_back( nodes[ 0 ]);
7641 int iNode = 0, nbNodes = nodes.size() - 1;
7642 for ( iNode = 0; iNode < nbNodes; iNode++ )
7643 if ((( nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7644 ( nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7645 ( ControlFreeBorder( &nodes[ iNode ], e->GetID() )))
7647 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart )]);
7648 curElemList.push_back( e );
7652 // analyse the found
7654 int nbNewBorders = curElemList.size();
7655 if ( nbNewBorders == 0 ) {
7656 // no free border furthermore
7657 return !needTheLast;
7659 else if ( nbNewBorders == 1 ) {
7660 // one more element found
7662 nStart = nStartList.front();
7663 curElem = curElemList.front();
7664 theFaces.push_back( curElem );
7665 theNodes.push_back( nStart );
7668 // several continuations found
7669 list< const SMDS_MeshElement* >::iterator curElemIt;
7670 list< const SMDS_MeshNode* >::iterator nStartIt;
7671 // check if one of them reached the last node
7672 if ( needTheLast ) {
7673 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7674 curElemIt!= curElemList.end();
7675 curElemIt++, nStartIt++ )
7676 if ( *nStartIt == theLastNode ) {
7677 theFaces.push_back( *curElemIt );
7678 theNodes.push_back( *nStartIt );
7682 // find the best free border by the continuations
7683 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7684 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7685 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7686 curElemIt!= curElemList.end();
7687 curElemIt++, nStartIt++ )
7689 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7690 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7691 // find one more free border
7692 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7696 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7697 // choice: clear a worse one
7698 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7699 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7700 contNodes[ iWorse ].clear();
7701 contFaces[ iWorse ].clear();
7704 if ( contNodes[0].empty() && contNodes[1].empty() )
7707 // push_back the best free border
7708 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7709 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7710 //theNodes.pop_back(); // remove nIgnore
7711 theNodes.pop_back(); // remove nStart
7712 //theFaces.pop_back(); // remove curElem
7713 theNodes.splice( theNodes.end(), *cNL );
7714 theFaces.splice( theFaces.end(), *cFL );
7717 } // several continuations found
7718 } // while ( nStart != theLastNode )
7723 //=======================================================================
7724 //function : CheckFreeBorderNodes
7725 //purpose : Return true if the tree nodes are on a free border
7726 //=======================================================================
7728 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7729 const SMDS_MeshNode* theNode2,
7730 const SMDS_MeshNode* theNode3)
7732 list< const SMDS_MeshNode* > nodes;
7733 list< const SMDS_MeshElement* > faces;
7734 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7737 //=======================================================================
7738 //function : SewFreeBorder
7740 //warning : for border-to-side sewing theSideSecondNode is considered as
7741 // the last side node and theSideThirdNode is not used
7742 //=======================================================================
7744 SMESH_MeshEditor::Sew_Error
7745 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7746 const SMDS_MeshNode* theBordSecondNode,
7747 const SMDS_MeshNode* theBordLastNode,
7748 const SMDS_MeshNode* theSideFirstNode,
7749 const SMDS_MeshNode* theSideSecondNode,
7750 const SMDS_MeshNode* theSideThirdNode,
7751 const bool theSideIsFreeBorder,
7752 const bool toCreatePolygons,
7753 const bool toCreatePolyedrs)
7757 Sew_Error aResult = SEW_OK;
7759 // ====================================
7760 // find side nodes and elements
7761 // ====================================
7763 list< const SMDS_MeshNode* > nSide[ 2 ];
7764 list< const SMDS_MeshElement* > eSide[ 2 ];
7765 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7766 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7770 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7771 nSide[0], eSide[0])) {
7772 MESSAGE(" Free Border 1 not found " );
7773 aResult = SEW_BORDER1_NOT_FOUND;
7775 if (theSideIsFreeBorder) {
7778 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7779 nSide[1], eSide[1])) {
7780 MESSAGE(" Free Border 2 not found " );
7781 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7784 if ( aResult != SEW_OK )
7787 if (!theSideIsFreeBorder) {
7791 // -------------------------------------------------------------------------
7793 // 1. If nodes to merge are not coincident, move nodes of the free border
7794 // from the coord sys defined by the direction from the first to last
7795 // nodes of the border to the correspondent sys of the side 2
7796 // 2. On the side 2, find the links most co-directed with the correspondent
7797 // links of the free border
7798 // -------------------------------------------------------------------------
7800 // 1. Since sewing may break if there are volumes to split on the side 2,
7801 // we won't move nodes but just compute new coordinates for them
7802 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7803 TNodeXYZMap nBordXYZ;
7804 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7805 list< const SMDS_MeshNode* >::iterator nBordIt;
7807 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7808 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7809 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7810 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7811 double tol2 = 1.e-8;
7812 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7813 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7814 // Need node movement.
7816 // find X and Z axes to create trsf
7817 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7819 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7821 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7824 gp_Ax3 toBordAx( Pb1, Zb, X );
7825 gp_Ax3 fromSideAx( Ps1, Zs, X );
7826 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7828 gp_Trsf toBordSys, fromSide2Sys;
7829 toBordSys.SetTransformation( toBordAx );
7830 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7831 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7834 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7835 const SMDS_MeshNode* n = *nBordIt;
7836 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7837 toBordSys.Transforms( xyz );
7838 fromSide2Sys.Transforms( xyz );
7839 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7843 // just insert nodes XYZ in the nBordXYZ map
7844 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7845 const SMDS_MeshNode* n = *nBordIt;
7846 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7850 // 2. On the side 2, find the links most co-directed with the correspondent
7851 // links of the free border
7853 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7854 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7855 sideNodes.push_back( theSideFirstNode );
7857 bool hasVolumes = false;
7858 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7859 set<long> foundSideLinkIDs, checkedLinkIDs;
7860 SMDS_VolumeTool volume;
7861 //const SMDS_MeshNode* faceNodes[ 4 ];
7863 const SMDS_MeshNode* sideNode;
7864 const SMDS_MeshElement* sideElem = 0;
7865 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7866 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7867 nBordIt = bordNodes.begin();
7869 // border node position and border link direction to compare with
7870 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7871 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7872 // choose next side node by link direction or by closeness to
7873 // the current border node:
7874 bool searchByDir = ( *nBordIt != theBordLastNode );
7876 // find the next node on the Side 2
7878 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7880 checkedLinkIDs.clear();
7881 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7883 // loop on inverse elements of current node (prevSideNode) on the Side 2
7884 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7885 while ( invElemIt->more() )
7887 const SMDS_MeshElement* elem = invElemIt->next();
7888 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7889 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
7890 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7891 bool isVolume = volume.Set( elem );
7892 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7893 if ( isVolume ) // --volume
7895 else if ( elem->GetType() == SMDSAbs_Face ) { // --face
7896 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7897 SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
7898 while ( nIt->more() ) {
7899 nodes[ iNode ] = cast2Node( nIt->next() );
7900 if ( nodes[ iNode++ ] == prevSideNode )
7901 iPrevNode = iNode - 1;
7903 // there are 2 links to check
7908 // loop on links, to be precise, on the second node of links
7909 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7910 const SMDS_MeshNode* n = nodes[ iNode ];
7912 if ( !volume.IsLinked( n, prevSideNode ))
7916 if ( iNode ) // a node before prevSideNode
7917 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7918 else // a node after prevSideNode
7919 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7921 // check if this link was already used
7922 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7923 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7924 if (!isJustChecked &&
7925 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7927 // test a link geometrically
7928 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7929 bool linkIsBetter = false;
7930 double dot = 0.0, dist = 0.0;
7931 if ( searchByDir ) { // choose most co-directed link
7932 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7933 linkIsBetter = ( dot > maxDot );
7935 else { // choose link with the node closest to bordPos
7936 dist = ( nextXYZ - bordPos ).SquareModulus();
7937 linkIsBetter = ( dist < minDist );
7939 if ( linkIsBetter ) {
7948 } // loop on inverse elements of prevSideNode
7951 MESSAGE(" Can't find path by links of the Side 2 ");
7952 return SEW_BAD_SIDE_NODES;
7954 sideNodes.push_back( sideNode );
7955 sideElems.push_back( sideElem );
7956 foundSideLinkIDs.insert ( linkID );
7957 prevSideNode = sideNode;
7959 if ( *nBordIt == theBordLastNode )
7960 searchByDir = false;
7962 // find the next border link to compare with
7963 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7964 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7965 // move to next border node if sideNode is before forward border node (bordPos)
7966 while ( *nBordIt != theBordLastNode && !searchByDir ) {
7967 prevBordNode = *nBordIt;
7969 bordPos = nBordXYZ[ *nBordIt ];
7970 bordDir = bordPos - nBordXYZ[ prevBordNode ];
7971 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7975 while ( sideNode != theSideSecondNode );
7977 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7978 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7979 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7981 } // end nodes search on the side 2
7983 // ============================
7984 // sew the border to the side 2
7985 // ============================
7987 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
7988 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
7990 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
7991 if ( toMergeConformal && toCreatePolygons )
7993 // do not merge quadrangles if polygons are OK (IPAL0052824)
7994 eIt[0] = eSide[0].begin();
7995 eIt[1] = eSide[1].begin();
7996 bool allQuads[2] = { true, true };
7997 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7998 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
7999 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8001 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8004 TListOfListOfNodes nodeGroupsToMerge;
8005 if (( toMergeConformal ) ||
8006 ( theSideIsFreeBorder && !theSideThirdNode )) {
8008 // all nodes are to be merged
8010 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8011 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8012 nIt[0]++, nIt[1]++ )
8014 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8015 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8016 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8021 // insert new nodes into the border and the side to get equal nb of segments
8023 // get normalized parameters of nodes on the borders
8024 vector< double > param[ 2 ];
8025 param[0].resize( maxNbNodes );
8026 param[1].resize( maxNbNodes );
8028 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8029 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8030 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8031 const SMDS_MeshNode* nPrev = *nIt;
8032 double bordLength = 0;
8033 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8034 const SMDS_MeshNode* nCur = *nIt;
8035 gp_XYZ segment (nCur->X() - nPrev->X(),
8036 nCur->Y() - nPrev->Y(),
8037 nCur->Z() - nPrev->Z());
8038 double segmentLen = segment.Modulus();
8039 bordLength += segmentLen;
8040 param[ iBord ][ iNode ] = bordLength;
8043 // normalize within [0,1]
8044 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8045 param[ iBord ][ iNode ] /= bordLength;
8049 // loop on border segments
8050 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8051 int i[ 2 ] = { 0, 0 };
8052 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8053 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8055 // element can be split while iterating on border if it has two edges in the border
8056 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* > elemReplaceMap;
8057 std::map< const SMDS_MeshElement* , const SMDS_MeshElement* >::iterator elemReplaceMapIt;
8059 TElemOfNodeListMap insertMap;
8060 TElemOfNodeListMap::iterator insertMapIt;
8062 // key: elem to insert nodes into
8063 // value: 2 nodes to insert between + nodes to be inserted
8065 bool next[ 2 ] = { false, false };
8067 // find min adjacent segment length after sewing
8068 double nextParam = 10., prevParam = 0;
8069 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8070 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8071 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8072 if ( i[ iBord ] > 0 )
8073 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8075 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8076 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8077 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8079 // choose to insert or to merge nodes
8080 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8081 if ( Abs( du ) <= minSegLen * 0.2 ) {
8084 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8085 const SMDS_MeshNode* n0 = *nIt[0];
8086 const SMDS_MeshNode* n1 = *nIt[1];
8087 nodeGroupsToMerge.back().push_back( n1 );
8088 nodeGroupsToMerge.back().push_back( n0 );
8089 // position of node of the border changes due to merge
8090 param[ 0 ][ i[0] ] += du;
8091 // move n1 for the sake of elem shape evaluation during insertion.
8092 // n1 will be removed by MergeNodes() anyway
8093 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8094 next[0] = next[1] = true;
8099 int intoBord = ( du < 0 ) ? 0 : 1;
8100 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8101 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8102 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8103 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8104 if ( intoBord == 1 ) {
8105 // move node of the border to be on a link of elem of the side
8106 SMESH_NodeXYZ p1( n1 ), p2( n2 );
8107 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8108 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8109 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8111 elemReplaceMapIt = elemReplaceMap.find( elem );
8112 if ( elemReplaceMapIt != elemReplaceMap.end() )
8113 elem = elemReplaceMapIt->second;
8115 insertMapIt = insertMap.find( elem );
8116 bool notFound = ( insertMapIt == insertMap.end() );
8117 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8119 // insert into another link of the same element:
8120 // 1. perform insertion into the other link of the elem
8121 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8122 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8123 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8124 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8125 // 2. perform insertion into the link of adjacent faces
8126 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8127 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8129 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8130 InsertNodesIntoLink( seg, n12, n22, nodeList );
8132 if (toCreatePolyedrs) {
8133 // perform insertion into the links of adjacent volumes
8134 UpdateVolumes(n12, n22, nodeList);
8136 // 3. find an element appeared on n1 and n2 after the insertion
8137 insertMap.erase( insertMapIt );
8138 const SMDS_MeshElement* elem2 = findAdjacentFace( n1, n2, 0 );
8139 elemReplaceMap.insert( std::make_pair( elem, elem2 ));
8142 if ( notFound || otherLink ) {
8143 // add element and nodes of the side into the insertMap
8144 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8145 (*insertMapIt).second.push_back( n1 );
8146 (*insertMapIt).second.push_back( n2 );
8148 // add node to be inserted into elem
8149 (*insertMapIt).second.push_back( nIns );
8150 next[ 1 - intoBord ] = true;
8153 // go to the next segment
8154 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8155 if ( next[ iBord ] ) {
8156 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8158 nPrev[ iBord ] = *nIt[ iBord ];
8159 nIt[ iBord ]++; i[ iBord ]++;
8163 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8165 // perform insertion of nodes into elements
8167 for (insertMapIt = insertMap.begin();
8168 insertMapIt != insertMap.end();
8171 const SMDS_MeshElement* elem = (*insertMapIt).first;
8172 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8173 if ( nodeList.size() < 3 ) continue;
8174 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8175 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8177 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8179 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8180 InsertNodesIntoLink( seg, n1, n2, nodeList );
8183 if ( !theSideIsFreeBorder ) {
8184 // look for and insert nodes into the faces adjacent to elem
8185 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8186 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8189 if (toCreatePolyedrs) {
8190 // perform insertion into the links of adjacent volumes
8191 UpdateVolumes(n1, n2, nodeList);
8194 } // end: insert new nodes
8196 MergeNodes ( nodeGroupsToMerge );
8199 // Remove coincident segments
8202 TIDSortedElemSet segments;
8203 SMESH_SequenceOfElemPtr newFaces;
8204 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8206 if ( !myLastCreatedElems[i] ) continue;
8207 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8208 segments.insert( segments.end(), myLastCreatedElems[i] );
8210 newFaces.push_back( myLastCreatedElems[i] );
8212 // get segments adjacent to merged nodes
8213 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8214 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8216 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8217 if ( nodes.front()->IsNull() ) continue;
8218 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8219 while ( segIt->more() )
8220 segments.insert( segIt->next() );
8224 TListOfListOfElementsID equalGroups;
8225 if ( !segments.empty() )
8226 FindEqualElements( segments, equalGroups );
8227 if ( !equalGroups.empty() )
8229 // remove from segments those that will be removed
8230 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8231 for ( ; itGroups != equalGroups.end(); ++itGroups )
8233 list< int >& group = *itGroups;
8234 list< int >::iterator id = group.begin();
8235 for ( ++id; id != group.end(); ++id )
8236 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8237 segments.erase( seg );
8239 // remove equal segments
8240 MergeElements( equalGroups );
8242 // restore myLastCreatedElems
8243 myLastCreatedElems = newFaces;
8244 TIDSortedElemSet::iterator seg = segments.begin();
8245 for ( ; seg != segments.end(); ++seg )
8246 myLastCreatedElems.push_back( *seg );
8252 //=======================================================================
8253 //function : InsertNodesIntoLink
8254 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8255 // and theBetweenNode2 and split theElement
8256 //=======================================================================
8258 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8259 const SMDS_MeshNode* theBetweenNode1,
8260 const SMDS_MeshNode* theBetweenNode2,
8261 list<const SMDS_MeshNode*>& theNodesToInsert,
8262 const bool toCreatePoly)
8264 if ( !theElement ) return;
8266 SMESHDS_Mesh *aMesh = GetMeshDS();
8267 vector<const SMDS_MeshElement*> newElems;
8269 if ( theElement->GetType() == SMDSAbs_Edge )
8271 theNodesToInsert.push_front( theBetweenNode1 );
8272 theNodesToInsert.push_back ( theBetweenNode2 );
8273 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8274 const SMDS_MeshNode* n1 = *n;
8275 for ( ++n; n != theNodesToInsert.end(); ++n )
8277 const SMDS_MeshNode* n2 = *n;
8278 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8279 AddToSameGroups( seg, theElement, aMesh );
8281 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8284 theNodesToInsert.pop_front();
8285 theNodesToInsert.pop_back();
8287 if ( theElement->IsQuadratic() ) // add a not split part
8289 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8290 theElement->end_nodes() );
8291 int iOther = 0, nbN = nodes.size();
8292 for ( ; iOther < nbN; ++iOther )
8293 if ( nodes[iOther] != theBetweenNode1 &&
8294 nodes[iOther] != theBetweenNode2 )
8298 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8299 AddToSameGroups( seg, theElement, aMesh );
8301 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8303 else if ( iOther == 2 )
8305 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8306 AddToSameGroups( seg, theElement, aMesh );
8308 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8311 // treat new elements
8312 for ( size_t i = 0; i < newElems.size(); ++i )
8315 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8316 myLastCreatedElems.push_back( newElems[i] );
8318 ReplaceElemInGroups( theElement, newElems, aMesh );
8319 aMesh->RemoveElement( theElement );
8322 } // if ( theElement->GetType() == SMDSAbs_Edge )
8324 const SMDS_MeshElement* theFace = theElement;
8325 if ( theFace->GetType() != SMDSAbs_Face ) return;
8327 // find indices of 2 link nodes and of the rest nodes
8328 int iNode = 0, il1, il2, i3, i4;
8329 il1 = il2 = i3 = i4 = -1;
8330 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8332 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8333 while ( nodeIt->more() ) {
8334 const SMDS_MeshNode* n = nodeIt->next();
8335 if ( n == theBetweenNode1 )
8337 else if ( n == theBetweenNode2 )
8343 nodes[ iNode++ ] = n;
8345 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8348 // arrange link nodes to go one after another regarding the face orientation
8349 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8350 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8355 aNodesToInsert.reverse();
8357 // check that not link nodes of a quadrangles are in good order
8358 int nbFaceNodes = theFace->NbNodes();
8359 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8365 if (toCreatePoly || theFace->IsPoly()) {
8368 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8370 // add nodes of face up to first node of link
8372 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8373 while ( nodeIt->more() && !isFLN ) {
8374 const SMDS_MeshNode* n = nodeIt->next();
8375 poly_nodes[iNode++] = n;
8376 isFLN = ( n == nodes[il1] );
8378 // add nodes to insert
8379 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8380 for (; nIt != aNodesToInsert.end(); nIt++) {
8381 poly_nodes[iNode++] = *nIt;
8383 // add nodes of face starting from last node of link
8384 while ( nodeIt->more() ) {
8385 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8386 poly_nodes[iNode++] = n;
8390 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8393 else if ( !theFace->IsQuadratic() )
8395 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8396 int nbLinkNodes = 2 + aNodesToInsert.size();
8397 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8398 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8399 linkNodes[ 0 ] = nodes[ il1 ];
8400 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8401 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8402 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8403 linkNodes[ iNode++ ] = *nIt;
8405 // decide how to split a quadrangle: compare possible variants
8406 // and choose which of splits to be a quadrangle
8407 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8408 if ( nbFaceNodes == 3 ) {
8409 iBestQuad = nbSplits;
8412 else if ( nbFaceNodes == 4 ) {
8413 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8414 double aBestRate = DBL_MAX;
8415 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8417 double aBadRate = 0;
8418 // evaluate elements quality
8419 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8420 if ( iSplit == iQuad ) {
8421 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8425 aBadRate += getBadRate( &quad, aCrit );
8428 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8430 nodes[ iSplit < iQuad ? i4 : i3 ]);
8431 aBadRate += getBadRate( &tria, aCrit );
8435 if ( aBadRate < aBestRate ) {
8437 aBestRate = aBadRate;
8442 // create new elements
8444 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8446 if ( iSplit == iBestQuad )
8447 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8452 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8454 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8457 const SMDS_MeshNode* newNodes[ 4 ];
8458 newNodes[ 0 ] = linkNodes[ i1 ];
8459 newNodes[ 1 ] = linkNodes[ i2 ];
8460 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8461 newNodes[ 3 ] = nodes[ i4 ];
8462 if (iSplit == iBestQuad)
8463 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8465 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8467 } // end if(!theFace->IsQuadratic())
8469 else { // theFace is quadratic
8470 // we have to split theFace on simple triangles and one simple quadrangle
8472 int nbshift = tmp*2;
8473 // shift nodes in nodes[] by nbshift
8475 for(i=0; i<nbshift; i++) {
8476 const SMDS_MeshNode* n = nodes[0];
8477 for(j=0; j<nbFaceNodes-1; j++) {
8478 nodes[j] = nodes[j+1];
8480 nodes[nbFaceNodes-1] = n;
8482 il1 = il1 - nbshift;
8483 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8484 // n0 n1 n2 n0 n1 n2
8485 // +-----+-----+ +-----+-----+
8494 // create new elements
8496 if ( nbFaceNodes == 6 ) { // quadratic triangle
8497 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8498 if ( theFace->IsMediumNode(nodes[il1]) ) {
8499 // create quadrangle
8500 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8506 // create quadrangle
8507 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8513 else { // nbFaceNodes==8 - quadratic quadrangle
8514 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8515 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8516 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8517 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8518 // create quadrangle
8519 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8525 // create quadrangle
8526 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8532 // create needed triangles using n1,n2,n3 and inserted nodes
8533 int nbn = 2 + aNodesToInsert.size();
8534 vector<const SMDS_MeshNode*> aNodes(nbn);
8535 aNodes[0 ] = nodes[n1];
8536 aNodes[nbn-1] = nodes[n2];
8537 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8538 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8539 aNodes[iNode++] = *nIt;
8541 for ( i = 1; i < nbn; i++ )
8542 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8545 // remove the old face
8546 for ( size_t i = 0; i < newElems.size(); ++i )
8549 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8550 myLastCreatedElems.push_back( newElems[i] );
8552 ReplaceElemInGroups( theFace, newElems, aMesh );
8553 aMesh->RemoveElement(theFace);
8555 } // InsertNodesIntoLink()
8557 //=======================================================================
8558 //function : UpdateVolumes
8560 //=======================================================================
8562 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8563 const SMDS_MeshNode* theBetweenNode2,
8564 list<const SMDS_MeshNode*>& theNodesToInsert)
8568 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8569 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8570 const SMDS_MeshElement* elem = invElemIt->next();
8572 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8573 SMDS_VolumeTool aVolume (elem);
8574 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8577 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8578 int iface, nbFaces = aVolume.NbFaces();
8579 vector<const SMDS_MeshNode *> poly_nodes;
8580 vector<int> quantities (nbFaces);
8582 for (iface = 0; iface < nbFaces; iface++) {
8583 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8584 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8585 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8587 for (int inode = 0; inode < nbFaceNodes; inode++) {
8588 poly_nodes.push_back(faceNodes[inode]);
8590 if (nbInserted == 0) {
8591 if (faceNodes[inode] == theBetweenNode1) {
8592 if (faceNodes[inode + 1] == theBetweenNode2) {
8593 nbInserted = theNodesToInsert.size();
8595 // add nodes to insert
8596 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8597 for (; nIt != theNodesToInsert.end(); nIt++) {
8598 poly_nodes.push_back(*nIt);
8602 else if (faceNodes[inode] == theBetweenNode2) {
8603 if (faceNodes[inode + 1] == theBetweenNode1) {
8604 nbInserted = theNodesToInsert.size();
8606 // add nodes to insert in reversed order
8607 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8609 for (; nIt != theNodesToInsert.begin(); nIt--) {
8610 poly_nodes.push_back(*nIt);
8612 poly_nodes.push_back(*nIt);
8619 quantities[iface] = nbFaceNodes + nbInserted;
8622 // Replace the volume
8623 SMESHDS_Mesh *aMesh = GetMeshDS();
8625 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8627 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8628 myLastCreatedElems.push_back( newElem );
8629 ReplaceElemInGroups( elem, newElem, aMesh );
8631 aMesh->RemoveElement( elem );
8637 //================================================================================
8639 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8641 //================================================================================
8643 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8644 vector<const SMDS_MeshNode *> & nodes,
8645 vector<int> & nbNodeInFaces )
8648 nbNodeInFaces.clear();
8649 SMDS_VolumeTool vTool ( elem );
8650 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8652 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8653 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8654 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8659 //=======================================================================
8661 * \brief Convert elements contained in a sub-mesh to quadratic
8662 * \return int - nb of checked elements
8664 //=======================================================================
8666 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8667 SMESH_MesherHelper& theHelper,
8668 const bool theForce3d)
8670 //MESSAGE("convertElemToQuadratic");
8672 if( !theSm ) return nbElem;
8674 vector<int> nbNodeInFaces;
8675 vector<const SMDS_MeshNode *> nodes;
8676 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8677 while(ElemItr->more())
8680 const SMDS_MeshElement* elem = ElemItr->next();
8681 if( !elem ) continue;
8683 // analyse a necessity of conversion
8684 const SMDSAbs_ElementType aType = elem->GetType();
8685 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8687 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8688 bool hasCentralNodes = false;
8689 if ( elem->IsQuadratic() )
8692 switch ( aGeomType ) {
8693 case SMDSEntity_Quad_Triangle:
8694 case SMDSEntity_Quad_Quadrangle:
8695 case SMDSEntity_Quad_Hexa:
8696 case SMDSEntity_Quad_Penta:
8697 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8699 case SMDSEntity_BiQuad_Triangle:
8700 case SMDSEntity_BiQuad_Quadrangle:
8701 case SMDSEntity_TriQuad_Hexa:
8702 case SMDSEntity_BiQuad_Penta:
8703 alreadyOK = theHelper.GetIsBiQuadratic();
8704 hasCentralNodes = true;
8709 // take into account already present medium nodes
8711 case SMDSAbs_Volume:
8712 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8714 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8716 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8722 // get elem data needed to re-create it
8724 const int id = elem->GetID();
8725 const int nbNodes = elem->NbCornerNodes();
8726 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8727 if ( aGeomType == SMDSEntity_Polyhedra )
8728 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
8729 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8730 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8732 // remove a linear element
8733 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8735 // remove central nodes of biquadratic elements (biquad->quad conversion)
8736 if ( hasCentralNodes )
8737 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8738 if ( nodes[i]->NbInverseElements() == 0 )
8739 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8741 const SMDS_MeshElement* NewElem = 0;
8747 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8755 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8758 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8761 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8765 case SMDSAbs_Volume :
8769 case SMDSEntity_Tetra:
8770 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8772 case SMDSEntity_Pyramid:
8773 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8775 case SMDSEntity_Penta:
8776 case SMDSEntity_Quad_Penta:
8777 case SMDSEntity_BiQuad_Penta:
8778 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8780 case SMDSEntity_Hexa:
8781 case SMDSEntity_Quad_Hexa:
8782 case SMDSEntity_TriQuad_Hexa:
8783 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8784 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8786 case SMDSEntity_Hexagonal_Prism:
8788 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8795 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8796 if( NewElem && NewElem->getshapeId() < 1 )
8797 theSm->AddElement( NewElem );
8801 //=======================================================================
8802 //function : ConvertToQuadratic
8804 //=======================================================================
8806 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8808 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
8809 SMESHDS_Mesh* meshDS = GetMeshDS();
8811 SMESH_MesherHelper aHelper(*myMesh);
8813 aHelper.SetIsQuadratic( true );
8814 aHelper.SetIsBiQuadratic( theToBiQuad );
8815 aHelper.SetElementsOnShape(true);
8816 aHelper.ToFixNodeParameters( true );
8818 // convert elements assigned to sub-meshes
8819 int nbCheckedElems = 0;
8820 if ( myMesh->HasShapeToMesh() )
8822 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8824 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8825 while ( smIt->more() ) {
8826 SMESH_subMesh* sm = smIt->next();
8827 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8828 aHelper.SetSubShape( sm->GetSubShape() );
8829 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8835 // convert elements NOT assigned to sub-meshes
8836 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8837 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8839 aHelper.SetElementsOnShape(false);
8840 SMESHDS_SubMesh *smDS = 0;
8843 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8844 while( aEdgeItr->more() )
8846 const SMDS_MeshEdge* edge = aEdgeItr->next();
8847 if ( !edge->IsQuadratic() )
8849 int id = edge->GetID();
8850 const SMDS_MeshNode* n1 = edge->GetNode(0);
8851 const SMDS_MeshNode* n2 = edge->GetNode(1);
8853 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8855 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8856 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8860 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8865 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8866 while( aFaceItr->more() )
8868 const SMDS_MeshFace* face = aFaceItr->next();
8869 if ( !face ) continue;
8871 const SMDSAbs_EntityType type = face->GetEntityType();
8875 case SMDSEntity_Quad_Triangle:
8876 case SMDSEntity_Quad_Quadrangle:
8877 alreadyOK = !theToBiQuad;
8878 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8880 case SMDSEntity_BiQuad_Triangle:
8881 case SMDSEntity_BiQuad_Quadrangle:
8882 alreadyOK = theToBiQuad;
8883 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8885 default: alreadyOK = false;
8890 const int id = face->GetID();
8891 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8893 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8895 SMDS_MeshFace * NewFace = 0;
8898 case SMDSEntity_Triangle:
8899 case SMDSEntity_Quad_Triangle:
8900 case SMDSEntity_BiQuad_Triangle:
8901 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8902 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8903 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8906 case SMDSEntity_Quadrangle:
8907 case SMDSEntity_Quad_Quadrangle:
8908 case SMDSEntity_BiQuad_Quadrangle:
8909 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8910 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8911 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8915 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8917 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8921 vector<int> nbNodeInFaces;
8922 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8923 while(aVolumeItr->more())
8925 const SMDS_MeshVolume* volume = aVolumeItr->next();
8926 if ( !volume ) continue;
8928 const SMDSAbs_EntityType type = volume->GetEntityType();
8929 if ( volume->IsQuadratic() )
8934 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8935 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8936 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
8937 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
8938 default: alreadyOK = true;
8942 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8946 const int id = volume->GetID();
8947 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8948 if ( type == SMDSEntity_Polyhedra )
8949 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
8950 else if ( type == SMDSEntity_Hexagonal_Prism )
8951 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8953 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8955 SMDS_MeshVolume * NewVolume = 0;
8958 case SMDSEntity_Tetra:
8959 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8961 case SMDSEntity_Hexa:
8962 case SMDSEntity_Quad_Hexa:
8963 case SMDSEntity_TriQuad_Hexa:
8964 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8965 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8966 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8967 if ( nodes[i]->NbInverseElements() == 0 )
8968 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8970 case SMDSEntity_Pyramid:
8971 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8972 nodes[3], nodes[4], id, theForce3d);
8974 case SMDSEntity_Penta:
8975 case SMDSEntity_Quad_Penta:
8976 case SMDSEntity_BiQuad_Penta:
8977 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8978 nodes[3], nodes[4], nodes[5], id, theForce3d);
8979 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
8980 if ( nodes[i]->NbInverseElements() == 0 )
8981 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8983 case SMDSEntity_Hexagonal_Prism:
8985 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8987 ReplaceElemInGroups(volume, NewVolume, meshDS);
8992 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8993 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8994 // aHelper.FixQuadraticElements(myError);
8995 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8999 //================================================================================
9001 * \brief Makes given elements quadratic
9002 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9003 * \param theElements - elements to make quadratic
9005 //================================================================================
9007 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9008 TIDSortedElemSet& theElements,
9009 const bool theToBiQuad)
9011 if ( theElements.empty() ) return;
9013 // we believe that all theElements are of the same type
9014 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9016 // get all nodes shared by theElements
9017 TIDSortedNodeSet allNodes;
9018 TIDSortedElemSet::iterator eIt = theElements.begin();
9019 for ( ; eIt != theElements.end(); ++eIt )
9020 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9022 // complete theElements with elements of lower dim whose all nodes are in allNodes
9024 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9025 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9026 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9027 for ( ; nIt != allNodes.end(); ++nIt )
9029 const SMDS_MeshNode* n = *nIt;
9030 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9031 while ( invIt->more() )
9033 const SMDS_MeshElement* e = invIt->next();
9034 const SMDSAbs_ElementType type = e->GetType();
9035 if ( e->IsQuadratic() )
9037 quadAdjacentElems[ type ].insert( e );
9040 switch ( e->GetEntityType() ) {
9041 case SMDSEntity_Quad_Triangle:
9042 case SMDSEntity_Quad_Quadrangle:
9043 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9044 case SMDSEntity_BiQuad_Triangle:
9045 case SMDSEntity_BiQuad_Quadrangle:
9046 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9047 default: alreadyOK = true;
9052 if ( type >= elemType )
9053 continue; // same type or more complex linear element
9055 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9056 continue; // e is already checked
9060 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9061 while ( nodeIt->more() && allIn )
9062 allIn = allNodes.count( nodeIt->next() );
9064 theElements.insert(e );
9068 SMESH_MesherHelper helper(*myMesh);
9069 helper.SetIsQuadratic( true );
9070 helper.SetIsBiQuadratic( theToBiQuad );
9072 // add links of quadratic adjacent elements to the helper
9074 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9075 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9076 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9078 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9080 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9081 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9082 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9084 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9086 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9087 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9088 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9090 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9093 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9095 SMESHDS_Mesh* meshDS = GetMeshDS();
9096 SMESHDS_SubMesh* smDS = 0;
9097 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9099 const SMDS_MeshElement* elem = *eIt;
9102 int nbCentralNodes = 0;
9103 switch ( elem->GetEntityType() ) {
9104 // linear convertible
9105 case SMDSEntity_Edge:
9106 case SMDSEntity_Triangle:
9107 case SMDSEntity_Quadrangle:
9108 case SMDSEntity_Tetra:
9109 case SMDSEntity_Pyramid:
9110 case SMDSEntity_Hexa:
9111 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9112 // quadratic that can become bi-quadratic
9113 case SMDSEntity_Quad_Triangle:
9114 case SMDSEntity_Quad_Quadrangle:
9115 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9117 case SMDSEntity_BiQuad_Triangle:
9118 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9119 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9121 default: alreadyOK = true;
9123 if ( alreadyOK ) continue;
9125 const SMDSAbs_ElementType type = elem->GetType();
9126 const int id = elem->GetID();
9127 const int nbNodes = elem->NbCornerNodes();
9128 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9130 helper.SetSubShape( elem->getshapeId() );
9132 if ( !smDS || !smDS->Contains( elem ))
9133 smDS = meshDS->MeshElements( elem->getshapeId() );
9134 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9136 SMDS_MeshElement * newElem = 0;
9139 case 4: // cases for most frequently used element types go first (for optimization)
9140 if ( type == SMDSAbs_Volume )
9141 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9143 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9146 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9147 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9150 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9153 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9156 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9157 nodes[4], id, theForce3d);
9160 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9161 nodes[4], nodes[5], id, theForce3d);
9165 ReplaceElemInGroups( elem, newElem, meshDS);
9166 if( newElem && smDS )
9167 smDS->AddElement( newElem );
9169 // remove central nodes
9170 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9171 if ( nodes[i]->NbInverseElements() == 0 )
9172 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9174 } // loop on theElements
9177 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9178 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9179 // helper.FixQuadraticElements( myError );
9180 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9184 //=======================================================================
9186 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9187 * \return int - nb of checked elements
9189 //=======================================================================
9191 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9192 SMDS_ElemIteratorPtr theItr,
9193 const int theShapeID)
9196 SMESHDS_Mesh* meshDS = GetMeshDS();
9197 ElemFeatures elemType;
9198 vector<const SMDS_MeshNode *> nodes;
9200 while( theItr->more() )
9202 const SMDS_MeshElement* elem = theItr->next();
9204 if( elem && elem->IsQuadratic())
9207 int nbCornerNodes = elem->NbCornerNodes();
9208 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9210 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9212 //remove a quadratic element
9213 if ( !theSm || !theSm->Contains( elem ))
9214 theSm = meshDS->MeshElements( elem->getshapeId() );
9215 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9217 // remove medium nodes
9218 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9219 if ( nodes[i]->NbInverseElements() == 0 )
9220 meshDS->RemoveFreeNode( nodes[i], theSm );
9222 // add a linear element
9223 nodes.resize( nbCornerNodes );
9224 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9225 ReplaceElemInGroups(elem, newElem, meshDS);
9226 if( theSm && newElem )
9227 theSm->AddElement( newElem );
9233 //=======================================================================
9234 //function : ConvertFromQuadratic
9236 //=======================================================================
9238 bool SMESH_MeshEditor::ConvertFromQuadratic()
9240 int nbCheckedElems = 0;
9241 if ( myMesh->HasShapeToMesh() )
9243 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9245 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9246 while ( smIt->more() ) {
9247 SMESH_subMesh* sm = smIt->next();
9248 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9249 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9255 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9256 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9258 SMESHDS_SubMesh *aSM = 0;
9259 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9267 //================================================================================
9269 * \brief Return true if all medium nodes of the element are in the node set
9271 //================================================================================
9273 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9275 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9276 if ( !nodeSet.count( elem->GetNode(i) ))
9282 //================================================================================
9284 * \brief Makes given elements linear
9286 //================================================================================
9288 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9290 if ( theElements.empty() ) return;
9292 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9293 set<int> mediumNodeIDs;
9294 TIDSortedElemSet::iterator eIt = theElements.begin();
9295 for ( ; eIt != theElements.end(); ++eIt )
9297 const SMDS_MeshElement* e = *eIt;
9298 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9299 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9302 // replace given elements by linear ones
9303 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9304 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9306 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9307 // except those elements sharing medium nodes of quadratic element whose medium nodes
9308 // are not all in mediumNodeIDs
9310 // get remaining medium nodes
9311 TIDSortedNodeSet mediumNodes;
9312 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9313 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9314 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9315 mediumNodes.insert( mediumNodes.end(), n );
9317 // find more quadratic elements to convert
9318 TIDSortedElemSet moreElemsToConvert;
9319 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9320 for ( ; nIt != mediumNodes.end(); ++nIt )
9322 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9323 while ( invIt->more() )
9325 const SMDS_MeshElement* e = invIt->next();
9326 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9328 // find a more complex element including e and
9329 // whose medium nodes are not in mediumNodes
9330 bool complexFound = false;
9331 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9333 SMDS_ElemIteratorPtr invIt2 =
9334 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9335 while ( invIt2->more() )
9337 const SMDS_MeshElement* eComplex = invIt2->next();
9338 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9340 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9341 if ( nbCommonNodes == e->NbNodes())
9343 complexFound = true;
9344 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9350 if ( !complexFound )
9351 moreElemsToConvert.insert( e );
9355 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9356 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9359 //=======================================================================
9360 //function : SewSideElements
9362 //=======================================================================
9364 SMESH_MeshEditor::Sew_Error
9365 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9366 TIDSortedElemSet& theSide2,
9367 const SMDS_MeshNode* theFirstNode1,
9368 const SMDS_MeshNode* theFirstNode2,
9369 const SMDS_MeshNode* theSecondNode1,
9370 const SMDS_MeshNode* theSecondNode2)
9374 if ( theSide1.size() != theSide2.size() )
9375 return SEW_DIFF_NB_OF_ELEMENTS;
9377 Sew_Error aResult = SEW_OK;
9379 // 1. Build set of faces representing each side
9380 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9381 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9383 // =======================================================================
9384 // 1. Build set of faces representing each side:
9385 // =======================================================================
9386 // a. build set of nodes belonging to faces
9387 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9388 // c. create temporary faces representing side of volumes if correspondent
9389 // face does not exist
9391 SMESHDS_Mesh* aMesh = GetMeshDS();
9392 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9393 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9394 TIDSortedElemSet faceSet1, faceSet2;
9395 set<const SMDS_MeshElement*> volSet1, volSet2;
9396 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9397 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9398 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9399 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9400 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9401 int iSide, iFace, iNode;
9403 list<const SMDS_MeshElement* > tempFaceList;
9404 for ( iSide = 0; iSide < 2; iSide++ ) {
9405 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9406 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9407 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9408 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9409 set<const SMDS_MeshElement*>::iterator vIt;
9410 TIDSortedElemSet::iterator eIt;
9411 set<const SMDS_MeshNode*>::iterator nIt;
9413 // check that given nodes belong to given elements
9414 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9415 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9416 int firstIndex = -1, secondIndex = -1;
9417 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9418 const SMDS_MeshElement* elem = *eIt;
9419 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9420 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9421 if ( firstIndex > -1 && secondIndex > -1 ) break;
9423 if ( firstIndex < 0 || secondIndex < 0 ) {
9424 // we can simply return until temporary faces created
9425 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9428 // -----------------------------------------------------------
9429 // 1a. Collect nodes of existing faces
9430 // and build set of face nodes in order to detect missing
9431 // faces corresponding to sides of volumes
9432 // -----------------------------------------------------------
9434 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9436 // loop on the given element of a side
9437 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9438 //const SMDS_MeshElement* elem = *eIt;
9439 const SMDS_MeshElement* elem = *eIt;
9440 if ( elem->GetType() == SMDSAbs_Face ) {
9441 faceSet->insert( elem );
9442 set <const SMDS_MeshNode*> faceNodeSet;
9443 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9444 while ( nodeIt->more() ) {
9445 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9446 nodeSet->insert( n );
9447 faceNodeSet.insert( n );
9449 setOfFaceNodeSet.insert( faceNodeSet );
9451 else if ( elem->GetType() == SMDSAbs_Volume )
9452 volSet->insert( elem );
9454 // ------------------------------------------------------------------------------
9455 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9456 // ------------------------------------------------------------------------------
9458 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9459 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9460 while ( fIt->more() ) { // loop on faces sharing a node
9461 const SMDS_MeshElement* f = fIt->next();
9462 if ( faceSet->find( f ) == faceSet->end() ) {
9463 // check if all nodes are in nodeSet and
9464 // complete setOfFaceNodeSet if they are
9465 set <const SMDS_MeshNode*> faceNodeSet;
9466 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9467 bool allInSet = true;
9468 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9469 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9470 if ( nodeSet->find( n ) == nodeSet->end() )
9473 faceNodeSet.insert( n );
9476 faceSet->insert( f );
9477 setOfFaceNodeSet.insert( faceNodeSet );
9483 // -------------------------------------------------------------------------
9484 // 1c. Create temporary faces representing sides of volumes if correspondent
9485 // face does not exist
9486 // -------------------------------------------------------------------------
9488 if ( !volSet->empty() ) {
9489 //int nodeSetSize = nodeSet->size();
9491 // loop on given volumes
9492 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9493 SMDS_VolumeTool vol (*vIt);
9494 // loop on volume faces: find free faces
9495 // --------------------------------------
9496 list<const SMDS_MeshElement* > freeFaceList;
9497 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9498 if ( !vol.IsFreeFace( iFace ))
9500 // check if there is already a face with same nodes in a face set
9501 const SMDS_MeshElement* aFreeFace = 0;
9502 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9503 int nbNodes = vol.NbFaceNodes( iFace );
9504 set <const SMDS_MeshNode*> faceNodeSet;
9505 vol.GetFaceNodes( iFace, faceNodeSet );
9506 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9508 // no such a face is given but it still can exist, check it
9509 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9510 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9513 // create a temporary face
9514 if ( nbNodes == 3 ) {
9515 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9516 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9518 else if ( nbNodes == 4 ) {
9519 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9520 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9523 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9524 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9525 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9528 tempFaceList.push_back( aFreeFace );
9532 freeFaceList.push_back( aFreeFace );
9534 } // loop on faces of a volume
9536 // choose one of several free faces of a volume
9537 // --------------------------------------------
9538 if ( freeFaceList.size() > 1 ) {
9539 // choose a face having max nb of nodes shared by other elems of a side
9540 int maxNbNodes = -1;
9541 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9542 while ( fIt != freeFaceList.end() ) { // loop on free faces
9543 int nbSharedNodes = 0;
9544 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9545 while ( nodeIt->more() ) { // loop on free face nodes
9546 const SMDS_MeshNode* n =
9547 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9548 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9549 while ( invElemIt->more() ) {
9550 const SMDS_MeshElement* e = invElemIt->next();
9551 nbSharedNodes += faceSet->count( e );
9552 nbSharedNodes += elemSet->count( e );
9555 if ( nbSharedNodes > maxNbNodes ) {
9556 maxNbNodes = nbSharedNodes;
9557 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9559 else if ( nbSharedNodes == maxNbNodes ) {
9563 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9566 if ( freeFaceList.size() > 1 )
9568 // could not choose one face, use another way
9569 // choose a face most close to the bary center of the opposite side
9570 gp_XYZ aBC( 0., 0., 0. );
9571 set <const SMDS_MeshNode*> addedNodes;
9572 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9573 eIt = elemSet2->begin();
9574 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9575 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9576 while ( nodeIt->more() ) { // loop on free face nodes
9577 const SMDS_MeshNode* n =
9578 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9579 if ( addedNodes.insert( n ).second )
9580 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9583 aBC /= addedNodes.size();
9584 double minDist = DBL_MAX;
9585 fIt = freeFaceList.begin();
9586 while ( fIt != freeFaceList.end() ) { // loop on free faces
9588 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9589 while ( nodeIt->more() ) { // loop on free face nodes
9590 const SMDS_MeshNode* n =
9591 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9592 gp_XYZ p( n->X(),n->Y(),n->Z() );
9593 dist += ( aBC - p ).SquareModulus();
9595 if ( dist < minDist ) {
9597 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9600 fIt = freeFaceList.erase( fIt++ );
9603 } // choose one of several free faces of a volume
9605 if ( freeFaceList.size() == 1 ) {
9606 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9607 faceSet->insert( aFreeFace );
9608 // complete a node set with nodes of a found free face
9609 // for ( iNode = 0; iNode < ; iNode++ )
9610 // nodeSet->insert( fNodes[ iNode ] );
9613 } // loop on volumes of a side
9615 // // complete a set of faces if new nodes in a nodeSet appeared
9616 // // ----------------------------------------------------------
9617 // if ( nodeSetSize != nodeSet->size() ) {
9618 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9619 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9620 // while ( fIt->more() ) { // loop on faces sharing a node
9621 // const SMDS_MeshElement* f = fIt->next();
9622 // if ( faceSet->find( f ) == faceSet->end() ) {
9623 // // check if all nodes are in nodeSet and
9624 // // complete setOfFaceNodeSet if they are
9625 // set <const SMDS_MeshNode*> faceNodeSet;
9626 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9627 // bool allInSet = true;
9628 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9629 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9630 // if ( nodeSet->find( n ) == nodeSet->end() )
9631 // allInSet = false;
9633 // faceNodeSet.insert( n );
9635 // if ( allInSet ) {
9636 // faceSet->insert( f );
9637 // setOfFaceNodeSet.insert( faceNodeSet );
9643 } // Create temporary faces, if there are volumes given
9646 if ( faceSet1.size() != faceSet2.size() ) {
9647 // delete temporary faces: they are in reverseElements of actual nodes
9648 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9649 // while ( tmpFaceIt->more() )
9650 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9651 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9652 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9653 // aMesh->RemoveElement(*tmpFaceIt);
9654 MESSAGE("Diff nb of faces");
9655 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9658 // ============================================================
9659 // 2. Find nodes to merge:
9660 // bind a node to remove to a node to put instead
9661 // ============================================================
9663 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9664 if ( theFirstNode1 != theFirstNode2 )
9665 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9666 if ( theSecondNode1 != theSecondNode2 )
9667 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9669 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9670 set< long > linkIdSet; // links to process
9671 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9673 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9674 list< NLink > linkList[2];
9675 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9676 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9677 // loop on links in linkList; find faces by links and append links
9678 // of the found faces to linkList
9679 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9680 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9682 NLink link[] = { *linkIt[0], *linkIt[1] };
9683 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9684 if ( !linkIdSet.count( linkID ) )
9687 // by links, find faces in the face sets,
9688 // and find indices of link nodes in the found faces;
9689 // in a face set, there is only one or no face sharing a link
9690 // ---------------------------------------------------------------
9692 const SMDS_MeshElement* face[] = { 0, 0 };
9693 vector<const SMDS_MeshNode*> fnodes[2];
9694 int iLinkNode[2][2];
9695 TIDSortedElemSet avoidSet;
9696 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9697 const SMDS_MeshNode* n1 = link[iSide].first;
9698 const SMDS_MeshNode* n2 = link[iSide].second;
9699 //cout << "Side " << iSide << " ";
9700 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9701 // find a face by two link nodes
9702 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9703 *faceSetPtr[ iSide ], avoidSet,
9704 &iLinkNode[iSide][0],
9705 &iLinkNode[iSide][1] );
9708 //cout << " F " << face[ iSide]->GetID() <<endl;
9709 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9710 // put face nodes to fnodes
9711 SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
9712 fnodes[ iSide ].assign( nIt, nEnd );
9713 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9717 // check similarity of elements of the sides
9718 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9719 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9720 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9721 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9724 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9726 break; // do not return because it's necessary to remove tmp faces
9729 // set nodes to merge
9730 // -------------------
9732 if ( face[0] && face[1] ) {
9733 const int nbNodes = face[0]->NbNodes();
9734 if ( nbNodes != face[1]->NbNodes() ) {
9735 MESSAGE("Diff nb of face nodes");
9736 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9737 break; // do not return because it s necessary to remove tmp faces
9739 bool reverse[] = { false, false }; // order of nodes in the link
9740 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9741 // analyse link orientation in faces
9742 int i1 = iLinkNode[ iSide ][ 0 ];
9743 int i2 = iLinkNode[ iSide ][ 1 ];
9744 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9746 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9747 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9748 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9750 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9751 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9754 // add other links of the faces to linkList
9755 // -----------------------------------------
9757 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9758 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9759 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9760 if ( !iter_isnew.second ) { // already in a set: no need to process
9761 linkIdSet.erase( iter_isnew.first );
9763 else // new in set == encountered for the first time: add
9765 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9766 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9767 linkList[0].push_back ( NLink( n1, n2 ));
9768 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9773 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9776 } // loop on link lists
9778 if ( aResult == SEW_OK &&
9779 ( //linkIt[0] != linkList[0].end() ||
9780 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9781 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9782 " " << (faceSetPtr[1]->empty()));
9783 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9786 // ====================================================================
9787 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9788 // ====================================================================
9790 // delete temporary faces
9791 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9792 // while ( tmpFaceIt->more() )
9793 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9794 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9795 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9796 aMesh->RemoveElement(*tmpFaceIt);
9798 if ( aResult != SEW_OK)
9801 list< int > nodeIDsToRemove;
9802 vector< const SMDS_MeshNode*> nodes;
9803 ElemFeatures elemType;
9805 // loop on nodes replacement map
9806 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9807 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9808 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
9810 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9811 nodeIDsToRemove.push_back( nToRemove->GetID() );
9812 // loop on elements sharing nToRemove
9813 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9814 while ( invElemIt->more() ) {
9815 const SMDS_MeshElement* e = invElemIt->next();
9816 // get a new suite of nodes: make replacement
9817 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9818 nodes.resize( nbNodes );
9819 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9820 while ( nIt->more() ) {
9821 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
9822 nnIt = nReplaceMap.find( n );
9823 if ( nnIt != nReplaceMap.end() ) {
9829 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9830 // elemIDsToRemove.push_back( e->GetID() );
9834 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
9835 aMesh->RemoveElement( e );
9837 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
9839 AddToSameGroups( newElem, e, aMesh );
9840 if ( int aShapeId = e->getshapeId() )
9841 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9847 Remove( nodeIDsToRemove, true );
9852 //================================================================================
9854 * \brief Find corresponding nodes in two sets of faces
9855 * \param theSide1 - first face set
9856 * \param theSide2 - second first face
9857 * \param theFirstNode1 - a boundary node of set 1
9858 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9859 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9860 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9861 * \param nReplaceMap - output map of corresponding nodes
9862 * \return bool - is a success or not
9864 //================================================================================
9867 //#define DEBUG_MATCHING_NODES
9870 SMESH_MeshEditor::Sew_Error
9871 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9872 set<const SMDS_MeshElement*>& theSide2,
9873 const SMDS_MeshNode* theFirstNode1,
9874 const SMDS_MeshNode* theFirstNode2,
9875 const SMDS_MeshNode* theSecondNode1,
9876 const SMDS_MeshNode* theSecondNode2,
9877 TNodeNodeMap & nReplaceMap)
9879 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9881 nReplaceMap.clear();
9882 //if ( theFirstNode1 != theFirstNode2 )
9883 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9884 //if ( theSecondNode1 != theSecondNode2 )
9885 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9887 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9888 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9890 list< NLink > linkList[2];
9891 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9892 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9894 // loop on links in linkList; find faces by links and append links
9895 // of the found faces to linkList
9896 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9897 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9898 NLink link[] = { *linkIt[0], *linkIt[1] };
9899 if ( linkSet.find( link[0] ) == linkSet.end() )
9902 // by links, find faces in the face sets,
9903 // and find indices of link nodes in the found faces;
9904 // in a face set, there is only one or no face sharing a link
9905 // ---------------------------------------------------------------
9907 const SMDS_MeshElement* face[] = { 0, 0 };
9908 list<const SMDS_MeshNode*> notLinkNodes[2];
9909 //bool reverse[] = { false, false }; // order of notLinkNodes
9911 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9913 const SMDS_MeshNode* n1 = link[iSide].first;
9914 const SMDS_MeshNode* n2 = link[iSide].second;
9915 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9916 set< const SMDS_MeshElement* > facesOfNode1;
9917 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9919 // during a loop of the first node, we find all faces around n1,
9920 // during a loop of the second node, we find one face sharing both n1 and n2
9921 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9922 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9923 while ( fIt->more() ) { // loop on faces sharing a node
9924 const SMDS_MeshElement* f = fIt->next();
9925 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9926 ! facesOfNode1.insert( f ).second ) // f encounters twice
9928 if ( face[ iSide ] ) {
9929 MESSAGE( "2 faces per link " );
9930 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9933 faceSet->erase( f );
9935 // get not link nodes
9936 int nbN = f->NbNodes();
9937 if ( f->IsQuadratic() )
9939 nbNodes[ iSide ] = nbN;
9940 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9941 int i1 = f->GetNodeIndex( n1 );
9942 int i2 = f->GetNodeIndex( n2 );
9943 int iEnd = nbN, iBeg = -1, iDelta = 1;
9944 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9946 std::swap( iEnd, iBeg ); iDelta = -1;
9951 if ( i == iEnd ) i = iBeg + iDelta;
9952 if ( i == i1 ) break;
9953 nodes.push_back ( f->GetNode( i ) );
9959 // check similarity of elements of the sides
9960 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9961 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9962 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9963 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9966 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9970 // set nodes to merge
9971 // -------------------
9973 if ( face[0] && face[1] ) {
9974 if ( nbNodes[0] != nbNodes[1] ) {
9975 MESSAGE("Diff nb of face nodes");
9976 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9978 #ifdef DEBUG_MATCHING_NODES
9979 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9980 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9981 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9983 int nbN = nbNodes[0];
9985 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9986 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9987 for ( int i = 0 ; i < nbN - 2; ++i ) {
9988 #ifdef DEBUG_MATCHING_NODES
9989 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9991 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9995 // add other links of the face 1 to linkList
9996 // -----------------------------------------
9998 const SMDS_MeshElement* f0 = face[0];
9999 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10000 for ( int i = 0; i < nbN; i++ )
10002 const SMDS_MeshNode* n2 = f0->GetNode( i );
10003 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10004 linkSet.insert( SMESH_TLink( n1, n2 ));
10005 if ( !iter_isnew.second ) { // already in a set: no need to process
10006 linkSet.erase( iter_isnew.first );
10008 else // new in set == encountered for the first time: add
10010 #ifdef DEBUG_MATCHING_NODES
10011 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10012 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10014 linkList[0].push_back ( NLink( n1, n2 ));
10015 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10020 } // loop on link lists
10025 namespace // automatically find theAffectedElems for DoubleNodes()
10027 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10029 //--------------------------------------------------------------------------------
10030 // Nodes shared by adjacent FissureBorder's.
10031 // 1 node if FissureBorder separates faces
10032 // 2 nodes if FissureBorder separates volumes
10035 const SMDS_MeshNode* _nodes[2];
10038 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10042 _nbNodes = bool( n1 ) + bool( n2 );
10043 if ( _nbNodes == 2 && n1 > n2 )
10044 std::swap( _nodes[0], _nodes[1] );
10046 bool operator<( const SubBorder& other ) const
10048 for ( int i = 0; i < _nbNodes; ++i )
10050 if ( _nodes[i] < other._nodes[i] ) return true;
10051 if ( _nodes[i] > other._nodes[i] ) return false;
10057 //--------------------------------------------------------------------------------
10058 // Map a SubBorder to all FissureBorder it bounds
10059 struct FissureBorder;
10060 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10061 typedef TBorderLinks::iterator TMappedSub;
10063 //--------------------------------------------------------------------------------
10065 * \brief Element border (volume facet or face edge) at a fissure
10067 struct FissureBorder
10069 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10070 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10072 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10073 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10075 FissureBorder( FissureBorder && from ) // move constructor
10077 std::swap( _nodes, from._nodes );
10078 std::swap( _sortedNodes, from._sortedNodes );
10079 _elems[0] = from._elems[0];
10080 _elems[1] = from._elems[1];
10083 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10084 std::vector< const SMDS_MeshElement* > & adjElems)
10085 : _nodes( elemToDuplicate->NbCornerNodes() )
10087 for ( size_t i = 0; i < _nodes.size(); ++i )
10088 _nodes[i] = elemToDuplicate->GetNode( i );
10090 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10091 findAdjacent( type, adjElems );
10094 FissureBorder( const SMDS_MeshNode** nodes,
10095 const size_t nbNodes,
10096 const SMDSAbs_ElementType adjElemsType,
10097 std::vector< const SMDS_MeshElement* > & adjElems)
10098 : _nodes( nodes, nodes + nbNodes )
10100 findAdjacent( adjElemsType, adjElems );
10103 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10104 std::vector< const SMDS_MeshElement* > & adjElems)
10106 _elems[0] = _elems[1] = 0;
10108 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10109 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10110 _elems[i] = adjElems[i];
10113 bool operator<( const FissureBorder& other ) const
10115 return GetSortedNodes() < other.GetSortedNodes();
10118 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10120 if ( _sortedNodes.empty() && !_nodes.empty() )
10122 FissureBorder* me = const_cast<FissureBorder*>( this );
10123 me->_sortedNodes = me->_nodes;
10124 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10126 return _sortedNodes;
10129 size_t NbSub() const
10131 return _nodes.size();
10134 SubBorder Sub(size_t i) const
10136 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10139 void AddSelfTo( TBorderLinks& borderLinks )
10141 _mappedSubs.resize( NbSub() );
10142 for ( size_t i = 0; i < NbSub(); ++i )
10144 TBorderLinks::iterator s2b =
10145 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10146 s2b->second.push_back( this );
10147 _mappedSubs[ i ] = s2b;
10156 const SMDS_MeshElement* GetMarkedElem() const
10158 if ( _nodes.empty() ) return 0; // cleared
10159 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10160 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10164 gp_XYZ GetNorm() const // normal to the border
10167 if ( _nodes.size() == 2 )
10169 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10170 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10172 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10175 gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10176 norm = bordDir ^ avgNorm;
10180 SMESH_NodeXYZ p0( _nodes[0] );
10181 SMESH_NodeXYZ p1( _nodes[1] );
10182 SMESH_NodeXYZ p2( _nodes[2] );
10183 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10185 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10191 void ChooseSide() // mark an _elem located at positive side of fissure
10193 _elems[0]->setIsMarked( true );
10194 gp_XYZ norm = GetNorm();
10195 double maxX = norm.Coord(1);
10196 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10197 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10200 _elems[0]->setIsMarked( false );
10201 _elems[1]->setIsMarked( true );
10205 }; // struct FissureBorder
10207 //--------------------------------------------------------------------------------
10209 * \brief Classifier of elements at fissure edge
10211 class FissureNormal
10213 std::vector< gp_XYZ > _normals;
10217 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10220 _normals.reserve(2);
10221 _normals.push_back( bord.GetNorm() );
10222 if ( _normals.size() == 2 )
10223 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10226 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10229 switch ( _normals.size() ) {
10232 isIn = !isOut( n, _normals[0], elem );
10237 bool in1 = !isOut( n, _normals[0], elem );
10238 bool in2 = !isOut( n, _normals[1], elem );
10239 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10246 //================================================================================
10248 * \brief Classify an element by a plane passing through a node
10250 //================================================================================
10252 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10254 SMESH_NodeXYZ p = n;
10256 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10258 SMESH_NodeXYZ pi = elem->GetNode( i );
10259 sumDot += norm * ( pi - p );
10261 return sumDot < -1e-100;
10264 //================================================================================
10266 * \brief Find FissureBorder's by nodes to duplicate
10268 //================================================================================
10270 void findFissureBorders( const TIDSortedElemSet& theNodes,
10271 std::vector< FissureBorder > & theFissureBorders )
10273 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10274 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10276 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10277 if ( n->NbInverseElements( elemType ) == 0 )
10279 elemType = SMDSAbs_Face;
10280 if ( n->NbInverseElements( elemType ) == 0 )
10283 // unmark elements touching the fissure
10284 for ( ; nIt != theNodes.end(); ++nIt )
10285 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10287 // loop on elements touching the fissure to get their borders belonging to the fissure
10288 std::set< FissureBorder > fissureBorders;
10289 std::vector< const SMDS_MeshElement* > adjElems;
10290 std::vector< const SMDS_MeshNode* > nodes;
10291 SMDS_VolumeTool volTool;
10292 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10294 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10295 while ( invIt->more() )
10297 const SMDS_MeshElement* eInv = invIt->next();
10298 if ( eInv->isMarked() ) continue;
10299 eInv->setIsMarked( true );
10301 if ( elemType == SMDSAbs_Volume )
10303 volTool.Set( eInv );
10304 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10305 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10307 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10308 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10310 bool allOnFissure = true;
10311 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10312 if (( allOnFissure = theNodes.count( nn[ iN ])))
10313 nodes.push_back( nn[ iN ]);
10314 if ( allOnFissure )
10315 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10316 elemType, adjElems )));
10319 else // elemType == SMDSAbs_Face
10321 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10322 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10323 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10325 nn[1] = eInv->GetNode( iN );
10326 onFissure1 = theNodes.count( nn[1] );
10327 if ( onFissure0 && onFissure1 )
10328 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10330 onFissure0 = onFissure1;
10336 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10337 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10338 for ( ; bord != fissureBorders.end(); ++bord )
10340 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10343 } // findFissureBorders()
10345 //================================================================================
10347 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10348 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10349 * \param [in] theNodesNot - nodes not to duplicate
10350 * \param [out] theAffectedElems - the found elements
10352 //================================================================================
10354 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10355 TIDSortedElemSet& theAffectedElems)
10357 if ( theElemsOrNodes.empty() ) return;
10359 // find FissureBorder's
10361 std::vector< FissureBorder > fissure;
10362 std::vector< const SMDS_MeshElement* > elemsByFacet;
10364 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10365 if ( (*elIt)->GetType() == SMDSAbs_Node )
10367 findFissureBorders( theElemsOrNodes, fissure );
10371 fissure.reserve( theElemsOrNodes.size() );
10372 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10373 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10375 if ( fissure.empty() )
10378 // fill borderLinks
10380 TBorderLinks borderLinks;
10382 for ( size_t i = 0; i < fissure.size(); ++i )
10384 fissure[i].AddSelfTo( borderLinks );
10387 // get theAffectedElems
10389 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10390 for ( size_t i = 0; i < fissure.size(); ++i )
10391 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10393 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10394 false, /*markElem=*/true );
10397 std::vector<const SMDS_MeshNode *> facetNodes;
10398 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10399 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10401 // choose a side of fissure
10402 fissure[0].ChooseSide();
10403 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10405 size_t nbCheckedBorders = 0;
10406 while ( nbCheckedBorders < fissure.size() )
10408 // find a FissureBorder to treat
10409 FissureBorder* bord = 0;
10410 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10411 if ( fissure[i].GetMarkedElem() )
10412 bord = & fissure[i];
10413 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10414 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10416 bord = & fissure[i];
10417 bord->ChooseSide();
10418 theAffectedElems.insert( bord->GetMarkedElem() );
10420 if ( !bord ) return;
10421 ++nbCheckedBorders;
10423 // treat FissureBorder's linked to bord
10424 fissureNodes.clear();
10425 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10426 for ( size_t i = 0; i < bord->NbSub(); ++i )
10428 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10429 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10430 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10431 const SubBorder& sb = l2b->first;
10432 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10434 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10436 for ( int j = 0; j < sb._nbNodes; ++j )
10437 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10441 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10442 // until an elem adjacent to a neighbour FissureBorder is found
10443 facetNodes.clear();
10444 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10445 facetNodes.resize( sb._nbNodes + 1 );
10449 // check if bordElem is adjacent to a neighbour FissureBorder
10450 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10452 FissureBorder* bord2 = linkedBorders[j];
10453 if ( bord2 == bord ) continue;
10454 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10457 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10462 // find the next bordElem
10463 const SMDS_MeshElement* nextBordElem = 0;
10464 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10466 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10467 if ( fissureNodes.count( n )) continue;
10469 facetNodes[ sb._nbNodes ] = n;
10470 elemsByFacet.clear();
10471 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10473 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10474 if ( elemsByFacet[ iE ] != bordElem &&
10475 !elemsByFacet[ iE ]->isMarked() )
10477 theAffectedElems.insert( elemsByFacet[ iE ]);
10478 elemsByFacet[ iE ]->setIsMarked( true );
10479 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10480 nextBordElem = elemsByFacet[ iE ];
10484 bordElem = nextBordElem;
10486 } // while ( bordElem )
10488 linkedBorders.clear(); // not to treat this link any more
10490 } // loop on SubBorder's of a FissureBorder
10494 } // loop on FissureBorder's
10497 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10499 // mark nodes of theAffectedElems
10500 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10502 // unmark nodes of the fissure
10503 elIt = theElemsOrNodes.begin();
10504 if ( (*elIt)->GetType() == SMDSAbs_Node )
10505 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10507 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10509 std::vector< gp_XYZ > normVec;
10511 // loop on nodes of the fissure, add elements having marked nodes
10512 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10514 const SMDS_MeshElement* e = (*elIt);
10515 if ( e->GetType() != SMDSAbs_Node )
10516 e->setIsMarked( true ); // avoid adding a fissure element
10518 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10520 const SMDS_MeshNode* n = e->GetNode( iN );
10521 if ( fissEdgeNodes2Norm.count( n ))
10524 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10525 while ( invIt->more() )
10527 const SMDS_MeshElement* eInv = invIt->next();
10528 if ( eInv->isMarked() ) continue;
10529 eInv->setIsMarked( true );
10531 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10532 while( nIt->more() )
10533 if ( nIt->next()->isMarked())
10535 theAffectedElems.insert( eInv );
10536 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10537 n->setIsMarked( false );
10544 // add elements on the fissure edge
10545 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10546 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10548 const SMDS_MeshNode* edgeNode = n2N->first;
10549 const FissureNormal & normals = n2N->second;
10551 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10552 while ( invIt->more() )
10554 const SMDS_MeshElement* eInv = invIt->next();
10555 if ( eInv->isMarked() ) continue;
10556 eInv->setIsMarked( true );
10558 // classify eInv using normals
10559 bool toAdd = normals.IsIn( edgeNode, eInv );
10560 if ( toAdd ) // check if all nodes lie on the fissure edge
10562 bool notOnEdge = false;
10563 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
10564 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10569 theAffectedElems.insert( eInv );
10575 } // findAffectedElems()
10578 //================================================================================
10580 * \brief Create elements equal (on same nodes) to given ones
10581 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10582 * elements of the uppest dimension are duplicated.
10584 //================================================================================
10586 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10588 ClearLastCreated();
10589 SMESHDS_Mesh* mesh = GetMeshDS();
10591 // get an element type and an iterator over elements
10593 SMDSAbs_ElementType type = SMDSAbs_All;
10594 SMDS_ElemIteratorPtr elemIt;
10595 if ( theElements.empty() )
10597 if ( mesh->NbNodes() == 0 )
10599 // get most complex type
10600 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10601 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10602 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10604 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10605 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10608 elemIt = mesh->elementsIterator( type );
10614 //type = (*theElements.begin())->GetType();
10615 elemIt = SMESHUtils::elemSetIterator( theElements );
10618 // un-mark all elements to avoid duplicating just created elements
10619 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10621 // duplicate elements
10623 ElemFeatures elemType;
10625 vector< const SMDS_MeshNode* > nodes;
10626 while ( elemIt->more() )
10628 const SMDS_MeshElement* elem = elemIt->next();
10629 if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10630 ( elem->isMarked() ))
10633 elemType.Init( elem, /*basicOnly=*/false );
10634 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10636 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10637 newElem->setIsMarked( true );
10641 //================================================================================
10643 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10644 \param theElems - the list of elements (edges or faces) to be replicated
10645 The nodes for duplication could be found from these elements
10646 \param theNodesNot - list of nodes to NOT replicate
10647 \param theAffectedElems - the list of elements (cells and edges) to which the
10648 replicated nodes should be associated to.
10649 \return TRUE if operation has been completed successfully, FALSE otherwise
10651 //================================================================================
10653 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10654 const TIDSortedElemSet& theNodesNot,
10655 const TIDSortedElemSet& theAffectedElems )
10657 ClearLastCreated();
10659 if ( theElems.size() == 0 )
10662 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10667 TNodeNodeMap anOldNodeToNewNode;
10668 // duplicate elements and nodes
10669 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10670 // replce nodes by duplications
10671 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10675 //================================================================================
10677 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10678 \param theMeshDS - mesh instance
10679 \param theElems - the elements replicated or modified (nodes should be changed)
10680 \param theNodesNot - nodes to NOT replicate
10681 \param theNodeNodeMap - relation of old node to new created node
10682 \param theIsDoubleElem - flag os to replicate element or modify
10683 \return TRUE if operation has been completed successfully, FALSE otherwise
10685 //================================================================================
10687 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10688 const TIDSortedElemSet& theElems,
10689 const TIDSortedElemSet& theNodesNot,
10690 TNodeNodeMap& theNodeNodeMap,
10691 const bool theIsDoubleElem )
10693 // iterate through element and duplicate them (by nodes duplication)
10695 std::vector<const SMDS_MeshNode*> newNodes;
10696 ElemFeatures elemType;
10698 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10699 for ( ; elemItr != theElems.end(); ++elemItr )
10701 const SMDS_MeshElement* anElem = *elemItr;
10705 // duplicate nodes to duplicate element
10706 bool isDuplicate = false;
10707 newNodes.resize( anElem->NbNodes() );
10708 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10710 while ( anIter->more() )
10712 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10713 const SMDS_MeshNode* aNewNode = aCurrNode;
10714 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10715 if ( n2n != theNodeNodeMap.end() )
10717 aNewNode = n2n->second;
10719 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10722 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10723 copyPosition( aCurrNode, aNewNode );
10724 theNodeNodeMap[ aCurrNode ] = aNewNode;
10725 myLastCreatedNodes.push_back( aNewNode );
10727 isDuplicate |= (aCurrNode != aNewNode);
10728 newNodes[ ind++ ] = aNewNode;
10730 if ( !isDuplicate )
10733 if ( theIsDoubleElem )
10734 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10736 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10743 //================================================================================
10745 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10746 \param theNodes - identifiers of nodes to be doubled
10747 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10748 nodes. If list of element identifiers is empty then nodes are doubled but
10749 they not assigned to elements
10750 \return TRUE if operation has been completed successfully, FALSE otherwise
10752 //================================================================================
10754 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10755 const std::list< int >& theListOfModifiedElems )
10757 ClearLastCreated();
10759 if ( theListOfNodes.size() == 0 )
10762 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10766 // iterate through nodes and duplicate them
10768 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10770 std::list< int >::const_iterator aNodeIter;
10771 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10773 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
10779 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10782 copyPosition( aNode, aNewNode );
10783 anOldNodeToNewNode[ aNode ] = aNewNode;
10784 myLastCreatedNodes.push_back( aNewNode );
10788 // Change nodes of elements
10790 std::vector<const SMDS_MeshNode*> aNodeArr;
10792 std::list< int >::const_iterator anElemIter;
10793 for ( anElemIter = theListOfModifiedElems.begin();
10794 anElemIter != theListOfModifiedElems.end();
10797 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
10801 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
10802 for( size_t i = 0; i < aNodeArr.size(); ++i )
10804 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
10805 anOldNodeToNewNode.find( aNodeArr[ i ]);
10806 if ( n2n != anOldNodeToNewNode.end() )
10807 aNodeArr[ i ] = n2n->second;
10809 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
10817 //================================================================================
10819 \brief Check if element located inside shape
10820 \return TRUE if IN or ON shape, FALSE otherwise
10822 //================================================================================
10824 template<class Classifier>
10825 bool isInside(const SMDS_MeshElement* theElem,
10826 Classifier& theClassifier,
10827 const double theTol)
10829 gp_XYZ centerXYZ (0, 0, 0);
10830 for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
10831 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
10833 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10834 theClassifier.Perform(aPnt, theTol);
10835 TopAbs_State aState = theClassifier.State();
10836 return (aState == TopAbs_IN || aState == TopAbs_ON );
10839 //================================================================================
10841 * \brief Classifier of the 3D point on the TopoDS_Face
10842 * with interaface suitable for isInside()
10844 //================================================================================
10846 struct _FaceClassifier
10848 Extrema_ExtPS _extremum;
10849 BRepAdaptor_Surface _surface;
10850 TopAbs_State _state;
10852 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10854 _extremum.Initialize( _surface,
10855 _surface.FirstUParameter(), _surface.LastUParameter(),
10856 _surface.FirstVParameter(), _surface.LastVParameter(),
10857 _surface.Tolerance(), _surface.Tolerance() );
10859 void Perform(const gp_Pnt& aPnt, double theTol)
10862 _state = TopAbs_OUT;
10863 _extremum.Perform(aPnt);
10864 if ( _extremum.IsDone() )
10865 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10866 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10868 TopAbs_State State() const
10875 //================================================================================
10877 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10878 This method is the first step of DoubleNodeElemGroupsInRegion.
10879 \param theElems - list of groups of elements (edges or faces) to be replicated
10880 \param theNodesNot - list of groups of nodes not to replicated
10881 \param theShape - shape to detect affected elements (element which geometric center
10882 located on or inside shape). If the shape is null, detection is done on faces orientations
10883 (select elements with a gravity center on the side given by faces normals).
10884 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10885 The replicated nodes should be associated to affected elements.
10887 \sa DoubleNodeElemGroupsInRegion()
10889 //================================================================================
10891 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10892 const TIDSortedElemSet& theNodesNot,
10893 const TopoDS_Shape& theShape,
10894 TIDSortedElemSet& theAffectedElems)
10896 if ( theShape.IsNull() )
10898 findAffectedElems( theElems, theAffectedElems );
10902 const double aTol = Precision::Confusion();
10903 std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
10904 std::unique_ptr<_FaceClassifier> aFaceClassifier;
10905 if ( theShape.ShapeType() == TopAbs_SOLID )
10907 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10908 bsc3d->PerformInfinitePoint(aTol);
10910 else if (theShape.ShapeType() == TopAbs_FACE )
10912 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10915 // iterates on indicated elements and get elements by back references from their nodes
10916 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10917 for ( ; elemItr != theElems.end(); ++elemItr )
10919 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10920 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10921 while ( nodeItr->more() )
10923 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10924 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10926 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10927 while ( backElemItr->more() )
10929 const SMDS_MeshElement* curElem = backElemItr->next();
10930 if ( curElem && theElems.find(curElem) == theElems.end() &&
10932 isInside( curElem, *bsc3d, aTol ) :
10933 isInside( curElem, *aFaceClassifier, aTol )))
10934 theAffectedElems.insert( curElem );
10942 //================================================================================
10944 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10945 \param theElems - group of of elements (edges or faces) to be replicated
10946 \param theNodesNot - group of nodes not to replicate
10947 \param theShape - shape to detect affected elements (element which geometric center
10948 located on or inside shape).
10949 The replicated nodes should be associated to affected elements.
10950 \return TRUE if operation has been completed successfully, FALSE otherwise
10952 //================================================================================
10954 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10955 const TIDSortedElemSet& theNodesNot,
10956 const TopoDS_Shape& theShape )
10958 if ( theShape.IsNull() )
10961 const double aTol = Precision::Confusion();
10962 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10963 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
10964 if ( theShape.ShapeType() == TopAbs_SOLID )
10966 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10967 bsc3d->PerformInfinitePoint(aTol);
10969 else if (theShape.ShapeType() == TopAbs_FACE )
10971 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
10974 // iterates on indicated elements and get elements by back references from their nodes
10975 TIDSortedElemSet anAffected;
10976 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10977 for ( ; elemItr != theElems.end(); ++elemItr )
10979 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10983 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10984 while ( nodeItr->more() )
10986 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10987 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10989 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10990 while ( backElemItr->more() )
10992 const SMDS_MeshElement* curElem = backElemItr->next();
10993 if ( curElem && theElems.find(curElem) == theElems.end() &&
10995 isInside( curElem, *bsc3d, aTol ) :
10996 isInside( curElem, *aFaceClassifier, aTol )))
10997 anAffected.insert( curElem );
11001 return DoubleNodes( theElems, theNodesNot, anAffected );
11005 * \brief compute an oriented angle between two planes defined by four points.
11006 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11007 * @param p0 base of the rotation axe
11008 * @param p1 extremity of the rotation axe
11009 * @param g1 belongs to the first plane
11010 * @param g2 belongs to the second plane
11012 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11014 gp_Vec vref(p0, p1);
11017 gp_Vec n1 = vref.Crossed(v1);
11018 gp_Vec n2 = vref.Crossed(v2);
11020 return n2.AngleWithRef(n1, vref);
11022 catch ( Standard_Failure ) {
11024 return Max( v1.Magnitude(), v2.Magnitude() );
11028 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11029 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11030 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11031 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11032 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11033 * 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.
11034 * 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.
11035 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11036 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11037 * \param theElems - list of groups of volumes, where a group of volume is a set of
11038 * SMDS_MeshElements sorted by Id.
11039 * \param createJointElems - if TRUE, create the elements
11040 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11041 * the boundary between \a theDomains and the rest mesh
11042 * \return TRUE if operation has been completed successfully, FALSE otherwise
11044 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11045 bool createJointElems,
11046 bool onAllBoundaries)
11048 // MESSAGE("----------------------------------------------");
11049 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11050 // MESSAGE("----------------------------------------------");
11052 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11053 meshDS->BuildDownWardConnectivity(true);
11055 SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11057 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11058 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11059 // build the list of nodes shared by 2 or more domains, with their domain indexes
11061 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11062 std::map<int,int>celldom; // cell vtkId --> domain
11063 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11064 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11065 faceDomains.clear();
11067 cellDomains.clear();
11068 nodeDomains.clear();
11069 std::map<int,int> emptyMap;
11070 std::set<int> emptySet;
11073 //MESSAGE(".. Number of domains :"<<theElems.size());
11075 TIDSortedElemSet theRestDomElems;
11076 const int iRestDom = -1;
11077 const int idom0 = onAllBoundaries ? iRestDom : 0;
11078 const int nbDomains = theElems.size();
11080 // Check if the domains do not share an element
11081 for (int idom = 0; idom < nbDomains-1; idom++)
11083 // MESSAGE("... Check of domain #" << idom);
11084 const TIDSortedElemSet& domain = theElems[idom];
11085 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11086 for (; elemItr != domain.end(); ++elemItr)
11088 const SMDS_MeshElement* anElem = *elemItr;
11089 int idombisdeb = idom + 1 ;
11090 // check if the element belongs to a domain further in the list
11091 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11093 const TIDSortedElemSet& domainbis = theElems[idombis];
11094 if ( domainbis.count( anElem ))
11096 MESSAGE(".... Domain #" << idom);
11097 MESSAGE(".... Domain #" << idombis);
11098 throw SALOME_Exception("The domains are not disjoint.");
11105 for (int idom = 0; idom < nbDomains; idom++)
11108 // --- build a map (face to duplicate --> volume to modify)
11109 // with all the faces shared by 2 domains (group of elements)
11110 // and corresponding volume of this domain, for each shared face.
11111 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11113 //MESSAGE("... Neighbors of domain #" << idom);
11114 const TIDSortedElemSet& domain = theElems[idom];
11115 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11116 for (; elemItr != domain.end(); ++elemItr)
11118 const SMDS_MeshElement* anElem = *elemItr;
11121 int vtkId = anElem->GetVtkID();
11122 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11123 int neighborsVtkIds[NBMAXNEIGHBORS];
11124 int downIds[NBMAXNEIGHBORS];
11125 unsigned char downTypes[NBMAXNEIGHBORS];
11126 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11127 for (int n = 0; n < nbNeighbors; n++)
11129 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11130 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11131 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11134 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11136 // MESSAGE("Domain " << idombis);
11137 const TIDSortedElemSet& domainbis = theElems[idombis];
11138 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11140 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11142 DownIdType face(downIds[n], downTypes[n]);
11143 if (!faceDomains[face].count(idom))
11145 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11146 celldom[vtkId] = idom;
11147 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11151 theRestDomElems.insert( elem );
11152 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11153 celldom[neighborsVtkIds[n]] = iRestDom;
11161 //MESSAGE("Number of shared faces " << faceDomains.size());
11162 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11164 // --- explore the shared faces domain by domain,
11165 // explore the nodes of the face and see if they belong to a cell in the domain,
11166 // which has only a node or an edge on the border (not a shared face)
11168 for (int idomain = idom0; idomain < nbDomains; idomain++)
11170 //MESSAGE("Domain " << idomain);
11171 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11172 itface = faceDomains.begin();
11173 for (; itface != faceDomains.end(); ++itface)
11175 const std::map<int, int>& domvol = itface->second;
11176 if (!domvol.count(idomain))
11178 DownIdType face = itface->first;
11179 //MESSAGE(" --- face " << face.cellId);
11180 std::set<int> oldNodes;
11182 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11183 std::set<int>::iterator itn = oldNodes.begin();
11184 for (; itn != oldNodes.end(); ++itn)
11187 //MESSAGE(" node " << oldId);
11188 vtkCellLinks::Link l = (static_cast <vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11189 for (int i=0; i<l.ncells; i++)
11191 int vtkId = l.cells[i];
11192 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11193 if (!domain.count(anElem))
11195 int vtkType = grid->GetCellType(vtkId);
11196 int downId = grid->CellIdToDownId(vtkId);
11199 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11200 continue; // not OK at this stage of the algorithm:
11201 //no cells created after BuildDownWardConnectivity
11203 DownIdType aCell(downId, vtkType);
11204 cellDomains[aCell][idomain] = vtkId;
11205 celldom[vtkId] = idomain;
11206 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11212 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11213 // for each shared face, get the nodes
11214 // for each node, for each domain of the face, create a clone of the node
11216 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11217 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11218 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11220 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11221 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11222 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11224 //MESSAGE(".. Duplication of the nodes");
11225 for (int idomain = idom0; idomain < nbDomains; idomain++)
11227 itface = faceDomains.begin();
11228 for (; itface != faceDomains.end(); ++itface)
11230 const std::map<int, int>& domvol = itface->second;
11231 if (!domvol.count(idomain))
11233 DownIdType face = itface->first;
11234 //MESSAGE(" --- face " << face.cellId);
11235 std::set<int> oldNodes;
11237 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11238 std::set<int>::iterator itn = oldNodes.begin();
11239 for (; itn != oldNodes.end(); ++itn)
11242 if (nodeDomains[oldId].empty())
11244 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11245 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11247 std::map<int, int>::const_iterator itdom = domvol.begin();
11248 for (; itdom != domvol.end(); ++itdom)
11250 int idom = itdom->first;
11251 //MESSAGE(" domain " << idom);
11252 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11254 if (nodeDomains[oldId].size() >= 2) // a multiple node
11256 vector<int> orderedDoms;
11257 //MESSAGE("multiple node " << oldId);
11258 if (mutipleNodes.count(oldId))
11259 orderedDoms = mutipleNodes[oldId];
11262 map<int,int>::iterator it = nodeDomains[oldId].begin();
11263 for (; it != nodeDomains[oldId].end(); ++it)
11264 orderedDoms.push_back(it->first);
11266 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11267 //stringstream txt;
11268 //for (int i=0; i<orderedDoms.size(); i++)
11269 // txt << orderedDoms[i] << " ";
11270 //MESSAGE("orderedDoms " << txt.str());
11271 mutipleNodes[oldId] = orderedDoms;
11273 double *coords = grid->GetPoint(oldId);
11274 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11275 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11276 int newId = newNode->GetVtkID();
11277 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11278 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11285 //MESSAGE(".. Creation of elements");
11286 for (int idomain = idom0; idomain < nbDomains; idomain++)
11288 itface = faceDomains.begin();
11289 for (; itface != faceDomains.end(); ++itface)
11291 std::map<int, int> domvol = itface->second;
11292 if (!domvol.count(idomain))
11294 DownIdType face = itface->first;
11295 //MESSAGE(" --- face " << face.cellId);
11296 std::set<int> oldNodes;
11298 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11299 int nbMultipleNodes = 0;
11300 std::set<int>::iterator itn = oldNodes.begin();
11301 for (; itn != oldNodes.end(); ++itn)
11304 if (mutipleNodes.count(oldId))
11307 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11309 //MESSAGE("multiple Nodes detected on a shared face");
11310 int downId = itface->first.cellId;
11311 unsigned char cellType = itface->first.cellType;
11312 // --- shared edge or shared face ?
11313 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11316 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11317 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11318 if (mutipleNodes.count(nodes[i]))
11319 if (!mutipleNodesToFace.count(nodes[i]))
11320 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11322 else // shared face (between two volumes)
11324 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11325 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11326 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11327 for (int ie =0; ie < nbEdges; ie++)
11330 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11331 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11333 vector<int> vn0 = mutipleNodes[nodes[0]];
11334 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11336 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11337 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11338 if ( vn0[i0] == vn1[i1] )
11339 doms.push_back( vn0[ i0 ]);
11340 if ( doms.size() > 2 )
11342 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11343 double *coords = grid->GetPoint(nodes[0]);
11344 gp_Pnt p0(coords[0], coords[1], coords[2]);
11345 coords = grid->GetPoint(nodes[nbNodes - 1]);
11346 gp_Pnt p1(coords[0], coords[1], coords[2]);
11348 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11349 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11350 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11351 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11352 for ( size_t id = 0; id < doms.size(); id++ )
11354 int idom = doms[id];
11355 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11356 for ( int ivol = 0; ivol < nbvol; ivol++ )
11358 int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11359 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11360 if (domain.count(elem))
11362 const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11363 domvol[idom] = (SMDS_MeshVolume*) svol;
11364 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11365 double values[3] = { 0,0,0 };
11366 vtkIdType npts = 0;
11367 vtkIdType const *pts(nullptr);
11368 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11369 for ( vtkIdType i = 0; i < npts; ++i )
11371 double *coords = grid->GetPoint( pts[i] );
11372 for ( int j = 0; j < 3; ++j )
11373 values[j] += coords[j] / npts;
11377 gref.SetCoord( values[0], values[1], values[2] );
11378 angleDom[idom] = 0;
11382 gp_Pnt g( values[0], values[1], values[2] );
11383 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11384 //MESSAGE(" angle=" << angleDom[idom]);
11390 map<double, int> sortedDom; // sort domains by angle
11391 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11392 sortedDom[ia->second] = ia->first;
11393 vector<int> vnodes;
11395 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11397 vdom.push_back(ib->second);
11398 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11400 for (int ino = 0; ino < nbNodes; ino++)
11401 vnodes.push_back(nodes[ino]);
11402 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11411 // --- iterate on shared faces (volumes to modify, face to extrude)
11412 // get node id's of the face (id SMDS = id VTK)
11413 // create flat element with old and new nodes if requested
11415 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11416 // (domain1 X domain2) = domain1 + MAXINT*domain2
11418 std::map<int, std::map<long,int> > nodeQuadDomains;
11419 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11421 //MESSAGE(".. Creation of elements: simple junction");
11422 if (createJointElems)
11424 string joints2DName = "joints2D";
11425 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11426 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11427 string joints3DName = "joints3D";
11428 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11429 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11431 itface = faceDomains.begin();
11432 for (; itface != faceDomains.end(); ++itface)
11434 DownIdType face = itface->first;
11435 std::set<int> oldNodes;
11436 std::set<int>::iterator itn;
11438 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11440 std::map<int, int> domvol = itface->second;
11441 std::map<int, int>::iterator itdom = domvol.begin();
11442 int dom1 = itdom->first;
11443 int vtkVolId = itdom->second;
11445 int dom2 = itdom->first;
11446 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11448 stringstream grpname;
11451 grpname << dom1 << "_" << dom2;
11453 grpname << dom2 << "_" << dom1;
11454 string namegrp = grpname.str();
11455 if (!mapOfJunctionGroups.count(namegrp))
11456 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11457 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11459 sgrp->Add(vol->GetID());
11460 if (vol->GetType() == SMDSAbs_Volume)
11461 joints3DGrp->Add(vol->GetID());
11462 else if (vol->GetType() == SMDSAbs_Face)
11463 joints2DGrp->Add(vol->GetID());
11467 // --- create volumes on multiple domain intersection if requested
11468 // iterate on mutipleNodesToFace
11469 // iterate on edgesMultiDomains
11471 //MESSAGE(".. Creation of elements: multiple junction");
11472 if (createJointElems)
11474 // --- iterate on mutipleNodesToFace
11476 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11477 for (; itn != mutipleNodesToFace.end(); ++itn)
11479 int node = itn->first;
11480 vector<int> orderDom = itn->second;
11481 vector<vtkIdType> orderedNodes;
11482 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11483 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11484 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11486 stringstream grpname;
11488 grpname << 0 << "_" << 0;
11489 string namegrp = grpname.str();
11490 if (!mapOfJunctionGroups.count(namegrp))
11491 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11492 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11494 sgrp->Add(face->GetID());
11497 // --- iterate on edgesMultiDomains
11499 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11500 for (; ite != edgesMultiDomains.end(); ++ite)
11502 vector<int> nodes = ite->first;
11503 vector<int> orderDom = ite->second;
11504 vector<vtkIdType> orderedNodes;
11505 if (nodes.size() == 2)
11507 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11508 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11509 if ( orderDom.size() == 3 )
11510 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11511 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11513 for (int idom = orderDom.size()-1; idom >=0; idom--)
11514 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11515 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11517 string namegrp = "jointsMultiples";
11518 if (!mapOfJunctionGroups.count(namegrp))
11519 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11520 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11522 sgrp->Add(vol->GetID());
11526 //INFOS("Quadratic multiple joints not implemented");
11527 // TODO quadratic nodes
11532 // --- list the explicit faces and edges of the mesh that need to be modified,
11533 // i.e. faces and edges built with one or more duplicated nodes.
11534 // associate these faces or edges to their corresponding domain.
11535 // only the first domain found is kept when a face or edge is shared
11537 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11538 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11539 faceOrEdgeDom.clear();
11542 //MESSAGE(".. Modification of elements");
11543 for (int idomain = idom0; idomain < nbDomains; idomain++)
11545 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11546 for (; itnod != nodeDomains.end(); ++itnod)
11548 int oldId = itnod->first;
11549 //MESSAGE(" node " << oldId);
11550 vtkCellLinks::Link l = (static_cast< vtkCellLinks *>(grid->GetCellLinks()))->GetLink(oldId);
11551 for (int i = 0; i < l.ncells; i++)
11553 int vtkId = l.cells[i];
11554 int vtkType = grid->GetCellType(vtkId);
11555 int downId = grid->CellIdToDownId(vtkId);
11557 continue; // new cells: not to be modified
11558 DownIdType aCell(downId, vtkType);
11559 int volParents[1000];
11560 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11561 for (int j = 0; j < nbvol; j++)
11562 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11563 if (!feDom.count(vtkId))
11565 feDom[vtkId] = idomain;
11566 faceOrEdgeDom[aCell] = emptyMap;
11567 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11568 //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11569 // << " type " << vtkType << " downId " << downId);
11575 // --- iterate on shared faces (volumes to modify, face to extrude)
11576 // get node id's of the face
11577 // replace old nodes by new nodes in volumes, and update inverse connectivity
11579 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11580 for (int m=0; m<3; m++)
11582 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11583 itface = (*amap).begin();
11584 for (; itface != (*amap).end(); ++itface)
11586 DownIdType face = itface->first;
11587 std::set<int> oldNodes;
11588 std::set<int>::iterator itn;
11590 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11591 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11592 std::map<int, int> localClonedNodeIds;
11594 std::map<int, int> domvol = itface->second;
11595 std::map<int, int>::iterator itdom = domvol.begin();
11596 for (; itdom != domvol.end(); ++itdom)
11598 int idom = itdom->first;
11599 int vtkVolId = itdom->second;
11600 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11601 localClonedNodeIds.clear();
11602 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11605 if (nodeDomains[oldId].count(idom))
11607 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11608 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11611 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11616 // Remove empty groups (issue 0022812)
11617 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11618 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11620 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11621 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11624 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11625 grid->DeleteLinks();
11633 * \brief Double nodes on some external faces and create flat elements.
11634 * Flat elements are mainly used by some types of mechanic calculations.
11636 * Each group of the list must be constituted of faces.
11637 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11638 * @param theElems - list of groups of faces, where a group of faces is a set of
11639 * SMDS_MeshElements sorted by Id.
11640 * @return TRUE if operation has been completed successfully, FALSE otherwise
11642 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11644 // MESSAGE("-------------------------------------------------");
11645 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11646 // MESSAGE("-------------------------------------------------");
11648 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11650 // --- For each group of faces
11651 // duplicate the nodes, create a flat element based on the face
11652 // replace the nodes of the faces by their clones
11654 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11655 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11656 clonedNodes.clear();
11657 intermediateNodes.clear();
11658 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11659 mapOfJunctionGroups.clear();
11661 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11663 const TIDSortedElemSet& domain = theElems[idom];
11664 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11665 for ( ; elemItr != domain.end(); ++elemItr )
11667 const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
11670 // MESSAGE("aFace=" << aFace->GetID());
11671 bool isQuad = aFace->IsQuadratic();
11672 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11674 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11676 SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
11677 while (nodeIt->more())
11679 const SMDS_MeshNode* node = nodeIt->next();
11680 bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
11682 ln2.push_back(node);
11684 ln0.push_back(node);
11686 const SMDS_MeshNode* clone = 0;
11687 if (!clonedNodes.count(node))
11689 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11690 copyPosition( node, clone );
11691 clonedNodes[node] = clone;
11694 clone = clonedNodes[node];
11697 ln3.push_back(clone);
11699 ln1.push_back(clone);
11701 const SMDS_MeshNode* inter = 0;
11702 if (isQuad && (!isMedium))
11704 if (!intermediateNodes.count(node))
11706 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11707 copyPosition( node, inter );
11708 intermediateNodes[node] = inter;
11711 inter = intermediateNodes[node];
11712 ln4.push_back(inter);
11716 // --- extrude the face
11718 vector<const SMDS_MeshNode*> ln;
11719 SMDS_MeshVolume* vol = 0;
11720 vtkIdType aType = aFace->GetVtkType();
11724 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11725 // MESSAGE("vol prism " << vol->GetID());
11726 ln.push_back(ln1[0]);
11727 ln.push_back(ln1[1]);
11728 ln.push_back(ln1[2]);
11731 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11732 // MESSAGE("vol hexa " << vol->GetID());
11733 ln.push_back(ln1[0]);
11734 ln.push_back(ln1[1]);
11735 ln.push_back(ln1[2]);
11736 ln.push_back(ln1[3]);
11738 case VTK_QUADRATIC_TRIANGLE:
11739 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11740 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11741 // MESSAGE("vol quad prism " << vol->GetID());
11742 ln.push_back(ln1[0]);
11743 ln.push_back(ln1[1]);
11744 ln.push_back(ln1[2]);
11745 ln.push_back(ln3[0]);
11746 ln.push_back(ln3[1]);
11747 ln.push_back(ln3[2]);
11749 case VTK_QUADRATIC_QUAD:
11750 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11751 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11752 // ln4[0], ln4[1], ln4[2], ln4[3]);
11753 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11754 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11755 ln4[0], ln4[1], ln4[2], ln4[3]);
11756 // MESSAGE("vol quad hexa " << vol->GetID());
11757 ln.push_back(ln1[0]);
11758 ln.push_back(ln1[1]);
11759 ln.push_back(ln1[2]);
11760 ln.push_back(ln1[3]);
11761 ln.push_back(ln3[0]);
11762 ln.push_back(ln3[1]);
11763 ln.push_back(ln3[2]);
11764 ln.push_back(ln3[3]);
11774 stringstream grpname;
11777 string namegrp = grpname.str();
11778 if (!mapOfJunctionGroups.count(namegrp))
11779 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11780 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11782 sgrp->Add(vol->GetID());
11785 // --- modify the face
11787 const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
11794 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11795 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11796 * groups of faces to remove inside the object, (idem edges).
11797 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11799 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11800 const TopoDS_Shape& theShape,
11801 SMESH_NodeSearcher* theNodeSearcher,
11802 const char* groupName,
11803 std::vector<double>& nodesCoords,
11804 std::vector<std::vector<int> >& listOfListOfNodes)
11806 // MESSAGE("--------------------------------");
11807 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11808 // MESSAGE("--------------------------------");
11810 // --- zone of volumes to remove is given :
11811 // 1 either by a geom shape (one or more vertices) and a radius,
11812 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11813 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11814 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11815 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11816 // defined by it's name.
11818 SMESHDS_GroupBase* groupDS = 0;
11819 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11820 while ( groupIt->more() )
11823 SMESH_Group * group = groupIt->next();
11824 if ( !group ) continue;
11825 groupDS = group->GetGroupDS();
11826 if ( !groupDS || groupDS->IsEmpty() ) continue;
11827 std::string grpName = group->GetName();
11828 //MESSAGE("grpName=" << grpName);
11829 if (grpName == groupName)
11835 bool isNodeGroup = false;
11836 bool isNodeCoords = false;
11839 if (groupDS->GetType() != SMDSAbs_Node)
11841 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11844 if (nodesCoords.size() > 0)
11845 isNodeCoords = true; // a list o nodes given by their coordinates
11846 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11848 // --- define groups to build
11850 // --- group of SMDS volumes
11851 string grpvName = groupName;
11852 grpvName += "_vol";
11853 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
11856 MESSAGE("group not created " << grpvName);
11859 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11861 // --- group of SMDS faces on the skin
11862 string grpsName = groupName;
11863 grpsName += "_skin";
11864 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
11867 MESSAGE("group not created " << grpsName);
11870 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11872 // --- group of SMDS faces internal (several shapes)
11873 string grpiName = groupName;
11874 grpiName += "_internalFaces";
11875 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
11878 MESSAGE("group not created " << grpiName);
11881 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11883 // --- group of SMDS faces internal (several shapes)
11884 string grpeiName = groupName;
11885 grpeiName += "_internalEdges";
11886 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
11889 MESSAGE("group not created " << grpeiName);
11892 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11894 // --- build downward connectivity
11896 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11897 meshDS->BuildDownWardConnectivity(true);
11898 SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
11900 // --- set of volumes detected inside
11902 std::set<int> setOfInsideVol;
11903 std::set<int> setOfVolToCheck;
11905 std::vector<gp_Pnt> gpnts;
11908 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11910 //MESSAGE("group of nodes provided");
11911 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11912 while ( elemIt->more() )
11914 const SMDS_MeshElement* elem = elemIt->next();
11917 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11920 SMDS_MeshElement* vol = 0;
11921 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11922 while (volItr->more())
11924 vol = (SMDS_MeshElement*)volItr->next();
11925 setOfInsideVol.insert(vol->GetVtkID());
11926 sgrp->Add(vol->GetID());
11930 else if (isNodeCoords)
11932 //MESSAGE("list of nodes coordinates provided");
11935 while ( i < nodesCoords.size()-2 )
11937 double x = nodesCoords[i++];
11938 double y = nodesCoords[i++];
11939 double z = nodesCoords[i++];
11940 gp_Pnt p = gp_Pnt(x, y ,z);
11941 gpnts.push_back(p);
11942 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11946 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11948 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11949 TopTools_IndexedMapOfShape vertexMap;
11950 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11951 gp_Pnt p = gp_Pnt(0,0,0);
11952 if (vertexMap.Extent() < 1)
11955 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11957 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11958 p = BRep_Tool::Pnt(vertex);
11959 gpnts.push_back(p);
11960 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11964 if (gpnts.size() > 0)
11966 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11967 //MESSAGE("startNode->nodeId " << nodeId);
11969 double radius2 = radius*radius;
11970 //MESSAGE("radius2 " << radius2);
11972 // --- volumes on start node
11974 setOfVolToCheck.clear();
11975 SMDS_MeshElement* startVol = 0;
11976 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11977 while (volItr->more())
11979 startVol = (SMDS_MeshElement*)volItr->next();
11980 setOfVolToCheck.insert(startVol->GetVtkID());
11982 if (setOfVolToCheck.empty())
11984 MESSAGE("No volumes found");
11988 // --- starting with central volumes then their neighbors, check if they are inside
11989 // or outside the domain, until no more new neighbor volume is inside.
11990 // Fill the group of inside volumes
11992 std::map<int, double> mapOfNodeDistance2;
11993 mapOfNodeDistance2.clear();
11994 std::set<int> setOfOutsideVol;
11995 while (!setOfVolToCheck.empty())
11997 std::set<int>::iterator it = setOfVolToCheck.begin();
11999 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12000 bool volInside = false;
12001 vtkIdType npts = 0;
12002 vtkIdType const *pts(nullptr);
12003 grid->GetCellPoints(vtkId, npts, pts);
12004 for (int i=0; i<npts; i++)
12006 double distance2 = 0;
12007 if (mapOfNodeDistance2.count(pts[i]))
12009 distance2 = mapOfNodeDistance2[pts[i]];
12010 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12014 double *coords = grid->GetPoint(pts[i]);
12015 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12017 for ( size_t j = 0; j < gpnts.size(); j++ )
12019 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12020 if (d2 < distance2)
12023 if (distance2 < radius2)
12027 mapOfNodeDistance2[pts[i]] = distance2;
12028 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12030 if (distance2 < radius2)
12032 volInside = true; // one or more nodes inside the domain
12033 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12039 setOfInsideVol.insert(vtkId);
12040 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12041 int neighborsVtkIds[NBMAXNEIGHBORS];
12042 int downIds[NBMAXNEIGHBORS];
12043 unsigned char downTypes[NBMAXNEIGHBORS];
12044 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12045 for (int n = 0; n < nbNeighbors; n++)
12046 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12047 setOfVolToCheck.insert(neighborsVtkIds[n]);
12051 setOfOutsideVol.insert(vtkId);
12052 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12054 setOfVolToCheck.erase(vtkId);
12058 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12059 // If yes, add the volume to the inside set
12061 bool addedInside = true;
12062 std::set<int> setOfVolToReCheck;
12063 while (addedInside)
12065 //MESSAGE(" --------------------------- re check");
12066 addedInside = false;
12067 std::set<int>::iterator itv = setOfInsideVol.begin();
12068 for (; itv != setOfInsideVol.end(); ++itv)
12071 int neighborsVtkIds[NBMAXNEIGHBORS];
12072 int downIds[NBMAXNEIGHBORS];
12073 unsigned char downTypes[NBMAXNEIGHBORS];
12074 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12075 for (int n = 0; n < nbNeighbors; n++)
12076 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12077 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12079 setOfVolToCheck = setOfVolToReCheck;
12080 setOfVolToReCheck.clear();
12081 while (!setOfVolToCheck.empty())
12083 std::set<int>::iterator it = setOfVolToCheck.begin();
12085 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12087 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12088 int countInside = 0;
12089 int neighborsVtkIds[NBMAXNEIGHBORS];
12090 int downIds[NBMAXNEIGHBORS];
12091 unsigned char downTypes[NBMAXNEIGHBORS];
12092 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12093 for (int n = 0; n < nbNeighbors; n++)
12094 if (setOfInsideVol.count(neighborsVtkIds[n]))
12096 //MESSAGE("countInside " << countInside);
12097 if (countInside > 1)
12099 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12100 setOfInsideVol.insert(vtkId);
12101 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12102 addedInside = true;
12105 setOfVolToReCheck.insert(vtkId);
12107 setOfVolToCheck.erase(vtkId);
12111 // --- map of Downward faces at the boundary, inside the global volume
12112 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12113 // fill group of SMDS faces inside the volume (when several volume shapes)
12114 // fill group of SMDS faces on the skin of the global volume (if skin)
12116 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12117 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12118 std::set<int>::iterator it = setOfInsideVol.begin();
12119 for (; it != setOfInsideVol.end(); ++it)
12122 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12123 int neighborsVtkIds[NBMAXNEIGHBORS];
12124 int downIds[NBMAXNEIGHBORS];
12125 unsigned char downTypes[NBMAXNEIGHBORS];
12126 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12127 for (int n = 0; n < nbNeighbors; n++)
12129 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12130 if (neighborDim == 3)
12132 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12134 DownIdType face(downIds[n], downTypes[n]);
12135 boundaryFaces[face] = vtkId;
12137 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12138 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12139 if (vtkFaceId >= 0)
12141 sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12142 // find also the smds edges on this face
12143 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12144 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12145 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12146 for (int i = 0; i < nbEdges; i++)
12148 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12149 if (vtkEdgeId >= 0)
12150 sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12154 else if (neighborDim == 2) // skin of the volume
12156 DownIdType face(downIds[n], downTypes[n]);
12157 skinFaces[face] = vtkId;
12158 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12159 if (vtkFaceId >= 0)
12160 sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12165 // --- identify the edges constituting the wire of each subshape on the skin
12166 // define polylines with the nodes of edges, equivalent to wires
12167 // project polylines on subshapes, and partition, to get geom faces
12169 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12170 std::set<int> emptySet;
12172 std::set<int> shapeIds;
12174 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12175 while (itelem->more())
12177 const SMDS_MeshElement *elem = itelem->next();
12178 int shapeId = elem->getshapeId();
12179 int vtkId = elem->GetVtkID();
12180 if (!shapeIdToVtkIdSet.count(shapeId))
12182 shapeIdToVtkIdSet[shapeId] = emptySet;
12183 shapeIds.insert(shapeId);
12185 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12188 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12189 std::set<DownIdType, DownIdCompare> emptyEdges;
12190 emptyEdges.clear();
12192 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12193 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12195 int shapeId = itShape->first;
12196 //MESSAGE(" --- Shape ID --- "<< shapeId);
12197 shapeIdToEdges[shapeId] = emptyEdges;
12199 std::vector<int> nodesEdges;
12201 std::set<int>::iterator its = itShape->second.begin();
12202 for (; its != itShape->second.end(); ++its)
12205 //MESSAGE(" " << vtkId);
12206 int neighborsVtkIds[NBMAXNEIGHBORS];
12207 int downIds[NBMAXNEIGHBORS];
12208 unsigned char downTypes[NBMAXNEIGHBORS];
12209 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12210 for (int n = 0; n < nbNeighbors; n++)
12212 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12214 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12215 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12216 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12218 DownIdType edge(downIds[n], downTypes[n]);
12219 if (!shapeIdToEdges[shapeId].count(edge))
12221 shapeIdToEdges[shapeId].insert(edge);
12223 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12224 nodesEdges.push_back(vtkNodeId[0]);
12225 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12226 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12232 std::list<int> order;
12234 if (nodesEdges.size() > 0)
12236 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12237 nodesEdges[0] = -1;
12238 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12239 nodesEdges[1] = -1; // do not reuse this edge
12243 int nodeTofind = order.back(); // try first to push back
12245 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12246 if (nodesEdges[i] == nodeTofind)
12248 if ( i == (int) nodesEdges.size() )
12249 found = false; // no follower found on back
12252 if (i%2) // odd ==> use the previous one
12253 if (nodesEdges[i-1] < 0)
12257 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12258 nodesEdges[i-1] = -1;
12260 else // even ==> use the next one
12261 if (nodesEdges[i+1] < 0)
12265 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12266 nodesEdges[i+1] = -1;
12271 // try to push front
12273 nodeTofind = order.front(); // try to push front
12274 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12275 if ( nodesEdges[i] == nodeTofind )
12277 if ( i == (int)nodesEdges.size() )
12279 found = false; // no predecessor found on front
12282 if (i%2) // odd ==> use the previous one
12283 if (nodesEdges[i-1] < 0)
12287 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12288 nodesEdges[i-1] = -1;
12290 else // even ==> use the next one
12291 if (nodesEdges[i+1] < 0)
12295 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12296 nodesEdges[i+1] = -1;
12302 std::vector<int> nodes;
12303 nodes.push_back(shapeId);
12304 std::list<int>::iterator itl = order.begin();
12305 for (; itl != order.end(); itl++)
12307 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12308 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12310 listOfListOfNodes.push_back(nodes);
12313 // partition geom faces with blocFissure
12314 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12315 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12321 //================================================================================
12323 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12324 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12325 * \return TRUE if operation has been completed successfully, FALSE otherwise
12327 //================================================================================
12329 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12331 // iterates on volume elements and detect all free faces on them
12332 SMESHDS_Mesh* aMesh = GetMeshDS();
12336 ElemFeatures faceType( SMDSAbs_Face );
12337 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12338 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12341 const SMDS_MeshVolume* volume = vIt->next();
12342 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12343 vTool.SetExternalNormal();
12344 const int iQuad = volume->IsQuadratic();
12345 faceType.SetQuad( iQuad );
12346 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12348 if (!vTool.IsFreeFace(iface))
12351 vector<const SMDS_MeshNode *> nodes;
12352 int nbFaceNodes = vTool.NbFaceNodes(iface);
12353 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12355 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12356 nodes.push_back(faceNodes[inode]);
12358 if (iQuad) // add medium nodes
12360 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12361 nodes.push_back(faceNodes[inode]);
12362 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12363 nodes.push_back(faceNodes[8]);
12365 // add new face based on volume nodes
12366 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12368 nbExisted++; // face already exists
12372 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12377 return ( nbFree == ( nbExisted + nbCreated ));
12382 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12384 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12386 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12389 //================================================================================
12391 * \brief Creates missing boundary elements
12392 * \param elements - elements whose boundary is to be checked
12393 * \param dimension - defines type of boundary elements to create
12394 * \param group - a group to store created boundary elements in
12395 * \param targetMesh - a mesh to store created boundary elements in
12396 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12397 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12398 * boundary elements will be copied into the targetMesh
12399 * \param toAddExistingBondary - if true, not only new but also pre-existing
12400 * boundary elements will be added into the new group
12401 * \param aroundElements - if true, elements will be created on boundary of given
12402 * elements else, on boundary of the whole mesh.
12403 * \return nb of added boundary elements
12405 //================================================================================
12407 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12408 Bnd_Dimension dimension,
12409 SMESH_Group* group/*=0*/,
12410 SMESH_Mesh* targetMesh/*=0*/,
12411 bool toCopyElements/*=false*/,
12412 bool toCopyExistingBoundary/*=false*/,
12413 bool toAddExistingBondary/*= false*/,
12414 bool aroundElements/*= false*/)
12416 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12417 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12418 // hope that all elements are of the same type, do not check them all
12419 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12420 throw SALOME_Exception(LOCALIZED("wrong element type"));
12423 toCopyElements = toCopyExistingBoundary = false;
12425 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12426 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12427 int nbAddedBnd = 0;
12429 // editor adding present bnd elements and optionally holding elements to add to the group
12430 SMESH_MeshEditor* presentEditor;
12431 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12432 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12434 SMESH_MesherHelper helper( *myMesh );
12435 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12436 SMDS_VolumeTool vTool;
12437 TIDSortedElemSet avoidSet;
12438 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12441 typedef vector<const SMDS_MeshNode*> TConnectivity;
12442 TConnectivity tgtNodes;
12443 ElemFeatures elemKind( missType ), elemToCopy;
12445 vector<const SMDS_MeshElement*> presentBndElems;
12446 vector<TConnectivity> missingBndElems;
12447 vector<int> freeFacets;
12448 TConnectivity nodes, elemNodes;
12450 SMDS_ElemIteratorPtr eIt;
12451 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12452 else eIt = SMESHUtils::elemSetIterator( elements );
12454 while ( eIt->more() )
12456 const SMDS_MeshElement* elem = eIt->next();
12457 const int iQuad = elem->IsQuadratic();
12458 elemKind.SetQuad( iQuad );
12460 // ------------------------------------------------------------------------------------
12461 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12462 // ------------------------------------------------------------------------------------
12463 presentBndElems.clear();
12464 missingBndElems.clear();
12465 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12466 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12468 const SMDS_MeshElement* otherVol = 0;
12469 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12471 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12472 ( !aroundElements || elements.count( otherVol )))
12474 freeFacets.push_back( iface );
12476 if ( missType == SMDSAbs_Face )
12477 vTool.SetExternalNormal();
12478 for ( size_t i = 0; i < freeFacets.size(); ++i )
12480 int iface = freeFacets[i];
12481 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12482 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12483 if ( missType == SMDSAbs_Edge ) // boundary edges
12485 nodes.resize( 2+iQuad );
12486 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12488 for ( size_t j = 0; j < nodes.size(); ++j )
12489 nodes[ j ] = nn[ i+j ];
12490 if ( const SMDS_MeshElement* edge =
12491 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12492 presentBndElems.push_back( edge );
12494 missingBndElems.push_back( nodes );
12497 else // boundary face
12500 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12501 nodes.push_back( nn[inode] ); // add corner nodes
12503 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12504 nodes.push_back( nn[inode] ); // add medium nodes
12505 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12507 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12509 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12510 SMDSAbs_Face, /*noMedium=*/false ))
12511 presentBndElems.push_back( f );
12513 missingBndElems.push_back( nodes );
12515 if ( targetMesh != myMesh )
12517 // add 1D elements on face boundary to be added to a new mesh
12518 const SMDS_MeshElement* edge;
12519 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12522 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12524 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12525 if ( edge && avoidSet.insert( edge ).second )
12526 presentBndElems.push_back( edge );
12532 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12534 avoidSet.clear(), avoidSet.insert( elem );
12535 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12536 SMDS_MeshElement::iterator() );
12537 elemNodes.push_back( elemNodes[0] );
12538 nodes.resize( 2 + iQuad );
12539 const int nbLinks = elem->NbCornerNodes();
12540 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12542 nodes[0] = elemNodes[iN];
12543 nodes[1] = elemNodes[iN+1+iQuad];
12544 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12545 continue; // not free link
12547 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12548 if ( const SMDS_MeshElement* edge =
12549 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12550 presentBndElems.push_back( edge );
12552 missingBndElems.push_back( nodes );
12556 // ---------------------------------
12557 // 2. Add missing boundary elements
12558 // ---------------------------------
12559 if ( targetMesh != myMesh )
12560 // instead of making a map of nodes in this mesh and targetMesh,
12561 // we create nodes with same IDs.
12562 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12564 TConnectivity& srcNodes = missingBndElems[i];
12565 tgtNodes.resize( srcNodes.size() );
12566 for ( inode = 0; inode < srcNodes.size(); ++inode )
12567 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12568 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12570 /*noMedium=*/false))
12572 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12576 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12578 TConnectivity& nodes = missingBndElems[ i ];
12579 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12581 /*noMedium=*/false))
12583 SMDS_MeshElement* newElem =
12584 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12585 nbAddedBnd += bool( newElem );
12587 // try to set a new element to a shape
12588 if ( myMesh->HasShapeToMesh() )
12591 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12592 const size_t nbN = nodes.size() / (iQuad+1 );
12593 for ( inode = 0; inode < nbN && ok; ++inode )
12595 pair<int, TopAbs_ShapeEnum> i_stype =
12596 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12597 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12598 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12600 if ( ok && mediumShapes.size() > 1 )
12602 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12603 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12604 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12606 if (( ok = ( stype_i->first != stype_i_0.first )))
12607 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12608 aMesh->IndexToShape( stype_i_0.second ));
12611 if ( ok && mediumShapes.begin()->first == missShapeType )
12612 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12616 // ----------------------------------
12617 // 3. Copy present boundary elements
12618 // ----------------------------------
12619 if ( toCopyExistingBoundary )
12620 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12622 const SMDS_MeshElement* e = presentBndElems[i];
12623 tgtNodes.resize( e->NbNodes() );
12624 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12625 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12626 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12628 else // store present elements to add them to a group
12629 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12631 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12634 } // loop on given elements
12636 // ---------------------------------------------
12637 // 4. Fill group with boundary elements
12638 // ---------------------------------------------
12641 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12642 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12643 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12645 tgtEditor.myLastCreatedElems.clear();
12646 tgtEditor2.myLastCreatedElems.clear();
12648 // -----------------------
12649 // 5. Copy given elements
12650 // -----------------------
12651 if ( toCopyElements && targetMesh != myMesh )
12653 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12654 else eIt = SMESHUtils::elemSetIterator( elements );
12655 while (eIt->more())
12657 const SMDS_MeshElement* elem = eIt->next();
12658 tgtNodes.resize( elem->NbNodes() );
12659 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12660 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12661 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12663 tgtEditor.myLastCreatedElems.clear();
12669 //================================================================================
12671 * \brief Copy node position and set \a to node on the same geometry
12673 //================================================================================
12675 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12676 const SMDS_MeshNode* to )
12678 if ( !from || !to ) return;
12680 SMDS_PositionPtr pos = from->GetPosition();
12681 if ( !pos || from->getshapeId() < 1 ) return;
12683 switch ( pos->GetTypeOfPosition() )
12685 case SMDS_TOP_3DSPACE: break;
12687 case SMDS_TOP_FACE:
12689 SMDS_FacePositionPtr fPos = pos;
12690 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12691 fPos->GetUParameter(), fPos->GetVParameter() );
12694 case SMDS_TOP_EDGE:
12696 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12697 SMDS_EdgePositionPtr ePos = pos;
12698 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12701 case SMDS_TOP_VERTEX:
12703 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12706 case SMDS_TOP_UNSPEC: