1 // Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
49 #include "utilities.h"
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_ListOfInteger.hxx>
66 #include <TopAbs_State.hxx>
68 #include <TopExp_Explorer.hxx>
69 #include <TopTools_ListIteratorOfListOfShape.hxx>
70 #include <TopTools_ListOfShape.hxx>
71 #include <TopTools_SequenceOfShape.hxx>
73 #include <TopoDS_Edge.hxx>
74 #include <TopoDS_Face.hxx>
75 #include <TopoDS_Solid.hxx>
81 #include <gp_Trsf.hxx>
95 #include <boost/tuple/tuple.hpp>
96 #include <boost/container/flat_set.hpp>
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
101 #include "SMESH_TryCatch.hxx" // include after OCCT headers!
103 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
106 using namespace SMESH::Controls;
108 //=======================================================================
109 //function : SMESH_MeshEditor
111 //=======================================================================
113 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
114 :myMesh( theMesh ) // theMesh may be NULL
118 //================================================================================
120 * \brief Return mesh DS
122 //================================================================================
124 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
126 return myMesh->GetMeshDS();
130 //================================================================================
132 * \brief Clears myLastCreatedNodes and myLastCreatedElems
134 //================================================================================
136 void SMESH_MeshEditor::ClearLastCreated()
138 SMESHUtils::FreeVector( myLastCreatedElems );
139 SMESHUtils::FreeVector( myLastCreatedNodes );
142 //================================================================================
144 * \brief Initializes members by an existing element
145 * \param [in] elem - the source element
146 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
148 //================================================================================
150 SMESH_MeshEditor::ElemFeatures&
151 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
155 myType = elem->GetType();
156 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
158 myIsPoly = elem->IsPoly();
161 myIsQuad = elem->IsQuadratic();
162 if ( myType == SMDSAbs_Volume && !basicOnly )
164 vector<int> quant = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
165 myPolyhedQuantities.swap( quant );
169 else if ( myType == SMDSAbs_Ball && !basicOnly )
171 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
177 //=======================================================================
181 //=======================================================================
184 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
185 const ElemFeatures& features)
187 SMDS_MeshElement* e = 0;
188 int nbnode = node.size();
189 SMESHDS_Mesh* mesh = GetMeshDS();
190 const int ID = features.myID;
192 switch ( features.myType ) {
194 if ( !features.myIsPoly ) {
196 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
197 else e = mesh->AddFace (node[0], node[1], node[2] );
199 else if (nbnode == 4) {
200 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
201 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
203 else if (nbnode == 6) {
204 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
205 node[4], node[5], ID);
206 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
209 else if (nbnode == 7) {
210 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
211 node[4], node[5], node[6], ID);
212 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
213 node[4], node[5], node[6] );
215 else if (nbnode == 8) {
216 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
217 node[4], node[5], node[6], node[7], ID);
218 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
219 node[4], node[5], node[6], node[7] );
221 else if (nbnode == 9) {
222 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
223 node[4], node[5], node[6], node[7], node[8], ID);
224 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
225 node[4], node[5], node[6], node[7], node[8] );
228 else if ( !features.myIsQuad )
230 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
231 else e = mesh->AddPolygonalFace (node );
233 else if ( nbnode % 2 == 0 ) // just a protection
235 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
236 else e = mesh->AddQuadPolygonalFace (node );
241 if ( !features.myIsPoly ) {
243 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
244 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
246 else if (nbnode == 5) {
247 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
249 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
252 else if (nbnode == 6) {
253 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
254 node[4], node[5], ID);
255 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
258 else if (nbnode == 8) {
259 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
260 node[4], node[5], node[6], node[7], ID);
261 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
262 node[4], node[5], node[6], node[7] );
264 else if (nbnode == 10) {
265 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
266 node[4], node[5], node[6], node[7],
267 node[8], node[9], ID);
268 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
269 node[4], node[5], node[6], node[7],
272 else if (nbnode == 12) {
273 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
274 node[4], node[5], node[6], node[7],
275 node[8], node[9], node[10], node[11], ID);
276 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
277 node[4], node[5], node[6], node[7],
278 node[8], node[9], node[10], node[11] );
280 else if (nbnode == 13) {
281 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
282 node[4], node[5], node[6], node[7],
283 node[8], node[9], node[10],node[11],
285 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
286 node[4], node[5], node[6], node[7],
287 node[8], node[9], node[10],node[11],
290 else if (nbnode == 15) {
291 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
292 node[4], node[5], node[6], node[7],
293 node[8], node[9], node[10],node[11],
294 node[12],node[13],node[14],ID);
295 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
296 node[4], node[5], node[6], node[7],
297 node[8], node[9], node[10],node[11],
298 node[12],node[13],node[14] );
300 else if (nbnode == 20) {
301 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
302 node[4], node[5], node[6], node[7],
303 node[8], node[9], node[10],node[11],
304 node[12],node[13],node[14],node[15],
305 node[16],node[17],node[18],node[19],ID);
306 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
307 node[4], node[5], node[6], node[7],
308 node[8], node[9], node[10],node[11],
309 node[12],node[13],node[14],node[15],
310 node[16],node[17],node[18],node[19] );
312 else if (nbnode == 27) {
313 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
314 node[4], node[5], node[6], node[7],
315 node[8], node[9], node[10],node[11],
316 node[12],node[13],node[14],node[15],
317 node[16],node[17],node[18],node[19],
318 node[20],node[21],node[22],node[23],
319 node[24],node[25],node[26], ID);
320 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
321 node[4], node[5], node[6], node[7],
322 node[8], node[9], node[10],node[11],
323 node[12],node[13],node[14],node[15],
324 node[16],node[17],node[18],node[19],
325 node[20],node[21],node[22],node[23],
326 node[24],node[25],node[26] );
329 else if ( !features.myIsQuad )
331 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
332 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
336 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
337 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
343 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
344 else e = mesh->AddEdge (node[0], node[1] );
346 else if ( nbnode == 3 ) {
347 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
348 else e = mesh->AddEdge (node[0], node[1], node[2] );
352 case SMDSAbs_0DElement:
354 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
355 else e = mesh->Add0DElement (node[0] );
360 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
361 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
365 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
366 else e = mesh->AddBall (node[0], features.myBallDiameter );
371 if ( e ) myLastCreatedElems.push_back( e );
375 //=======================================================================
379 //=======================================================================
381 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
382 const ElemFeatures& features)
384 vector<const SMDS_MeshNode*> nodes;
385 nodes.reserve( nodeIDs.size() );
386 vector<int>::const_iterator id = nodeIDs.begin();
387 while ( id != nodeIDs.end() ) {
388 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
389 nodes.push_back( node );
393 return AddElement( nodes, features );
396 //=======================================================================
398 //purpose : Remove a node or an element.
399 // Modify a compute state of sub-meshes which become empty
400 //=======================================================================
402 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
407 SMESHDS_Mesh* aMesh = GetMeshDS();
408 set< SMESH_subMesh *> smmap;
411 list<int>::const_iterator it = theIDs.begin();
412 for ( ; it != theIDs.end(); it++ ) {
413 const SMDS_MeshElement * elem;
415 elem = aMesh->FindNode( *it );
417 elem = aMesh->FindElement( *it );
421 // Notify VERTEX sub-meshes about modification
423 const SMDS_MeshNode* node = cast2Node( elem );
424 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
425 if ( int aShapeID = node->getshapeId() )
426 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
429 // Find sub-meshes to notify about modification
430 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
431 // while ( nodeIt->more() ) {
432 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
433 // const SMDS_PositionPtr& aPosition = node->GetPosition();
434 // if ( aPosition.get() ) {
435 // if ( int aShapeID = aPosition->GetShapeId() ) {
436 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
437 // smmap.insert( sm );
444 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
446 aMesh->RemoveElement( elem );
450 // Notify sub-meshes about modification
451 if ( !smmap.empty() ) {
452 set< SMESH_subMesh *>::iterator smIt;
453 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
454 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
457 // // Check if the whole mesh becomes empty
458 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
459 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
464 //================================================================================
466 * \brief Create 0D elements on all nodes of the given object.
467 * \param elements - Elements on whose nodes to create 0D elements; if empty,
468 * the all mesh is treated
469 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
470 * \param duplicateElements - to add one more 0D element to a node or not
472 //================================================================================
474 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
475 TIDSortedElemSet& all0DElems,
476 const bool duplicateElements )
478 SMDS_ElemIteratorPtr elemIt;
479 if ( elements.empty() )
481 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
485 elemIt = SMESHUtils::elemSetIterator( elements );
488 while ( elemIt->more() )
490 const SMDS_MeshElement* e = elemIt->next();
491 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
492 while ( nodeIt->more() )
494 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
495 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
496 if ( duplicateElements || !it0D->more() )
498 myLastCreatedElems.push_back( GetMeshDS()->Add0DElement( n ));
499 all0DElems.insert( myLastCreatedElems.back() );
501 while ( it0D->more() )
502 all0DElems.insert( it0D->next() );
507 //=======================================================================
508 //function : FindShape
509 //purpose : Return an index of the shape theElem is on
510 // or zero if a shape not found
511 //=======================================================================
513 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
517 SMESHDS_Mesh * aMesh = GetMeshDS();
518 if ( aMesh->ShapeToMesh().IsNull() )
521 int aShapeID = theElem->getshapeId();
525 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
526 if ( sm->Contains( theElem ))
529 if ( theElem->GetType() == SMDSAbs_Node ) {
530 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
533 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
536 TopoDS_Shape aShape; // the shape a node of theElem is on
537 if ( theElem->GetType() != SMDSAbs_Node )
539 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
540 while ( nodeIt->more() ) {
541 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
542 if ((aShapeID = node->getshapeId()) > 0) {
543 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
544 if ( sm->Contains( theElem ))
546 if ( aShape.IsNull() )
547 aShape = aMesh->IndexToShape( aShapeID );
553 // None of nodes is on a proper shape,
554 // find the shape among ancestors of aShape on which a node is
555 if ( !aShape.IsNull() ) {
556 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
557 for ( ; ancIt.More(); ancIt.Next() ) {
558 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
559 if ( sm && sm->Contains( theElem ))
560 return aMesh->ShapeToIndex( ancIt.Value() );
565 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
566 while ( const SMESHDS_SubMesh* sm = smIt->next() )
567 if ( sm->Contains( theElem ))
574 //=======================================================================
575 //function : IsMedium
577 //=======================================================================
579 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
580 const SMDSAbs_ElementType typeToCheck)
582 bool isMedium = false;
583 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
584 while (it->more() && !isMedium ) {
585 const SMDS_MeshElement* elem = it->next();
586 isMedium = elem->IsMediumNode(node);
591 //=======================================================================
592 //function : shiftNodesQuadTria
593 //purpose : Shift nodes in the array corresponded to quadratic triangle
594 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
595 //=======================================================================
597 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
599 const SMDS_MeshNode* nd1 = aNodes[0];
600 aNodes[0] = aNodes[1];
601 aNodes[1] = aNodes[2];
603 const SMDS_MeshNode* nd2 = aNodes[3];
604 aNodes[3] = aNodes[4];
605 aNodes[4] = aNodes[5];
609 //=======================================================================
610 //function : nbEdgeConnectivity
611 //purpose : return number of the edges connected with the theNode.
612 // if theEdges has connections with the other type of the
613 // elements, return -1
614 //=======================================================================
616 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
618 // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
620 // while(elemIt->more()) {
625 return theNode->NbInverseElements();
628 //=======================================================================
629 //function : getNodesFromTwoTria
631 //=======================================================================
633 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
634 const SMDS_MeshElement * theTria2,
635 vector< const SMDS_MeshNode*>& N1,
636 vector< const SMDS_MeshNode*>& N2)
638 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
639 if ( N1.size() < 6 ) return false;
640 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
641 if ( N2.size() < 6 ) return false;
643 int sames[3] = {-1,-1,-1};
655 if(nbsames!=2) return false;
657 shiftNodesQuadTria(N1);
659 shiftNodesQuadTria(N1);
662 i = sames[0] + sames[1] + sames[2];
664 shiftNodesQuadTria(N2);
666 // now we receive following N1 and N2 (using numeration as in the image below)
667 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
668 // i.e. first nodes from both arrays form a new diagonal
672 //=======================================================================
673 //function : InverseDiag
674 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
675 // but having other common link.
676 // Return False if args are improper
677 //=======================================================================
679 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
680 const SMDS_MeshElement * theTria2 )
684 if ( !theTria1 || !theTria2 ||
685 !dynamic_cast<const SMDS_MeshCell*>( theTria1 ) ||
686 !dynamic_cast<const SMDS_MeshCell*>( theTria2 ) ||
687 theTria1->GetType() != SMDSAbs_Face ||
688 theTria2->GetType() != SMDSAbs_Face )
691 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
692 (theTria2->GetEntityType() == SMDSEntity_Triangle))
694 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
695 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
699 // put nodes in array and find out indices of the same ones
700 const SMDS_MeshNode* aNodes [6];
701 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
703 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
704 while ( it->more() ) {
705 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
707 if ( i > 2 ) // theTria2
708 // find same node of theTria1
709 for ( int j = 0; j < 3; j++ )
710 if ( aNodes[ i ] == aNodes[ j ]) {
719 return false; // theTria1 is not a triangle
720 it = theTria2->nodesIterator();
722 if ( i == 6 && it->more() )
723 return false; // theTria2 is not a triangle
726 // find indices of 1,2 and of A,B in theTria1
727 int iA = -1, iB = 0, i1 = 0, i2 = 0;
728 for ( i = 0; i < 6; i++ ) {
729 if ( sameInd [ i ] == -1 ) {
734 if ( iA >= 0) iB = i;
738 // nodes 1 and 2 should not be the same
739 if ( aNodes[ i1 ] == aNodes[ i2 ] )
743 aNodes[ iA ] = aNodes[ i2 ];
745 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
747 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
748 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
752 } // end if(F1 && F2)
754 // check case of quadratic faces
755 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
756 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
758 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
759 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
763 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
764 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
772 vector< const SMDS_MeshNode* > N1;
773 vector< const SMDS_MeshNode* > N2;
774 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
776 // now we receive following N1 and N2 (using numeration as above image)
777 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
778 // i.e. first nodes from both arrays determ new diagonal
780 vector< const SMDS_MeshNode*> N1new( N1.size() );
781 vector< const SMDS_MeshNode*> N2new( N2.size() );
782 N1new.back() = N1.back(); // central node of biquadratic
783 N2new.back() = N2.back();
784 N1new[0] = N1[0]; N2new[0] = N1[0];
785 N1new[1] = N2[0]; N2new[1] = N1[1];
786 N1new[2] = N2[1]; N2new[2] = N2[0];
787 N1new[3] = N1[4]; N2new[3] = N1[3];
788 N1new[4] = N2[3]; N2new[4] = N2[5];
789 N1new[5] = N1[5]; N2new[5] = N1[4];
790 // change nodes in faces
791 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
792 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
794 // move the central node of biquadratic triangle
795 SMESH_MesherHelper helper( *GetMesh() );
796 for ( int is2nd = 0; is2nd < 2; ++is2nd )
798 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
799 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
800 if ( nodes.size() < 7 )
802 helper.SetSubShape( tria->getshapeId() );
803 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
807 xyz = ( SMESH_NodeXYZ( nodes[3] ) +
808 SMESH_NodeXYZ( nodes[4] ) +
809 SMESH_NodeXYZ( nodes[5] )) / 3.;
814 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
815 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
816 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
818 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
819 xyz = S->Value( uv.X(), uv.Y() );
820 xyz.Transform( loc );
821 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
822 nodes[6]->getshapeId() > 0 )
823 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
825 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
830 //=======================================================================
831 //function : findTriangles
832 //purpose : find triangles sharing theNode1-theNode2 link
833 //=======================================================================
835 static bool findTriangles(const SMDS_MeshNode * theNode1,
836 const SMDS_MeshNode * theNode2,
837 const SMDS_MeshElement*& theTria1,
838 const SMDS_MeshElement*& theTria2)
840 if ( !theNode1 || !theNode2 ) return false;
842 theTria1 = theTria2 = 0;
844 set< const SMDS_MeshElement* > emap;
845 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
847 const SMDS_MeshElement* elem = it->next();
848 if ( elem->NbCornerNodes() == 3 )
851 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
853 const SMDS_MeshElement* elem = it->next();
854 if ( emap.count( elem )) {
862 // theTria1 must be element with minimum ID
863 if ( theTria2->GetID() < theTria1->GetID() )
864 std::swap( theTria2, theTria1 );
872 //=======================================================================
873 //function : InverseDiag
874 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
875 // with ones built on the same 4 nodes but having other common link.
876 // Return false if proper faces not found
877 //=======================================================================
879 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
880 const SMDS_MeshNode * theNode2)
884 const SMDS_MeshElement *tr1, *tr2;
885 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
888 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
889 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
892 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
893 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
895 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
896 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
900 // put nodes in array
901 // and find indices of 1,2 and of A in tr1 and of B in tr2
902 int i, iA1 = 0, i1 = 0;
903 const SMDS_MeshNode* aNodes1 [3];
904 SMDS_ElemIteratorPtr it;
905 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
906 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
907 if ( aNodes1[ i ] == theNode1 )
908 iA1 = i; // node A in tr1
909 else if ( aNodes1[ i ] != theNode2 )
913 const SMDS_MeshNode* aNodes2 [3];
914 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
915 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
916 if ( aNodes2[ i ] == theNode2 )
917 iB2 = i; // node B in tr2
918 else if ( aNodes2[ i ] != theNode1 )
922 // nodes 1 and 2 should not be the same
923 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
927 aNodes1[ iA1 ] = aNodes2[ i2 ];
929 aNodes2[ iB2 ] = aNodes1[ i1 ];
931 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
932 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
937 // check case of quadratic faces
938 return InverseDiag(tr1,tr2);
941 //=======================================================================
942 //function : getQuadrangleNodes
943 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
944 // fusion of triangles tr1 and tr2 having shared link on
945 // theNode1 and theNode2
946 //=======================================================================
948 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
949 const SMDS_MeshNode * theNode1,
950 const SMDS_MeshNode * theNode2,
951 const SMDS_MeshElement * tr1,
952 const SMDS_MeshElement * tr2 )
954 if( tr1->NbNodes() != tr2->NbNodes() )
956 // find the 4-th node to insert into tr1
957 const SMDS_MeshNode* n4 = 0;
958 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
960 while ( !n4 && i<3 ) {
961 const SMDS_MeshNode * n = cast2Node( it->next() );
963 bool isDiag = ( n == theNode1 || n == theNode2 );
967 // Make an array of nodes to be in a quadrangle
968 int iNode = 0, iFirstDiag = -1;
969 it = tr1->nodesIterator();
972 const SMDS_MeshNode * n = cast2Node( it->next() );
974 bool isDiag = ( n == theNode1 || n == theNode2 );
976 if ( iFirstDiag < 0 )
978 else if ( iNode - iFirstDiag == 1 )
979 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
981 else if ( n == n4 ) {
982 return false; // tr1 and tr2 should not have all the same nodes
984 theQuadNodes[ iNode++ ] = n;
986 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
987 theQuadNodes[ iNode ] = n4;
992 //=======================================================================
993 //function : DeleteDiag
994 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
995 // with a quadrangle built on the same 4 nodes.
996 // Return false if proper faces not found
997 //=======================================================================
999 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1000 const SMDS_MeshNode * theNode2)
1004 const SMDS_MeshElement *tr1, *tr2;
1005 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1008 if ( !dynamic_cast<const SMDS_MeshCell*>( tr1 ) ||
1009 !dynamic_cast<const SMDS_MeshCell*>( tr2 ))
1012 SMESHDS_Mesh * aMesh = GetMeshDS();
1014 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1015 (tr2->GetEntityType() == SMDSEntity_Triangle))
1017 const SMDS_MeshNode* aNodes [ 4 ];
1018 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1021 const SMDS_MeshElement* newElem = 0;
1022 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1023 myLastCreatedElems.push_back(newElem);
1024 AddToSameGroups( newElem, tr1, aMesh );
1025 int aShapeId = tr1->getshapeId();
1027 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1029 aMesh->RemoveElement( tr1 );
1030 aMesh->RemoveElement( tr2 );
1035 // check case of quadratic faces
1036 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1038 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1042 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1043 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1051 vector< const SMDS_MeshNode* > N1;
1052 vector< const SMDS_MeshNode* > N2;
1053 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1055 // now we receive following N1 and N2 (using numeration as above image)
1056 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1057 // i.e. first nodes from both arrays determ new diagonal
1059 const SMDS_MeshNode* aNodes[8];
1069 const SMDS_MeshElement* newElem = 0;
1070 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1071 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1072 myLastCreatedElems.push_back(newElem);
1073 AddToSameGroups( newElem, tr1, aMesh );
1074 int aShapeId = tr1->getshapeId();
1077 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1079 aMesh->RemoveElement( tr1 );
1080 aMesh->RemoveElement( tr2 );
1082 // remove middle node (9)
1083 GetMeshDS()->RemoveNode( N1[4] );
1088 //=======================================================================
1089 //function : Reorient
1090 //purpose : Reverse theElement orientation
1091 //=======================================================================
1093 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1099 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1100 if ( !it || !it->more() )
1103 const SMDSAbs_ElementType type = theElem->GetType();
1104 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1107 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1108 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1110 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( theElem );
1112 MESSAGE("Warning: bad volumic element");
1115 const int nbFaces = aPolyedre->NbFaces();
1116 vector<const SMDS_MeshNode *> poly_nodes;
1117 vector<int> quantities (nbFaces);
1119 // reverse each face of the polyedre
1120 for (int iface = 1; iface <= nbFaces; iface++) {
1121 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1122 quantities[iface - 1] = nbFaceNodes;
1124 for (inode = nbFaceNodes; inode >= 1; inode--) {
1125 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1126 poly_nodes.push_back(curNode);
1129 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1131 else // other elements
1133 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1134 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1135 if ( interlace.empty() )
1137 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1141 SMDS_MeshCell::applyInterlace( interlace, nodes );
1143 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1148 //================================================================================
1150 * \brief Reorient faces.
1151 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1152 * \param theDirection - desired direction of normal of \a theFace
1153 * \param theFace - one of \a theFaces that should be oriented according to
1154 * \a theDirection and whose orientation defines orientation of other faces
1155 * \return number of reoriented faces.
1157 //================================================================================
1159 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1160 const gp_Dir& theDirection,
1161 const SMDS_MeshElement * theFace)
1164 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1166 if ( theFaces.empty() )
1168 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=true*/);
1169 while ( fIt->more() )
1170 theFaces.insert( theFaces.end(), fIt->next() );
1173 // orient theFace according to theDirection
1175 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1176 if ( normal * theDirection.XYZ() < 0 )
1177 nbReori += Reorient( theFace );
1179 // Orient other faces
1181 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1182 TIDSortedElemSet avoidSet;
1183 set< SMESH_TLink > checkedLinks;
1184 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1186 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1187 theFaces.erase( theFace );
1188 startFaces.insert( theFace );
1190 int nodeInd1, nodeInd2;
1191 const SMDS_MeshElement* otherFace;
1192 vector< const SMDS_MeshElement* > facesNearLink;
1193 vector< std::pair< int, int > > nodeIndsOfFace;
1195 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1196 while ( !startFaces.empty() )
1198 startFace = startFaces.begin();
1199 theFace = *startFace;
1200 startFaces.erase( startFace );
1201 if ( !visitedFaces.insert( theFace ).second )
1205 avoidSet.insert(theFace);
1207 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1209 const int nbNodes = theFace->NbCornerNodes();
1210 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1212 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1213 linkIt_isNew = checkedLinks.insert( link );
1214 if ( !linkIt_isNew.second )
1216 // link has already been checked and won't be encountered more
1217 // if the group (theFaces) is manifold
1218 //checkedLinks.erase( linkIt_isNew.first );
1222 facesNearLink.clear();
1223 nodeIndsOfFace.clear();
1224 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1226 &nodeInd1, &nodeInd2 )))
1227 if ( otherFace != theFace)
1229 facesNearLink.push_back( otherFace );
1230 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1231 avoidSet.insert( otherFace );
1233 if ( facesNearLink.size() > 1 )
1235 // NON-MANIFOLD mesh shell !
1236 // select a face most co-directed with theFace,
1237 // other faces won't be visited this time
1239 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1240 double proj, maxProj = -1;
1241 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1242 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1243 if (( proj = Abs( NF * NOF )) > maxProj ) {
1245 otherFace = facesNearLink[i];
1246 nodeInd1 = nodeIndsOfFace[i].first;
1247 nodeInd2 = nodeIndsOfFace[i].second;
1250 // not to visit rejected faces
1251 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1252 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1253 visitedFaces.insert( facesNearLink[i] );
1255 else if ( facesNearLink.size() == 1 )
1257 otherFace = facesNearLink[0];
1258 nodeInd1 = nodeIndsOfFace.back().first;
1259 nodeInd2 = nodeIndsOfFace.back().second;
1261 if ( otherFace && otherFace != theFace)
1263 // link must be reverse in otherFace if orientation to otherFace
1264 // is same as that of theFace
1265 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1267 nbReori += Reorient( otherFace );
1269 startFaces.insert( otherFace );
1272 std::swap( link.first, link.second ); // reverse the link
1278 //================================================================================
1280 * \brief Reorient faces basing on orientation of adjacent volumes.
1281 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1282 * \param theVolumes - reference volumes.
1283 * \param theOutsideNormal - to orient faces to have their normal
1284 * pointing either \a outside or \a inside the adjacent volumes.
1285 * \return number of reoriented faces.
1287 //================================================================================
1289 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1290 TIDSortedElemSet & theVolumes,
1291 const bool theOutsideNormal)
1295 SMDS_ElemIteratorPtr faceIt;
1296 if ( theFaces.empty() )
1297 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1299 faceIt = SMESHUtils::elemSetIterator( theFaces );
1301 vector< const SMDS_MeshNode* > faceNodes;
1302 TIDSortedElemSet checkedVolumes;
1303 set< const SMDS_MeshNode* > faceNodesSet;
1304 SMDS_VolumeTool volumeTool;
1306 while ( faceIt->more() ) // loop on given faces
1308 const SMDS_MeshElement* face = faceIt->next();
1309 if ( face->GetType() != SMDSAbs_Face )
1312 const size_t nbCornersNodes = face->NbCornerNodes();
1313 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1315 checkedVolumes.clear();
1316 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1317 while ( vIt->more() )
1319 const SMDS_MeshElement* volume = vIt->next();
1321 if ( !checkedVolumes.insert( volume ).second )
1323 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1326 // is volume adjacent?
1327 bool allNodesCommon = true;
1328 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1329 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1330 if ( !allNodesCommon )
1333 // get nodes of a corresponding volume facet
1334 faceNodesSet.clear();
1335 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1336 volumeTool.Set( volume );
1337 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1338 if ( facetID < 0 ) continue;
1339 volumeTool.SetExternalNormal();
1340 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1342 // compare order of faceNodes and facetNodes
1343 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1345 for ( int i = 0; i < 2; ++i )
1347 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1348 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1349 if ( faceNodes[ iN ] == n )
1355 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1356 if ( isOutside != theOutsideNormal )
1357 nbReori += Reorient( face );
1359 } // loop on given faces
1364 //=======================================================================
1365 //function : getBadRate
1367 //=======================================================================
1369 static double getBadRate (const SMDS_MeshElement* theElem,
1370 SMESH::Controls::NumericalFunctorPtr& theCrit)
1372 SMESH::Controls::TSequenceOfXYZ P;
1373 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1375 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1376 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1379 //=======================================================================
1380 //function : QuadToTri
1381 //purpose : Cut quadrangles into triangles.
1382 // theCrit is used to select a diagonal to cut
1383 //=======================================================================
1385 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1386 SMESH::Controls::NumericalFunctorPtr theCrit)
1390 if ( !theCrit.get() )
1393 SMESHDS_Mesh * aMesh = GetMeshDS();
1394 Handle(Geom_Surface) surface;
1395 SMESH_MesherHelper helper( *GetMesh() );
1397 myLastCreatedElems.reserve( theElems.size() * 2 );
1399 TIDSortedElemSet::iterator itElem;
1400 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1402 const SMDS_MeshElement* elem = *itElem;
1403 if ( !elem || elem->GetType() != SMDSAbs_Face )
1405 if ( elem->NbCornerNodes() != 4 )
1408 // retrieve element nodes
1409 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1411 // compare two sets of possible triangles
1412 double aBadRate1, aBadRate2; // to what extent a set is bad
1413 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1414 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1415 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1417 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1418 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1419 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1421 const int aShapeId = FindShape( elem );
1422 const SMDS_MeshElement* newElem1 = 0;
1423 const SMDS_MeshElement* newElem2 = 0;
1425 if ( !elem->IsQuadratic() ) // split linear quadrangle
1427 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1428 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1429 if ( aBadRate1 <= aBadRate2 ) {
1430 // tr1 + tr2 is better
1431 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1432 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1435 // tr3 + tr4 is better
1436 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1437 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1440 else // split quadratic quadrangle
1442 helper.SetIsQuadratic( true );
1443 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1445 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1446 if ( aNodes.size() == 9 )
1448 helper.SetIsBiQuadratic( true );
1449 if ( aBadRate1 <= aBadRate2 )
1450 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1452 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1454 // create a new element
1455 if ( aBadRate1 <= aBadRate2 ) {
1456 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1457 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1460 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1461 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1465 // care of a new element
1467 myLastCreatedElems.push_back(newElem1);
1468 myLastCreatedElems.push_back(newElem2);
1469 AddToSameGroups( newElem1, elem, aMesh );
1470 AddToSameGroups( newElem2, elem, aMesh );
1472 // put a new triangle on the same shape
1474 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1475 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1477 aMesh->RemoveElement( elem );
1482 //=======================================================================
1484 * \brief Split each of given quadrangles into 4 triangles.
1485 * \param theElems - The faces to be split. If empty all faces are split.
1487 //=======================================================================
1489 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1492 myLastCreatedElems.reserve( theElems.size() * 4 );
1494 SMESH_MesherHelper helper( *GetMesh() );
1495 helper.SetElementsOnShape( true );
1497 SMDS_ElemIteratorPtr faceIt;
1498 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1499 else faceIt = SMESHUtils::elemSetIterator( theElems );
1502 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1504 vector< const SMDS_MeshNode* > nodes;
1505 SMESHDS_SubMesh* subMeshDS = 0;
1507 Handle(Geom_Surface) surface;
1508 TopLoc_Location loc;
1510 while ( faceIt->more() )
1512 const SMDS_MeshElement* quad = faceIt->next();
1513 if ( !quad || quad->NbCornerNodes() != 4 )
1516 // get a surface the quad is on
1518 if ( quad->getshapeId() < 1 )
1521 helper.SetSubShape( 0 );
1524 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1526 helper.SetSubShape( quad->getshapeId() );
1527 if ( !helper.GetSubShape().IsNull() &&
1528 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1530 F = TopoDS::Face( helper.GetSubShape() );
1531 surface = BRep_Tool::Surface( F, loc );
1532 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1536 helper.SetSubShape( 0 );
1541 // create a central node
1543 const SMDS_MeshNode* nCentral;
1544 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1546 if ( nodes.size() == 9 )
1548 nCentral = nodes.back();
1555 for ( ; iN < nodes.size(); ++iN )
1556 xyz[ iN ] = SMESH_NodeXYZ( nodes[ iN ] );
1558 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1559 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1561 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1562 xyz[0], xyz[1], xyz[2], xyz[3],
1563 xyz[4], xyz[5], xyz[6], xyz[7] );
1567 for ( ; iN < nodes.size(); ++iN )
1568 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1570 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1571 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1573 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1574 uv[0], uv[1], uv[2], uv[3],
1575 uv[4], uv[5], uv[6], uv[7] );
1577 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1581 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1582 uv[8].X(), uv[8].Y() );
1583 myLastCreatedNodes.push_back( nCentral );
1586 // create 4 triangles
1588 helper.SetIsQuadratic ( nodes.size() > 4 );
1589 helper.SetIsBiQuadratic( nodes.size() == 9 );
1590 if ( helper.GetIsQuadratic() )
1591 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1593 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1595 for ( int i = 0; i < 4; ++i )
1597 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1600 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1601 myLastCreatedElems.push_back( tria );
1606 //=======================================================================
1607 //function : BestSplit
1608 //purpose : Find better diagonal for cutting.
1609 //=======================================================================
1611 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1612 SMESH::Controls::NumericalFunctorPtr theCrit)
1619 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1622 if( theQuad->NbNodes()==4 ||
1623 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1625 // retrieve element nodes
1626 const SMDS_MeshNode* aNodes [4];
1627 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1629 //while (itN->more())
1631 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1633 // compare two sets of possible triangles
1634 double aBadRate1, aBadRate2; // to what extent a set is bad
1635 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1636 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1637 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1639 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1640 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1641 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1642 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1643 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1644 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1645 return 1; // diagonal 1-3
1647 return 2; // diagonal 2-4
1654 // Methods of splitting volumes into tetra
1656 const int theHexTo5_1[5*4+1] =
1658 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1660 const int theHexTo5_2[5*4+1] =
1662 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1664 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1666 const int theHexTo6_1[6*4+1] =
1668 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
1670 const int theHexTo6_2[6*4+1] =
1672 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
1674 const int theHexTo6_3[6*4+1] =
1676 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
1678 const int theHexTo6_4[6*4+1] =
1680 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
1682 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1684 const int thePyraTo2_1[2*4+1] =
1686 0, 1, 2, 4, 0, 2, 3, 4, -1
1688 const int thePyraTo2_2[2*4+1] =
1690 1, 2, 3, 4, 1, 3, 0, 4, -1
1692 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1694 const int thePentaTo3_1[3*4+1] =
1696 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1698 const int thePentaTo3_2[3*4+1] =
1700 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1702 const int thePentaTo3_3[3*4+1] =
1704 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1706 const int thePentaTo3_4[3*4+1] =
1708 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1710 const int thePentaTo3_5[3*4+1] =
1712 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1714 const int thePentaTo3_6[3*4+1] =
1716 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1718 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1719 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1721 // Methods of splitting hexahedron into prisms
1723 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1725 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
1727 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1729 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
1731 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1733 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
1736 const int theHexTo2Prisms_BT_1[6*2+1] =
1738 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1740 const int theHexTo2Prisms_BT_2[6*2+1] =
1742 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1744 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1746 const int theHexTo2Prisms_LR_1[6*2+1] =
1748 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1750 const int theHexTo2Prisms_LR_2[6*2+1] =
1752 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1754 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1756 const int theHexTo2Prisms_FB_1[6*2+1] =
1758 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1760 const int theHexTo2Prisms_FB_2[6*2+1] =
1762 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1764 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1767 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1770 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1771 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1772 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1773 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1779 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1780 bool _baryNode; //!< additional node is to be created at cell barycenter
1781 bool _ownConn; //!< to delete _connectivity in destructor
1782 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1784 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1785 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1786 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1787 bool hasFacet( const TTriangleFacet& facet ) const
1789 if ( _nbCorners == 4 )
1791 const int* tetConn = _connectivity;
1792 for ( ; tetConn[0] >= 0; tetConn += 4 )
1793 if (( facet.contains( tetConn[0] ) +
1794 facet.contains( tetConn[1] ) +
1795 facet.contains( tetConn[2] ) +
1796 facet.contains( tetConn[3] )) == 3 )
1799 else // prism, _nbCorners == 6
1801 const int* prismConn = _connectivity;
1802 for ( ; prismConn[0] >= 0; prismConn += 6 )
1804 if (( facet.contains( prismConn[0] ) &&
1805 facet.contains( prismConn[1] ) &&
1806 facet.contains( prismConn[2] ))
1808 ( facet.contains( prismConn[3] ) &&
1809 facet.contains( prismConn[4] ) &&
1810 facet.contains( prismConn[5] )))
1818 //=======================================================================
1820 * \brief return TSplitMethod for the given element to split into tetrahedra
1822 //=======================================================================
1824 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1826 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1828 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1829 // an edge and a face barycenter; tertaherdons are based on triangles and
1830 // a volume barycenter
1831 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1833 // Find out how adjacent volumes are split
1835 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1836 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1837 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1839 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1840 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1841 if ( nbNodes < 4 ) continue;
1843 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1844 const int* nInd = vol.GetFaceNodesIndices( iF );
1847 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1848 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1849 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1850 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1854 int iCom = 0; // common node of triangle faces to split into
1855 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1857 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1858 nInd[ iQ * ( (iCom+1)%nbNodes )],
1859 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1860 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1861 nInd[ iQ * ( (iCom+2)%nbNodes )],
1862 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1863 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1865 triaSplits.push_back( t012 );
1866 triaSplits.push_back( t023 );
1871 if ( !triaSplits.empty() )
1872 hasAdjacentSplits = true;
1875 // Among variants of split method select one compliant with adjacent volumes
1877 TSplitMethod method;
1878 if ( !vol.Element()->IsPoly() && !is24TetMode )
1880 int nbVariants = 2, nbTet = 0;
1881 const int** connVariants = 0;
1882 switch ( vol.Element()->GetEntityType() )
1884 case SMDSEntity_Hexa:
1885 case SMDSEntity_Quad_Hexa:
1886 case SMDSEntity_TriQuad_Hexa:
1887 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1888 connVariants = theHexTo5, nbTet = 5;
1890 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1892 case SMDSEntity_Pyramid:
1893 case SMDSEntity_Quad_Pyramid:
1894 connVariants = thePyraTo2; nbTet = 2;
1896 case SMDSEntity_Penta:
1897 case SMDSEntity_Quad_Penta:
1898 case SMDSEntity_BiQuad_Penta:
1899 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1904 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1906 // check method compliancy with adjacent tetras,
1907 // all found splits must be among facets of tetras described by this method
1908 method = TSplitMethod( nbTet, connVariants[variant] );
1909 if ( hasAdjacentSplits && method._nbSplits > 0 )
1911 bool facetCreated = true;
1912 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1914 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1915 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1916 facetCreated = method.hasFacet( *facet );
1918 if ( !facetCreated )
1919 method = TSplitMethod(0); // incompatible method
1923 if ( method._nbSplits < 1 )
1925 // No standard method is applicable, use a generic solution:
1926 // each facet of a volume is split into triangles and
1927 // each of triangles and a volume barycenter form a tetrahedron.
1929 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1931 int* connectivity = new int[ maxTetConnSize + 1 ];
1932 method._connectivity = connectivity;
1933 method._ownConn = true;
1934 method._baryNode = !isHex27; // to create central node or not
1937 int baryCenInd = vol.NbNodes() - int( isHex27 );
1938 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1940 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1941 const int* nInd = vol.GetFaceNodesIndices( iF );
1942 // find common node of triangle facets of tetra to create
1943 int iCommon = 0; // index in linear numeration
1944 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1945 if ( !triaSplits.empty() )
1948 const TTriangleFacet* facet = &triaSplits.front();
1949 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1950 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1951 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1954 else if ( nbNodes > 3 && !is24TetMode )
1956 // find the best method of splitting into triangles by aspect ratio
1957 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1958 map< double, int > badness2iCommon;
1959 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1960 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1961 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1964 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1966 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1967 nodes[ iQ*((iLast-1)%nbNodes)],
1968 nodes[ iQ*((iLast )%nbNodes)]);
1969 badness += getBadRate( &tria, aspectRatio );
1971 badness2iCommon.insert( make_pair( badness, iCommon ));
1973 // use iCommon with lowest badness
1974 iCommon = badness2iCommon.begin()->second;
1976 if ( iCommon >= nbNodes )
1977 iCommon = 0; // something wrong
1979 // fill connectivity of tetrahedra based on a current face
1980 int nbTet = nbNodes - 2;
1981 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1986 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1987 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1991 method._faceBaryNode[ iF ] = 0;
1992 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1995 for ( int i = 0; i < nbTet; ++i )
1997 int i1 = i, i2 = (i+1) % nbNodes;
1998 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1999 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2000 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2001 connectivity[ connSize++ ] = faceBaryCenInd;
2002 connectivity[ connSize++ ] = baryCenInd;
2007 for ( int i = 0; i < nbTet; ++i )
2009 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2010 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2011 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2012 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2013 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2014 connectivity[ connSize++ ] = baryCenInd;
2017 method._nbSplits += nbTet;
2019 } // loop on volume faces
2021 connectivity[ connSize++ ] = -1;
2023 } // end of generic solution
2027 //=======================================================================
2029 * \brief return TSplitMethod to split haxhedron into prisms
2031 //=======================================================================
2033 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2034 const int methodFlags,
2035 const int facetToSplit)
2037 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2039 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2041 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2043 static TSplitMethod to4methods[4]; // order BT, LR, FB
2044 if ( to4methods[iF]._nbSplits == 0 )
2048 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2049 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2050 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2053 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2054 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2055 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2058 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2059 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2060 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2062 default: return to4methods[3];
2064 to4methods[iF]._nbSplits = 4;
2065 to4methods[iF]._nbCorners = 6;
2067 return to4methods[iF];
2069 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2071 TSplitMethod method;
2073 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2075 const int nbVariants = 2, nbSplits = 2;
2076 const int** connVariants = 0;
2078 case 0: connVariants = theHexTo2Prisms_BT; break;
2079 case 1: connVariants = theHexTo2Prisms_LR; break;
2080 case 2: connVariants = theHexTo2Prisms_FB; break;
2081 default: return method;
2084 // look for prisms adjacent via facetToSplit and an opposite one
2085 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2087 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2088 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2089 if ( nbNodes != 4 ) return method;
2091 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2092 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2093 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2095 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2097 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2102 // there are adjacent prism
2103 for ( int variant = 0; variant < nbVariants; ++variant )
2105 // check method compliancy with adjacent prisms,
2106 // the found prism facets must be among facets of prisms described by current method
2107 method._nbSplits = nbSplits;
2108 method._nbCorners = 6;
2109 method._connectivity = connVariants[ variant ];
2110 if ( method.hasFacet( *t ))
2115 // No adjacent prisms. Select a variant with a best aspect ratio.
2117 double badness[2] = { 0., 0. };
2118 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2119 const SMDS_MeshNode** nodes = vol.GetNodes();
2120 for ( int variant = 0; variant < nbVariants; ++variant )
2121 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2123 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2124 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2126 method._connectivity = connVariants[ variant ];
2127 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2128 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2129 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2131 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2134 badness[ variant ] += getBadRate( &tria, aspectRatio );
2136 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2138 method._nbSplits = nbSplits;
2139 method._nbCorners = 6;
2140 method._connectivity = connVariants[ iBetter ];
2145 //================================================================================
2147 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2149 //================================================================================
2151 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2152 const SMDSAbs_GeometryType geom ) const
2154 // find the tetrahedron including the three nodes of facet
2155 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2156 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2157 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2158 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2159 while ( volIt1->more() )
2161 const SMDS_MeshElement* v = volIt1->next();
2162 if ( v->GetGeomType() != geom )
2164 const int lastCornerInd = v->NbCornerNodes() - 1;
2165 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2166 continue; // medium node not allowed
2167 const int ind2 = v->GetNodeIndex( n2 );
2168 if ( ind2 < 0 || lastCornerInd < ind2 )
2170 const int ind3 = v->GetNodeIndex( n3 );
2171 if ( ind3 < 0 || lastCornerInd < ind3 )
2178 //=======================================================================
2180 * \brief A key of a face of volume
2182 //=======================================================================
2184 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2186 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2188 TIDSortedNodeSet sortedNodes;
2189 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2190 int nbNodes = vol.NbFaceNodes( iF );
2191 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2192 for ( int i = 0; i < nbNodes; i += iQ )
2193 sortedNodes.insert( fNodes[i] );
2194 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2195 first.first = (*(n++))->GetID();
2196 first.second = (*(n++))->GetID();
2197 second.first = (*(n++))->GetID();
2198 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2203 //=======================================================================
2204 //function : SplitVolumes
2205 //purpose : Split volume elements into tetrahedra or prisms.
2206 // If facet ID < 0, element is split into tetrahedra,
2207 // else a hexahedron is split into prisms so that the given facet is
2208 // split into triangles
2209 //=======================================================================
2211 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2212 const int theMethodFlags)
2214 SMDS_VolumeTool volTool;
2215 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2216 fHelper.ToFixNodeParameters( true );
2218 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2219 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2221 SMESH_SequenceOfElemPtr newNodes, newElems;
2223 // map face of volume to it's baricenrtic node
2224 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2226 vector<const SMDS_MeshElement* > splitVols;
2228 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2229 for ( ; elem2facet != theElems.end(); ++elem2facet )
2231 const SMDS_MeshElement* elem = elem2facet->first;
2232 const int facetToSplit = elem2facet->second;
2233 if ( elem->GetType() != SMDSAbs_Volume )
2235 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2236 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2239 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2241 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2242 getTetraSplitMethod( volTool, theMethodFlags ) :
2243 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2244 if ( splitMethod._nbSplits < 1 ) continue;
2246 // find submesh to add new tetras to
2247 if ( !subMesh || !subMesh->Contains( elem ))
2249 int shapeID = FindShape( elem );
2250 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2251 subMesh = GetMeshDS()->MeshElements( shapeID );
2254 if ( elem->IsQuadratic() )
2257 // add quadratic links to the helper
2258 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2260 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2261 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2262 for ( int iN = 0; iN < nbN; iN += iQ )
2263 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2265 helper.SetIsQuadratic( true );
2270 helper.SetIsQuadratic( false );
2272 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2273 volTool.GetNodes() + elem->NbNodes() );
2274 helper.SetElementsOnShape( true );
2275 if ( splitMethod._baryNode )
2277 // make a node at barycenter
2278 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2279 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2280 nodes.push_back( gcNode );
2281 newNodes.push_back( gcNode );
2283 if ( !splitMethod._faceBaryNode.empty() )
2285 // make or find baricentric nodes of faces
2286 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2287 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2289 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2290 volFace2BaryNode.insert
2291 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2294 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2295 newNodes.push_back( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2297 nodes.push_back( iF_n->second = f_n->second );
2302 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2303 const int* volConn = splitMethod._connectivity;
2304 if ( splitMethod._nbCorners == 4 ) // tetra
2305 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2306 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2307 nodes[ volConn[1] ],
2308 nodes[ volConn[2] ],
2309 nodes[ volConn[3] ]));
2311 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2312 newElems.push_back( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2313 nodes[ volConn[1] ],
2314 nodes[ volConn[2] ],
2315 nodes[ volConn[3] ],
2316 nodes[ volConn[4] ],
2317 nodes[ volConn[5] ]));
2319 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2321 // Split faces on sides of the split volume
2323 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2324 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2326 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2327 if ( nbNodes < 4 ) continue;
2329 // find an existing face
2330 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2331 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2332 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2333 /*noMedium=*/false))
2336 helper.SetElementsOnShape( false );
2337 vector< const SMDS_MeshElement* > triangles;
2339 // find submesh to add new triangles in
2340 if ( !fSubMesh || !fSubMesh->Contains( face ))
2342 int shapeID = FindShape( face );
2343 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2345 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2346 if ( iF_n != splitMethod._faceBaryNode.end() )
2348 const SMDS_MeshNode *baryNode = iF_n->second;
2349 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2351 const SMDS_MeshNode* n1 = fNodes[iN];
2352 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2353 const SMDS_MeshNode *n3 = baryNode;
2354 if ( !volTool.IsFaceExternal( iF ))
2356 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2358 if ( fSubMesh ) // update position of the bary node on geometry
2361 subMesh->RemoveNode( baryNode );
2362 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2363 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2364 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2366 fHelper.SetSubShape( s );
2367 gp_XY uv( 1e100, 1e100 );
2369 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2370 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2373 // node is too far from the surface
2374 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2375 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2376 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2383 // among possible triangles create ones described by split method
2384 const int* nInd = volTool.GetFaceNodesIndices( iF );
2385 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2386 int iCom = 0; // common node of triangle faces to split into
2387 list< TTriangleFacet > facets;
2388 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2390 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2391 nInd[ iQ * ( (iCom+1)%nbNodes )],
2392 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2393 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2394 nInd[ iQ * ( (iCom+2)%nbNodes )],
2395 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2396 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2398 facets.push_back( t012 );
2399 facets.push_back( t023 );
2400 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2401 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2402 nInd[ iQ * ((iLast-1)%nbNodes )],
2403 nInd[ iQ * ((iLast )%nbNodes )]));
2407 list< TTriangleFacet >::iterator facet = facets.begin();
2408 if ( facet == facets.end() )
2410 for ( ; facet != facets.end(); ++facet )
2412 if ( !volTool.IsFaceExternal( iF ))
2413 swap( facet->_n2, facet->_n3 );
2414 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2415 volNodes[ facet->_n2 ],
2416 volNodes[ facet->_n3 ]));
2419 for ( size_t i = 0; i < triangles.size(); ++i )
2421 if ( !triangles[ i ]) continue;
2423 fSubMesh->AddElement( triangles[ i ]);
2424 newElems.push_back( triangles[ i ]);
2426 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2427 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2429 } // while a face based on facet nodes exists
2430 } // loop on volume faces to split them into triangles
2432 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2434 if ( geomType == SMDSEntity_TriQuad_Hexa )
2436 // remove medium nodes that could become free
2437 for ( int i = 20; i < volTool.NbNodes(); ++i )
2438 if ( volNodes[i]->NbInverseElements() == 0 )
2439 GetMeshDS()->RemoveNode( volNodes[i] );
2441 } // loop on volumes to split
2443 myLastCreatedNodes = newNodes;
2444 myLastCreatedElems = newElems;
2447 //=======================================================================
2448 //function : GetHexaFacetsToSplit
2449 //purpose : For hexahedra that will be split into prisms, finds facets to
2450 // split into triangles. Only hexahedra adjacent to the one closest
2451 // to theFacetNormal.Location() are returned.
2452 //param [in,out] theHexas - the hexahedra
2453 //param [in] theFacetNormal - facet normal
2454 //param [out] theFacets - the hexahedra and found facet IDs
2455 //=======================================================================
2457 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2458 const gp_Ax1& theFacetNormal,
2459 TFacetOfElem & theFacets)
2461 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2463 // Find a hexa closest to the location of theFacetNormal
2465 const SMDS_MeshElement* startHex;
2467 // get SMDS_ElemIteratorPtr on theHexas
2468 typedef const SMDS_MeshElement* TValue;
2469 typedef TIDSortedElemSet::iterator TSetIterator;
2470 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2471 typedef SMDS_MeshElement::GeomFilter TFilter;
2472 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2473 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2474 ( new TElemSetIter( theHexas.begin(),
2476 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2478 SMESH_ElementSearcher* searcher =
2479 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2481 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2486 throw SALOME_Exception( THIS_METHOD "startHex not found");
2489 // Select a facet of startHex by theFacetNormal
2491 SMDS_VolumeTool vTool( startHex );
2492 double norm[3], dot, maxDot = 0;
2494 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2495 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2497 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2505 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2507 // Fill theFacets starting from facetID of startHex
2509 // facets used for searching of volumes adjacent to already treated ones
2510 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2511 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2512 TFacetMap facetsToCheck;
2514 set<const SMDS_MeshNode*> facetNodes;
2515 const SMDS_MeshElement* curHex;
2517 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2521 // move in two directions from startHex via facetID
2522 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2525 int curFacet = facetID;
2526 if ( is2nd ) // do not treat startHex twice
2528 vTool.Set( curHex );
2529 if ( vTool.IsFreeFace( curFacet, &curHex ))
2535 vTool.GetFaceNodes( curFacet, facetNodes );
2536 vTool.Set( curHex );
2537 curFacet = vTool.GetFaceIndex( facetNodes );
2542 // store a facet to split
2543 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2545 theFacets.insert( make_pair( curHex, -1 ));
2548 if ( !allHex && !theHexas.count( curHex ))
2551 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2552 theFacets.insert( make_pair( curHex, curFacet ));
2553 if ( !facetIt2isNew.second )
2556 // remember not-to-split facets in facetsToCheck
2557 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2558 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2560 if ( iF == curFacet && iF == oppFacet )
2562 TVolumeFaceKey facetKey ( vTool, iF );
2563 TElemFacets elemFacet( facetIt2isNew.first, iF );
2564 pair< TFacetMap::iterator, bool > it2isnew =
2565 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2566 if ( !it2isnew.second )
2567 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2569 // pass to a volume adjacent via oppFacet
2570 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2576 // get a new curFacet
2577 vTool.GetFaceNodes( oppFacet, facetNodes );
2578 vTool.Set( curHex );
2579 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2582 } // move in two directions from startHex via facetID
2584 // Find a new startHex by facetsToCheck
2588 TFacetMap::iterator fIt = facetsToCheck.begin();
2589 while ( !startHex && fIt != facetsToCheck.end() )
2591 const TElemFacets& elemFacets = fIt->second;
2592 const SMDS_MeshElement* hex = elemFacets.first->first;
2593 int splitFacet = elemFacets.first->second;
2594 int lateralFacet = elemFacets.second;
2595 facetsToCheck.erase( fIt );
2596 fIt = facetsToCheck.begin();
2599 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2600 curHex->GetGeomType() != SMDSGeom_HEXA )
2602 if ( !allHex && !theHexas.count( curHex ))
2607 // find a facet of startHex to split
2609 set<const SMDS_MeshNode*> lateralNodes;
2610 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2611 vTool.GetFaceNodes( splitFacet, facetNodes );
2612 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2613 vTool.Set( startHex );
2614 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2616 // look for a facet of startHex having common nodes with facetNodes
2617 // but not lateralFacet
2618 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2620 if ( iF == lateralFacet )
2622 int nbCommonNodes = 0;
2623 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2624 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2625 nbCommonNodes += facetNodes.count( nn[ iN ]);
2627 if ( nbCommonNodes >= 2 )
2634 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2636 } // while ( startHex )
2643 //================================================================================
2645 * \brief Selects nodes of several elements according to a given interlace
2646 * \param [in] srcNodes - nodes to select from
2647 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2648 * \param [in] interlace - indices of nodes for all elements
2649 * \param [in] nbElems - nb of elements
2650 * \param [in] nbNodes - nb of nodes in each element
2651 * \param [in] mesh - the mesh
2652 * \param [out] elemQueue - a list to push elements found by the selected nodes
2653 * \param [in] type - type of elements to look for
2655 //================================================================================
2657 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2658 vector< const SMDS_MeshNode* >* tgtNodesVec,
2659 const int* interlace,
2662 SMESHDS_Mesh* mesh = 0,
2663 list< const SMDS_MeshElement* >* elemQueue=0,
2664 SMDSAbs_ElementType type=SMDSAbs_All)
2666 for ( int iE = 0; iE < nbElems; ++iE )
2668 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2669 const int* select = & interlace[iE*nbNodes];
2670 elemNodes.resize( nbNodes );
2671 for ( int iN = 0; iN < nbNodes; ++iN )
2672 elemNodes[iN] = srcNodes[ select[ iN ]];
2674 const SMDS_MeshElement* e;
2676 for ( int iE = 0; iE < nbElems; ++iE )
2677 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2678 elemQueue->push_back( e );
2682 //=======================================================================
2684 * Split bi-quadratic elements into linear ones without creation of additional nodes
2685 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2686 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2687 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2688 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2689 * will be split in order to keep the mesh conformal.
2690 * \param elems - elements to split
2692 //=======================================================================
2694 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2696 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2697 vector<const SMDS_MeshElement* > splitElems;
2698 list< const SMDS_MeshElement* > elemQueue;
2699 list< const SMDS_MeshElement* >::iterator elemIt;
2701 SMESHDS_Mesh * mesh = GetMeshDS();
2702 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2703 int nbElems, nbNodes;
2705 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2706 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2709 elemQueue.push_back( *elemSetIt );
2710 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2712 const SMDS_MeshElement* elem = *elemIt;
2713 switch( elem->GetEntityType() )
2715 case SMDSEntity_TriQuad_Hexa: // HEX27
2717 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2718 nbElems = nbNodes = 8;
2719 elemType = & hexaType;
2721 // get nodes for new elements
2722 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2723 { 1,9,20,8, 17,22,26,21 },
2724 { 2,10,20,9, 18,23,26,22 },
2725 { 3,11,20,10, 19,24,26,23 },
2726 { 16,21,26,24, 4,12,25,15 },
2727 { 17,22,26,21, 5,13,25,12 },
2728 { 18,23,26,22, 6,14,25,13 },
2729 { 19,24,26,23, 7,15,25,14 }};
2730 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2732 // add boundary faces to elemQueue
2733 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2734 { 4,5,6,7, 12,13,14,15, 25 },
2735 { 0,1,5,4, 8,17,12,16, 21 },
2736 { 1,2,6,5, 9,18,13,17, 22 },
2737 { 2,3,7,6, 10,19,14,18, 23 },
2738 { 3,0,4,7, 11,16,15,19, 24 }};
2739 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2741 // add boundary segments to elemQueue
2742 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2743 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2744 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2745 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2748 case SMDSEntity_BiQuad_Triangle: // TRIA7
2750 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2753 elemType = & quadType;
2755 // get nodes for new elements
2756 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2757 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2759 // add boundary segments to elemQueue
2760 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2761 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2764 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2766 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2769 elemType = & quadType;
2771 // get nodes for new elements
2772 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2773 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2775 // add boundary segments to elemQueue
2776 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2777 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2780 case SMDSEntity_Quad_Edge:
2782 if ( elemIt == elemQueue.begin() )
2783 continue; // an elem is in theElems
2784 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2787 elemType = & segType;
2789 // get nodes for new elements
2790 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2791 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2795 } // switch( elem->GetEntityType() )
2797 // Create new elements
2799 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2803 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2804 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2805 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2806 //elemType->SetID( -1 );
2808 for ( int iE = 0; iE < nbElems; ++iE )
2809 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2812 ReplaceElemInGroups( elem, splitElems, mesh );
2815 for ( size_t i = 0; i < splitElems.size(); ++i )
2816 subMesh->AddElement( splitElems[i] );
2821 //=======================================================================
2822 //function : AddToSameGroups
2823 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2824 //=======================================================================
2826 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2827 const SMDS_MeshElement* elemInGroups,
2828 SMESHDS_Mesh * aMesh)
2830 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2831 if (!groups.empty()) {
2832 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2833 for ( ; grIt != groups.end(); grIt++ ) {
2834 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2835 if ( group && group->Contains( elemInGroups ))
2836 group->SMDSGroup().Add( elemToAdd );
2842 //=======================================================================
2843 //function : RemoveElemFromGroups
2844 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2845 //=======================================================================
2846 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2847 SMESHDS_Mesh * aMesh)
2849 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2850 if (!groups.empty())
2852 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2853 for (; GrIt != groups.end(); GrIt++)
2855 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2856 if (!grp || grp->IsEmpty()) continue;
2857 grp->SMDSGroup().Remove(removeelem);
2862 //================================================================================
2864 * \brief Replace elemToRm by elemToAdd in the all groups
2866 //================================================================================
2868 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2869 const SMDS_MeshElement* elemToAdd,
2870 SMESHDS_Mesh * aMesh)
2872 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2873 if (!groups.empty()) {
2874 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2875 for ( ; grIt != groups.end(); grIt++ ) {
2876 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2877 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2878 group->SMDSGroup().Add( elemToAdd );
2883 //================================================================================
2885 * \brief Replace elemToRm by elemToAdd in the all groups
2887 //================================================================================
2889 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2890 const vector<const SMDS_MeshElement*>& elemToAdd,
2891 SMESHDS_Mesh * aMesh)
2893 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2894 if (!groups.empty())
2896 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2897 for ( ; grIt != groups.end(); grIt++ ) {
2898 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2899 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2900 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2901 group->SMDSGroup().Add( elemToAdd[ i ] );
2906 //=======================================================================
2907 //function : QuadToTri
2908 //purpose : Cut quadrangles into triangles.
2909 // theCrit is used to select a diagonal to cut
2910 //=======================================================================
2912 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2913 const bool the13Diag)
2916 myLastCreatedElems.reserve( theElems.size() * 2 );
2918 SMESHDS_Mesh * aMesh = GetMeshDS();
2919 Handle(Geom_Surface) surface;
2920 SMESH_MesherHelper helper( *GetMesh() );
2922 TIDSortedElemSet::iterator itElem;
2923 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2925 const SMDS_MeshElement* elem = *itElem;
2926 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2929 if ( elem->NbNodes() == 4 ) {
2930 // retrieve element nodes
2931 const SMDS_MeshNode* aNodes [4];
2932 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2934 while ( itN->more() )
2935 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2937 int aShapeId = FindShape( elem );
2938 const SMDS_MeshElement* newElem1 = 0;
2939 const SMDS_MeshElement* newElem2 = 0;
2941 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2942 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2945 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2946 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2948 myLastCreatedElems.push_back(newElem1);
2949 myLastCreatedElems.push_back(newElem2);
2950 // put a new triangle on the same shape and add to the same groups
2953 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2954 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2956 AddToSameGroups( newElem1, elem, aMesh );
2957 AddToSameGroups( newElem2, elem, aMesh );
2958 aMesh->RemoveElement( elem );
2961 // Quadratic quadrangle
2963 else if ( elem->NbNodes() >= 8 )
2965 // get surface elem is on
2966 int aShapeId = FindShape( elem );
2967 if ( aShapeId != helper.GetSubShapeID() ) {
2971 shape = aMesh->IndexToShape( aShapeId );
2972 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2973 TopoDS_Face face = TopoDS::Face( shape );
2974 surface = BRep_Tool::Surface( face );
2975 if ( !surface.IsNull() )
2976 helper.SetSubShape( shape );
2980 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
2981 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2982 for ( int i = 0; itN->more(); ++i )
2983 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2985 const SMDS_MeshNode* centrNode = aNodes[8];
2986 if ( centrNode == 0 )
2988 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2989 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
2991 myLastCreatedNodes.push_back(centrNode);
2994 // create a new element
2995 const SMDS_MeshElement* newElem1 = 0;
2996 const SMDS_MeshElement* newElem2 = 0;
2998 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2999 aNodes[6], aNodes[7], centrNode );
3000 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3001 centrNode, aNodes[4], aNodes[5] );
3004 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3005 aNodes[7], aNodes[4], centrNode );
3006 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3007 centrNode, aNodes[5], aNodes[6] );
3009 myLastCreatedElems.push_back(newElem1);
3010 myLastCreatedElems.push_back(newElem2);
3011 // put a new triangle on the same shape and add to the same groups
3014 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3015 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3017 AddToSameGroups( newElem1, elem, aMesh );
3018 AddToSameGroups( newElem2, elem, aMesh );
3019 aMesh->RemoveElement( elem );
3026 //=======================================================================
3027 //function : getAngle
3029 //=======================================================================
3031 double getAngle(const SMDS_MeshElement * tr1,
3032 const SMDS_MeshElement * tr2,
3033 const SMDS_MeshNode * n1,
3034 const SMDS_MeshNode * n2)
3036 double angle = 2. * M_PI; // bad angle
3039 SMESH::Controls::TSequenceOfXYZ P1, P2;
3040 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3041 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3044 if(!tr1->IsQuadratic())
3045 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3047 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3048 if ( N1.SquareMagnitude() <= gp::Resolution() )
3050 if(!tr2->IsQuadratic())
3051 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3053 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3054 if ( N2.SquareMagnitude() <= gp::Resolution() )
3057 // find the first diagonal node n1 in the triangles:
3058 // take in account a diagonal link orientation
3059 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3060 for ( int t = 0; t < 2; t++ ) {
3061 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3062 int i = 0, iDiag = -1;
3063 while ( it->more()) {
3064 const SMDS_MeshElement *n = it->next();
3065 if ( n == n1 || n == n2 ) {
3069 if ( i - iDiag == 1 )
3070 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3079 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3082 angle = N1.Angle( N2 );
3087 // =================================================
3088 // class generating a unique ID for a pair of nodes
3089 // and able to return nodes by that ID
3090 // =================================================
3094 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3095 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3098 long GetLinkID (const SMDS_MeshNode * n1,
3099 const SMDS_MeshNode * n2) const
3101 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3104 bool GetNodes (const long theLinkID,
3105 const SMDS_MeshNode* & theNode1,
3106 const SMDS_MeshNode* & theNode2) const
3108 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3109 if ( !theNode1 ) return false;
3110 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3111 if ( !theNode2 ) return false;
3117 const SMESHDS_Mesh* myMesh;
3122 //=======================================================================
3123 //function : TriToQuad
3124 //purpose : Fuse neighbour triangles into quadrangles.
3125 // theCrit is used to select a neighbour to fuse with.
3126 // theMaxAngle is a max angle between element normals at which
3127 // fusion is still performed.
3128 //=======================================================================
3130 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3131 SMESH::Controls::NumericalFunctorPtr theCrit,
3132 const double theMaxAngle)
3135 myLastCreatedElems.reserve( theElems.size() / 2 );
3137 if ( !theCrit.get() )
3140 SMESHDS_Mesh * aMesh = GetMeshDS();
3142 // Prepare data for algo: build
3143 // 1. map of elements with their linkIDs
3144 // 2. map of linkIDs with their elements
3146 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3147 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3148 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3149 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3151 TIDSortedElemSet::iterator itElem;
3152 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3154 const SMDS_MeshElement* elem = *itElem;
3155 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3156 bool IsTria = ( elem->NbCornerNodes()==3 );
3157 if (!IsTria) continue;
3159 // retrieve element nodes
3160 const SMDS_MeshNode* aNodes [4];
3161 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3164 aNodes[ i++ ] = itN->next();
3165 aNodes[ 3 ] = aNodes[ 0 ];
3168 for ( i = 0; i < 3; i++ ) {
3169 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3170 // check if elements sharing a link can be fused
3171 itLE = mapLi_listEl.find( link );
3172 if ( itLE != mapLi_listEl.end() ) {
3173 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3175 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3176 //if ( FindShape( elem ) != FindShape( elem2 ))
3177 // continue; // do not fuse triangles laying on different shapes
3178 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3179 continue; // avoid making badly shaped quads
3180 (*itLE).second.push_back( elem );
3183 mapLi_listEl[ link ].push_back( elem );
3185 mapEl_setLi [ elem ].insert( link );
3188 // Clean the maps from the links shared by a sole element, ie
3189 // links to which only one element is bound in mapLi_listEl
3191 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3192 int nbElems = (*itLE).second.size();
3193 if ( nbElems < 2 ) {
3194 const SMDS_MeshElement* elem = (*itLE).second.front();
3195 SMESH_TLink link = (*itLE).first;
3196 mapEl_setLi[ elem ].erase( link );
3197 if ( mapEl_setLi[ elem ].empty() )
3198 mapEl_setLi.erase( elem );
3202 // Algo: fuse triangles into quadrangles
3204 while ( ! mapEl_setLi.empty() ) {
3205 // Look for the start element:
3206 // the element having the least nb of shared links
3207 const SMDS_MeshElement* startElem = 0;
3209 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3210 int nbLinks = (*itEL).second.size();
3211 if ( nbLinks < minNbLinks ) {
3212 startElem = (*itEL).first;
3213 minNbLinks = nbLinks;
3214 if ( minNbLinks == 1 )
3219 // search elements to fuse starting from startElem or links of elements
3220 // fused earlyer - startLinks
3221 list< SMESH_TLink > startLinks;
3222 while ( startElem || !startLinks.empty() ) {
3223 while ( !startElem && !startLinks.empty() ) {
3224 // Get an element to start, by a link
3225 SMESH_TLink linkId = startLinks.front();
3226 startLinks.pop_front();
3227 itLE = mapLi_listEl.find( linkId );
3228 if ( itLE != mapLi_listEl.end() ) {
3229 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3230 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3231 for ( ; itE != listElem.end() ; itE++ )
3232 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3234 mapLi_listEl.erase( itLE );
3239 // Get candidates to be fused
3240 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3241 const SMESH_TLink *link12 = 0, *link13 = 0;
3243 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3244 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3245 ASSERT( !setLi.empty() );
3246 set< SMESH_TLink >::iterator itLi;
3247 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3249 const SMESH_TLink & link = (*itLi);
3250 itLE = mapLi_listEl.find( link );
3251 if ( itLE == mapLi_listEl.end() )
3254 const SMDS_MeshElement* elem = (*itLE).second.front();
3256 elem = (*itLE).second.back();
3257 mapLi_listEl.erase( itLE );
3258 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3269 // add other links of elem to list of links to re-start from
3270 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3271 set< SMESH_TLink >::iterator it;
3272 for ( it = links.begin(); it != links.end(); it++ ) {
3273 const SMESH_TLink& link2 = (*it);
3274 if ( link2 != link )
3275 startLinks.push_back( link2 );
3279 // Get nodes of possible quadrangles
3280 const SMDS_MeshNode *n12 [4], *n13 [4];
3281 bool Ok12 = false, Ok13 = false;
3282 const SMDS_MeshNode *linkNode1, *linkNode2;
3284 linkNode1 = link12->first;
3285 linkNode2 = link12->second;
3286 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3290 linkNode1 = link13->first;
3291 linkNode2 = link13->second;
3292 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3296 // Choose a pair to fuse
3297 if ( Ok12 && Ok13 ) {
3298 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3299 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3300 double aBadRate12 = getBadRate( &quad12, theCrit );
3301 double aBadRate13 = getBadRate( &quad13, theCrit );
3302 if ( aBadRate13 < aBadRate12 )
3309 // and remove fused elems and remove links from the maps
3310 mapEl_setLi.erase( tr1 );
3313 mapEl_setLi.erase( tr2 );
3314 mapLi_listEl.erase( *link12 );
3315 if ( tr1->NbNodes() == 3 )
3317 const SMDS_MeshElement* newElem = 0;
3318 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3319 myLastCreatedElems.push_back(newElem);
3320 AddToSameGroups( newElem, tr1, aMesh );
3321 int aShapeId = tr1->getshapeId();
3323 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3324 aMesh->RemoveElement( tr1 );
3325 aMesh->RemoveElement( tr2 );
3328 vector< const SMDS_MeshNode* > N1;
3329 vector< const SMDS_MeshNode* > N2;
3330 getNodesFromTwoTria(tr1,tr2,N1,N2);
3331 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3332 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3333 // i.e. first nodes from both arrays form a new diagonal
3334 const SMDS_MeshNode* aNodes[8];
3343 const SMDS_MeshElement* newElem = 0;
3344 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3345 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3346 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3348 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3349 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3350 myLastCreatedElems.push_back(newElem);
3351 AddToSameGroups( newElem, tr1, aMesh );
3352 int aShapeId = tr1->getshapeId();
3354 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3355 aMesh->RemoveElement( tr1 );
3356 aMesh->RemoveElement( tr2 );
3357 // remove middle node (9)
3358 if ( N1[4]->NbInverseElements() == 0 )
3359 aMesh->RemoveNode( N1[4] );
3360 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3361 aMesh->RemoveNode( N1[6] );
3362 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3363 aMesh->RemoveNode( N2[6] );
3368 mapEl_setLi.erase( tr3 );
3369 mapLi_listEl.erase( *link13 );
3370 if ( tr1->NbNodes() == 3 ) {
3371 const SMDS_MeshElement* newElem = 0;
3372 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3373 myLastCreatedElems.push_back(newElem);
3374 AddToSameGroups( newElem, tr1, aMesh );
3375 int aShapeId = tr1->getshapeId();
3377 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3378 aMesh->RemoveElement( tr1 );
3379 aMesh->RemoveElement( tr3 );
3382 vector< const SMDS_MeshNode* > N1;
3383 vector< const SMDS_MeshNode* > N2;
3384 getNodesFromTwoTria(tr1,tr3,N1,N2);
3385 // now we receive following N1 and N2 (using numeration as above image)
3386 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3387 // i.e. first nodes from both arrays form a new diagonal
3388 const SMDS_MeshNode* aNodes[8];
3397 const SMDS_MeshElement* newElem = 0;
3398 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3399 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3400 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3402 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3403 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3404 myLastCreatedElems.push_back(newElem);
3405 AddToSameGroups( newElem, tr1, aMesh );
3406 int aShapeId = tr1->getshapeId();
3408 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3409 aMesh->RemoveElement( tr1 );
3410 aMesh->RemoveElement( tr3 );
3411 // remove middle node (9)
3412 if ( N1[4]->NbInverseElements() == 0 )
3413 aMesh->RemoveNode( N1[4] );
3414 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3415 aMesh->RemoveNode( N1[6] );
3416 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3417 aMesh->RemoveNode( N2[6] );
3421 // Next element to fuse: the rejected one
3423 startElem = Ok12 ? tr3 : tr2;
3425 } // if ( startElem )
3426 } // while ( startElem || !startLinks.empty() )
3427 } // while ( ! mapEl_setLi.empty() )
3432 //================================================================================
3434 * \brief Return nodes linked to the given one
3435 * \param theNode - the node
3436 * \param linkedNodes - the found nodes
3437 * \param type - the type of elements to check
3439 * Medium nodes are ignored
3441 //================================================================================
3443 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3444 TIDSortedElemSet & linkedNodes,
3445 SMDSAbs_ElementType type )
3447 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3448 while ( elemIt->more() )
3450 const SMDS_MeshElement* elem = elemIt->next();
3451 if(elem->GetType() == SMDSAbs_0DElement)
3454 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3455 if ( elem->GetType() == SMDSAbs_Volume )
3457 SMDS_VolumeTool vol( elem );
3458 while ( nodeIt->more() ) {
3459 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3460 if ( theNode != n && vol.IsLinked( theNode, n ))
3461 linkedNodes.insert( n );
3466 for ( int i = 0; nodeIt->more(); ++i ) {
3467 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3468 if ( n == theNode ) {
3469 int iBefore = i - 1;
3471 if ( elem->IsQuadratic() ) {
3472 int nb = elem->NbNodes() / 2;
3473 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3474 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3476 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3477 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3484 //=======================================================================
3485 //function : laplacianSmooth
3486 //purpose : pulls theNode toward the center of surrounding nodes directly
3487 // connected to that node along an element edge
3488 //=======================================================================
3490 void laplacianSmooth(const SMDS_MeshNode* theNode,
3491 const Handle(Geom_Surface)& theSurface,
3492 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3494 // find surrounding nodes
3496 TIDSortedElemSet nodeSet;
3497 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3499 // compute new coodrs
3501 double coord[] = { 0., 0., 0. };
3502 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3503 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3504 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3505 if ( theSurface.IsNull() ) { // smooth in 3D
3506 coord[0] += node->X();
3507 coord[1] += node->Y();
3508 coord[2] += node->Z();
3510 else { // smooth in 2D
3511 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3512 gp_XY* uv = theUVMap[ node ];
3513 coord[0] += uv->X();
3514 coord[1] += uv->Y();
3517 int nbNodes = nodeSet.size();
3520 coord[0] /= nbNodes;
3521 coord[1] /= nbNodes;
3523 if ( !theSurface.IsNull() ) {
3524 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3525 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3526 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3532 coord[2] /= nbNodes;
3536 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3539 //=======================================================================
3540 //function : centroidalSmooth
3541 //purpose : pulls theNode toward the element-area-weighted centroid of the
3542 // surrounding elements
3543 //=======================================================================
3545 void centroidalSmooth(const SMDS_MeshNode* theNode,
3546 const Handle(Geom_Surface)& theSurface,
3547 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3549 gp_XYZ aNewXYZ(0.,0.,0.);
3550 SMESH::Controls::Area anAreaFunc;
3551 double totalArea = 0.;
3556 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3557 while ( elemIt->more() )
3559 const SMDS_MeshElement* elem = elemIt->next();
3562 gp_XYZ elemCenter(0.,0.,0.);
3563 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3564 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3565 int nn = elem->NbNodes();
3566 if(elem->IsQuadratic()) nn = nn/2;
3568 //while ( itN->more() ) {
3570 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3572 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3573 aNodePoints.push_back( aP );
3574 if ( !theSurface.IsNull() ) { // smooth in 2D
3575 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3576 gp_XY* uv = theUVMap[ aNode ];
3577 aP.SetCoord( uv->X(), uv->Y(), 0. );
3581 double elemArea = anAreaFunc.GetValue( aNodePoints );
3582 totalArea += elemArea;
3584 aNewXYZ += elemCenter * elemArea;
3586 aNewXYZ /= totalArea;
3587 if ( !theSurface.IsNull() ) {
3588 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3589 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3594 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3597 //=======================================================================
3598 //function : getClosestUV
3599 //purpose : return UV of closest projection
3600 //=======================================================================
3602 static bool getClosestUV (Extrema_GenExtPS& projector,
3603 const gp_Pnt& point,
3606 projector.Perform( point );
3607 if ( projector.IsDone() ) {
3608 double u, v, minVal = DBL_MAX;
3609 for ( int i = projector.NbExt(); i > 0; i-- )
3610 if ( projector.SquareDistance( i ) < minVal ) {
3611 minVal = projector.SquareDistance( i );
3612 projector.Point( i ).Parameter( u, v );
3614 result.SetCoord( u, v );
3620 //=======================================================================
3622 //purpose : Smooth theElements during theNbIterations or until a worst
3623 // element has aspect ratio <= theTgtAspectRatio.
3624 // Aspect Ratio varies in range [1.0, inf].
3625 // If theElements is empty, the whole mesh is smoothed.
3626 // theFixedNodes contains additionally fixed nodes. Nodes built
3627 // on edges and boundary nodes are always fixed.
3628 //=======================================================================
3630 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3631 set<const SMDS_MeshNode*> & theFixedNodes,
3632 const SmoothMethod theSmoothMethod,
3633 const int theNbIterations,
3634 double theTgtAspectRatio,
3639 if ( theTgtAspectRatio < 1.0 )
3640 theTgtAspectRatio = 1.0;
3642 const double disttol = 1.e-16;
3644 SMESH::Controls::AspectRatio aQualityFunc;
3646 SMESHDS_Mesh* aMesh = GetMeshDS();
3648 if ( theElems.empty() ) {
3649 // add all faces to theElems
3650 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3651 while ( fIt->more() ) {
3652 const SMDS_MeshElement* face = fIt->next();
3653 theElems.insert( theElems.end(), face );
3656 // get all face ids theElems are on
3657 set< int > faceIdSet;
3658 TIDSortedElemSet::iterator itElem;
3660 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3661 int fId = FindShape( *itElem );
3662 // check that corresponding submesh exists and a shape is face
3664 faceIdSet.find( fId ) == faceIdSet.end() &&
3665 aMesh->MeshElements( fId )) {
3666 TopoDS_Shape F = aMesh->IndexToShape( fId );
3667 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3668 faceIdSet.insert( fId );
3671 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3673 // ===============================================
3674 // smooth elements on each TopoDS_Face separately
3675 // ===============================================
3677 SMESH_MesherHelper helper( *GetMesh() );
3679 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3680 for ( ; fId != faceIdSet.rend(); ++fId )
3682 // get face surface and submesh
3683 Handle(Geom_Surface) surface;
3684 SMESHDS_SubMesh* faceSubMesh = 0;
3687 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3688 bool isUPeriodic = false, isVPeriodic = false;
3691 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3692 surface = BRep_Tool::Surface( face );
3693 faceSubMesh = aMesh->MeshElements( *fId );
3694 fToler2 = BRep_Tool::Tolerance( face );
3695 fToler2 *= fToler2 * 10.;
3696 isUPeriodic = surface->IsUPeriodic();
3697 // if ( isUPeriodic )
3698 // surface->UPeriod();
3699 isVPeriodic = surface->IsVPeriodic();
3700 // if ( isVPeriodic )
3701 // surface->VPeriod();
3702 surface->Bounds( u1, u2, v1, v2 );
3703 helper.SetSubShape( face );
3705 // ---------------------------------------------------------
3706 // for elements on a face, find movable and fixed nodes and
3707 // compute UV for them
3708 // ---------------------------------------------------------
3709 bool checkBoundaryNodes = false;
3710 bool isQuadratic = false;
3711 set<const SMDS_MeshNode*> setMovableNodes;
3712 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3713 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3714 list< const SMDS_MeshElement* > elemsOnFace;
3716 Extrema_GenExtPS projector;
3717 GeomAdaptor_Surface surfAdaptor;
3718 if ( !surface.IsNull() ) {
3719 surfAdaptor.Load( surface );
3720 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3722 int nbElemOnFace = 0;
3723 itElem = theElems.begin();
3724 // loop on not yet smoothed elements: look for elems on a face
3725 while ( itElem != theElems.end() )
3727 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3728 break; // all elements found
3730 const SMDS_MeshElement* elem = *itElem;
3731 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3732 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3736 elemsOnFace.push_back( elem );
3737 theElems.erase( itElem++ );
3741 isQuadratic = elem->IsQuadratic();
3743 // get movable nodes of elem
3744 const SMDS_MeshNode* node;
3745 SMDS_TypeOfPosition posType;
3746 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3747 int nn = 0, nbn = elem->NbNodes();
3748 if(elem->IsQuadratic())
3750 while ( nn++ < nbn ) {
3751 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3752 const SMDS_PositionPtr& pos = node->GetPosition();
3753 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3754 if (posType != SMDS_TOP_EDGE &&
3755 posType != SMDS_TOP_VERTEX &&
3756 theFixedNodes.find( node ) == theFixedNodes.end())
3758 // check if all faces around the node are on faceSubMesh
3759 // because a node on edge may be bound to face
3761 if ( faceSubMesh ) {
3762 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3763 while ( eIt->more() && all ) {
3764 const SMDS_MeshElement* e = eIt->next();
3765 all = faceSubMesh->Contains( e );
3769 setMovableNodes.insert( node );
3771 checkBoundaryNodes = true;
3773 if ( posType == SMDS_TOP_3DSPACE )
3774 checkBoundaryNodes = true;
3777 if ( surface.IsNull() )
3780 // get nodes to check UV
3781 list< const SMDS_MeshNode* > uvCheckNodes;
3782 const SMDS_MeshNode* nodeInFace = 0;
3783 itN = elem->nodesIterator();
3784 nn = 0; nbn = elem->NbNodes();
3785 if(elem->IsQuadratic())
3787 while ( nn++ < nbn ) {
3788 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3789 if ( node->GetPosition()->GetDim() == 2 )
3791 if ( uvMap.find( node ) == uvMap.end() )
3792 uvCheckNodes.push_back( node );
3793 // add nodes of elems sharing node
3794 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3795 // while ( eIt->more() ) {
3796 // const SMDS_MeshElement* e = eIt->next();
3797 // if ( e != elem ) {
3798 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3799 // while ( nIt->more() ) {
3800 // const SMDS_MeshNode* n =
3801 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3802 // if ( uvMap.find( n ) == uvMap.end() )
3803 // uvCheckNodes.push_back( n );
3809 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3810 for ( ; n != uvCheckNodes.end(); ++n ) {
3813 const SMDS_PositionPtr& pos = node->GetPosition();
3814 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3818 bool toCheck = true;
3819 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3821 // compute not existing UV
3822 bool project = ( posType == SMDS_TOP_3DSPACE );
3823 // double dist1 = DBL_MAX, dist2 = 0;
3824 // if ( posType != SMDS_TOP_3DSPACE ) {
3825 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3826 // project = dist1 > fToler2;
3828 if ( project ) { // compute new UV
3830 gp_Pnt pNode = SMESH_NodeXYZ( node );
3831 if ( !getClosestUV( projector, pNode, newUV )) {
3832 MESSAGE("Node Projection Failed " << node);
3836 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3838 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3840 // if ( posType != SMDS_TOP_3DSPACE )
3841 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3842 // if ( dist2 < dist1 )
3846 // store UV in the map
3847 listUV.push_back( uv );
3848 uvMap.insert( make_pair( node, &listUV.back() ));
3850 } // loop on not yet smoothed elements
3852 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3853 checkBoundaryNodes = true;
3855 // fix nodes on mesh boundary
3857 if ( checkBoundaryNodes ) {
3858 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3859 map< SMESH_TLink, int >::iterator link_nb;
3860 // put all elements links to linkNbMap
3861 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3862 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3863 const SMDS_MeshElement* elem = (*elemIt);
3864 int nbn = elem->NbCornerNodes();
3865 // loop on elem links: insert them in linkNbMap
3866 for ( int iN = 0; iN < nbn; ++iN ) {
3867 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3868 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3869 SMESH_TLink link( n1, n2 );
3870 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3874 // remove nodes that are in links encountered only once from setMovableNodes
3875 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3876 if ( link_nb->second == 1 ) {
3877 setMovableNodes.erase( link_nb->first.node1() );
3878 setMovableNodes.erase( link_nb->first.node2() );
3883 // -----------------------------------------------------
3884 // for nodes on seam edge, compute one more UV ( uvMap2 );
3885 // find movable nodes linked to nodes on seam and which
3886 // are to be smoothed using the second UV ( uvMap2 )
3887 // -----------------------------------------------------
3889 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3890 if ( !surface.IsNull() ) {
3891 TopExp_Explorer eExp( face, TopAbs_EDGE );
3892 for ( ; eExp.More(); eExp.Next() ) {
3893 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3894 if ( !BRep_Tool::IsClosed( edge, face ))
3896 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3897 if ( !sm ) continue;
3898 // find out which parameter varies for a node on seam
3901 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3902 if ( pcurve.IsNull() ) continue;
3903 uv1 = pcurve->Value( f );
3905 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3906 if ( pcurve.IsNull() ) continue;
3907 uv2 = pcurve->Value( f );
3908 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3910 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
3911 std::swap( uv1, uv2 );
3912 // get nodes on seam and its vertices
3913 list< const SMDS_MeshNode* > seamNodes;
3914 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3915 while ( nSeamIt->more() ) {
3916 const SMDS_MeshNode* node = nSeamIt->next();
3917 if ( !isQuadratic || !IsMedium( node ))
3918 seamNodes.push_back( node );
3920 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3921 for ( ; vExp.More(); vExp.Next() ) {
3922 sm = aMesh->MeshElements( vExp.Current() );
3924 nSeamIt = sm->GetNodes();
3925 while ( nSeamIt->more() )
3926 seamNodes.push_back( nSeamIt->next() );
3929 // loop on nodes on seam
3930 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3931 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3932 const SMDS_MeshNode* nSeam = *noSeIt;
3933 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3934 if ( n_uv == uvMap.end() )
3937 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3938 // set the second UV
3939 listUV.push_back( *n_uv->second );
3940 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3941 if ( uvMap2.empty() )
3942 uvMap2 = uvMap; // copy the uvMap contents
3943 uvMap2[ nSeam ] = &listUV.back();
3945 // collect movable nodes linked to ones on seam in nodesNearSeam
3946 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3947 while ( eIt->more() ) {
3948 const SMDS_MeshElement* e = eIt->next();
3949 int nbUseMap1 = 0, nbUseMap2 = 0;
3950 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3951 int nn = 0, nbn = e->NbNodes();
3952 if(e->IsQuadratic()) nbn = nbn/2;
3953 while ( nn++ < nbn )
3955 const SMDS_MeshNode* n =
3956 static_cast<const SMDS_MeshNode*>( nIt->next() );
3958 setMovableNodes.find( n ) == setMovableNodes.end() )
3960 // add only nodes being closer to uv2 than to uv1
3961 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3962 // 0.5 * ( n->Y() + nSeam->Y() ),
3963 // 0.5 * ( n->Z() + nSeam->Z() ));
3965 // getClosestUV( projector, pMid, uv );
3966 double x = uvMap[ n ]->Coord( iPar );
3967 if ( Abs( uv1.Coord( iPar ) - x ) >
3968 Abs( uv2.Coord( iPar ) - x )) {
3969 nodesNearSeam.insert( n );
3975 // for centroidalSmooth all element nodes must
3976 // be on one side of a seam
3977 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3978 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3980 while ( nn++ < nbn ) {
3981 const SMDS_MeshNode* n =
3982 static_cast<const SMDS_MeshNode*>( nIt->next() );
3983 setMovableNodes.erase( n );
3987 } // loop on nodes on seam
3988 } // loop on edge of a face
3989 } // if ( !face.IsNull() )
3991 if ( setMovableNodes.empty() ) {
3992 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3993 continue; // goto next face
4001 double maxRatio = -1., maxDisplacement = -1.;
4002 set<const SMDS_MeshNode*>::iterator nodeToMove;
4003 for ( it = 0; it < theNbIterations; it++ ) {
4004 maxDisplacement = 0.;
4005 nodeToMove = setMovableNodes.begin();
4006 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4007 const SMDS_MeshNode* node = (*nodeToMove);
4008 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4011 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4012 if ( theSmoothMethod == LAPLACIAN )
4013 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4015 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4017 // node displacement
4018 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4019 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4020 if ( aDispl > maxDisplacement )
4021 maxDisplacement = aDispl;
4023 // no node movement => exit
4024 //if ( maxDisplacement < 1.e-16 ) {
4025 if ( maxDisplacement < disttol ) {
4026 MESSAGE("-- no node movement --");
4030 // check elements quality
4032 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4033 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4034 const SMDS_MeshElement* elem = (*elemIt);
4035 if ( !elem || elem->GetType() != SMDSAbs_Face )
4037 SMESH::Controls::TSequenceOfXYZ aPoints;
4038 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4039 double aValue = aQualityFunc.GetValue( aPoints );
4040 if ( aValue > maxRatio )
4044 if ( maxRatio <= theTgtAspectRatio ) {
4045 //MESSAGE("-- quality achieved --");
4048 if (it+1 == theNbIterations) {
4049 //MESSAGE("-- Iteration limit exceeded --");
4051 } // smoothing iterations
4053 // MESSAGE(" Face id: " << *fId <<
4054 // " Nb iterstions: " << it <<
4055 // " Displacement: " << maxDisplacement <<
4056 // " Aspect Ratio " << maxRatio);
4058 // ---------------------------------------
4059 // new nodes positions are computed,
4060 // record movement in DS and set new UV
4061 // ---------------------------------------
4062 nodeToMove = setMovableNodes.begin();
4063 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4064 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4065 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4066 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4067 if ( node_uv != uvMap.end() ) {
4068 gp_XY* uv = node_uv->second;
4070 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4074 // move medium nodes of quadratic elements
4077 vector<const SMDS_MeshNode*> nodes;
4079 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4080 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4082 const SMDS_MeshElement* QF = *elemIt;
4083 if ( QF->IsQuadratic() )
4085 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesIterator() ),
4086 SMDS_MeshElement::iterator() );
4087 nodes.push_back( nodes[0] );
4089 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4091 if ( !surface.IsNull() )
4093 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4094 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4095 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4096 xyz = surface->Value( uv.X(), uv.Y() );
4099 xyz = 0.5 * ( SMESH_NodeXYZ( nodes[i-1] ) + SMESH_NodeXYZ( nodes[i+1] ));
4101 if (( SMESH_NodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4102 // we have to move a medium node
4103 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4109 } // loop on face ids
4115 //=======================================================================
4116 //function : isReverse
4117 //purpose : Return true if normal of prevNodes is not co-directied with
4118 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4119 // iNotSame is where prevNodes and nextNodes are different.
4120 // If result is true then future volume orientation is OK
4121 //=======================================================================
4123 bool isReverse(const SMDS_MeshElement* face,
4124 const vector<const SMDS_MeshNode*>& prevNodes,
4125 const vector<const SMDS_MeshNode*>& nextNodes,
4129 SMESH_NodeXYZ pP = prevNodes[ iNotSame ];
4130 SMESH_NodeXYZ pN = nextNodes[ iNotSame ];
4131 gp_XYZ extrDir( pN - pP ), faceNorm;
4132 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4134 return faceNorm * extrDir < 0.0;
4137 //================================================================================
4139 * \brief Assure that theElemSets[0] holds elements, not nodes
4141 //================================================================================
4143 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4145 if ( !theElemSets[0].empty() &&
4146 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4148 std::swap( theElemSets[0], theElemSets[1] );
4150 else if ( !theElemSets[1].empty() &&
4151 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4153 std::swap( theElemSets[0], theElemSets[1] );
4158 //=======================================================================
4160 * \brief Create elements by sweeping an element
4161 * \param elem - element to sweep
4162 * \param newNodesItVec - nodes generated from each node of the element
4163 * \param newElems - generated elements
4164 * \param nbSteps - number of sweeping steps
4165 * \param srcElements - to append elem for each generated element
4167 //=======================================================================
4169 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4170 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4171 list<const SMDS_MeshElement*>& newElems,
4172 const size_t nbSteps,
4173 SMESH_SequenceOfElemPtr& srcElements)
4175 SMESHDS_Mesh* aMesh = GetMeshDS();
4177 const int nbNodes = elem->NbNodes();
4178 const int nbCorners = elem->NbCornerNodes();
4179 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4180 polyhedron creation !!! */
4181 // Loop on elem nodes:
4182 // find new nodes and detect same nodes indices
4183 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4184 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4185 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4186 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4188 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4189 vector<int> sames(nbNodes);
4190 vector<bool> isSingleNode(nbNodes);
4192 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4193 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4194 const SMDS_MeshNode* node = nnIt->first;
4195 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4196 if ( listNewNodes.empty() )
4199 itNN [ iNode ] = listNewNodes.begin();
4200 prevNod[ iNode ] = node;
4201 nextNod[ iNode ] = listNewNodes.front();
4203 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4204 corner node of linear */
4205 if ( prevNod[ iNode ] != nextNod [ iNode ])
4206 nbDouble += !isSingleNode[iNode];
4208 if( iNode < nbCorners ) { // check corners only
4209 if ( prevNod[ iNode ] == nextNod [ iNode ])
4210 sames[nbSame++] = iNode;
4212 iNotSameNode = iNode;
4216 if ( nbSame == nbNodes || nbSame > 2) {
4217 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4221 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4223 // fix nodes order to have bottom normal external
4224 if ( baseType == SMDSEntity_Polygon )
4226 std::reverse( itNN.begin(), itNN.end() );
4227 std::reverse( prevNod.begin(), prevNod.end() );
4228 std::reverse( midlNod.begin(), midlNod.end() );
4229 std::reverse( nextNod.begin(), nextNod.end() );
4230 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4234 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4235 SMDS_MeshCell::applyInterlace( ind, itNN );
4236 SMDS_MeshCell::applyInterlace( ind, prevNod );
4237 SMDS_MeshCell::applyInterlace( ind, nextNod );
4238 SMDS_MeshCell::applyInterlace( ind, midlNod );
4239 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4242 sames[nbSame] = iNotSameNode;
4243 for ( int j = 0; j <= nbSame; ++j )
4244 for ( size_t i = 0; i < ind.size(); ++i )
4245 if ( ind[i] == sames[j] )
4250 iNotSameNode = sames[nbSame];
4254 else if ( elem->GetType() == SMDSAbs_Edge )
4256 // orient a new face same as adjacent one
4258 const SMDS_MeshElement* e;
4259 TIDSortedElemSet dummy;
4260 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4261 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4262 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4264 // there is an adjacent face, check order of nodes in it
4265 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4268 std::swap( itNN[0], itNN[1] );
4269 std::swap( prevNod[0], prevNod[1] );
4270 std::swap( nextNod[0], nextNod[1] );
4271 std::swap( isSingleNode[0], isSingleNode[1] );
4273 sames[0] = 1 - sames[0];
4274 iNotSameNode = 1 - iNotSameNode;
4279 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4281 iSameNode = sames[ nbSame-1 ];
4282 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4283 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4284 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4287 if ( baseType == SMDSEntity_Polygon )
4289 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4290 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4292 else if ( baseType == SMDSEntity_Quad_Polygon )
4294 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4295 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4298 // make new elements
4299 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4302 for ( iNode = 0; iNode < nbNodes; iNode++ )
4304 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4305 nextNod[ iNode ] = *itNN[ iNode ]++;
4308 SMDS_MeshElement* aNewElem = 0;
4309 /*if(!elem->IsPoly())*/ {
4310 switch ( baseType ) {
4312 case SMDSEntity_Node: { // sweep NODE
4313 if ( nbSame == 0 ) {
4314 if ( isSingleNode[0] )
4315 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4317 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4323 case SMDSEntity_Edge: { // sweep EDGE
4324 if ( nbDouble == 0 )
4326 if ( nbSame == 0 ) // ---> quadrangle
4327 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4328 nextNod[ 1 ], nextNod[ 0 ] );
4329 else // ---> triangle
4330 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4331 nextNod[ iNotSameNode ] );
4333 else // ---> polygon
4335 vector<const SMDS_MeshNode*> poly_nodes;
4336 poly_nodes.push_back( prevNod[0] );
4337 poly_nodes.push_back( prevNod[1] );
4338 if ( prevNod[1] != nextNod[1] )
4340 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4341 poly_nodes.push_back( nextNod[1] );
4343 if ( prevNod[0] != nextNod[0] )
4345 poly_nodes.push_back( nextNod[0] );
4346 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4348 switch ( poly_nodes.size() ) {
4350 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4353 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4354 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4357 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4362 case SMDSEntity_Triangle: // TRIANGLE --->
4364 if ( nbDouble > 0 ) break;
4365 if ( nbSame == 0 ) // ---> pentahedron
4366 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4367 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4369 else if ( nbSame == 1 ) // ---> pyramid
4370 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4371 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4372 nextNod[ iSameNode ]);
4374 else // 2 same nodes: ---> tetrahedron
4375 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4376 nextNod[ iNotSameNode ]);
4379 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4383 if ( nbDouble+nbSame == 2 )
4385 if(nbSame==0) { // ---> quadratic quadrangle
4386 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4387 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4389 else { //(nbSame==1) // ---> quadratic triangle
4391 return; // medium node on axis
4393 else if(sames[0]==0)
4394 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4395 prevNod[2], midlNod[1], nextNod[2] );
4397 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4398 prevNod[2], nextNod[2], midlNod[0]);
4401 else if ( nbDouble == 3 )
4403 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4404 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4405 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4412 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4413 if ( nbDouble > 0 ) break;
4415 if ( nbSame == 0 ) // ---> hexahedron
4416 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4417 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4419 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4420 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4421 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4422 nextNod[ iSameNode ]);
4423 newElems.push_back( aNewElem );
4424 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4425 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4426 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4428 else if ( nbSame == 2 ) { // ---> pentahedron
4429 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4430 // iBeforeSame is same too
4431 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4432 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4433 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4435 // iAfterSame is same too
4436 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4437 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4438 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4442 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4443 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4444 if ( nbDouble+nbSame != 3 ) break;
4446 // ---> pentahedron with 15 nodes
4447 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4448 nextNod[0], nextNod[1], nextNod[2],
4449 prevNod[3], prevNod[4], prevNod[5],
4450 nextNod[3], nextNod[4], nextNod[5],
4451 midlNod[0], midlNod[1], midlNod[2]);
4453 else if(nbSame==1) {
4454 // ---> 2d order pyramid of 13 nodes
4455 int apex = iSameNode;
4456 int i0 = ( apex + 1 ) % nbCorners;
4457 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4461 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4462 nextNod[i0], nextNod[i1], prevNod[apex],
4463 prevNod[i01], midlNod[i0],
4464 nextNod[i01], midlNod[i1],
4465 prevNod[i1a], prevNod[i0a],
4466 nextNod[i0a], nextNod[i1a]);
4468 else if(nbSame==2) {
4469 // ---> 2d order tetrahedron of 10 nodes
4470 int n1 = iNotSameNode;
4471 int n2 = ( n1 + 1 ) % nbCorners;
4472 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4476 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4477 prevNod[n12], prevNod[n23], prevNod[n31],
4478 midlNod[n1], nextNod[n12], nextNod[n31]);
4482 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4484 if ( nbDouble != 4 ) break;
4485 // ---> hexahedron with 20 nodes
4486 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4487 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4488 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4489 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4490 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4492 else if(nbSame==1) {
4493 // ---> pyramid + pentahedron - can not be created since it is needed
4494 // additional middle node at the center of face
4495 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4498 else if( nbSame == 2 ) {
4499 if ( nbDouble != 2 ) break;
4500 // ---> 2d order Pentahedron with 15 nodes
4502 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4503 // iBeforeSame is same too
4510 // iAfterSame is same too
4520 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4521 prevNod[n4], prevNod[n5], nextNod[n5],
4522 prevNod[n12], midlNod[n2], nextNod[n12],
4523 prevNod[n45], midlNod[n5], nextNod[n45],
4524 prevNod[n14], prevNod[n25], nextNod[n25]);
4528 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4530 if( nbSame == 0 && nbDouble == 9 ) {
4531 // ---> tri-quadratic hexahedron with 27 nodes
4532 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4533 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4534 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4535 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4536 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4537 prevNod[8], // bottom center
4538 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4539 nextNod[8], // top center
4540 midlNod[8]);// elem center
4548 case SMDSEntity_Polygon: { // sweep POLYGON
4550 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4551 // ---> hexagonal prism
4552 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4553 prevNod[3], prevNod[4], prevNod[5],
4554 nextNod[0], nextNod[1], nextNod[2],
4555 nextNod[3], nextNod[4], nextNod[5]);
4559 case SMDSEntity_Ball:
4564 } // switch ( baseType )
4567 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4569 if ( baseType != SMDSEntity_Polygon )
4571 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4572 SMDS_MeshCell::applyInterlace( ind, prevNod );
4573 SMDS_MeshCell::applyInterlace( ind, nextNod );
4574 SMDS_MeshCell::applyInterlace( ind, midlNod );
4575 SMDS_MeshCell::applyInterlace( ind, itNN );
4576 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4577 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4579 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4580 vector<int> quantities (nbNodes + 2);
4581 polyedre_nodes.clear();
4585 for (int inode = 0; inode < nbNodes; inode++)
4586 polyedre_nodes.push_back( prevNod[inode] );
4587 quantities.push_back( nbNodes );
4590 polyedre_nodes.push_back( nextNod[0] );
4591 for (int inode = nbNodes; inode-1; --inode )
4592 polyedre_nodes.push_back( nextNod[inode-1] );
4593 quantities.push_back( nbNodes );
4601 const int iQuad = elem->IsQuadratic();
4602 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4604 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4605 int inextface = (iface+1+iQuad) % nbNodes;
4606 int imid = (iface+1) % nbNodes;
4607 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4608 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4609 polyedre_nodes.push_back( prevNod[iface] ); // 1
4610 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4612 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4613 polyedre_nodes.push_back( nextNod[iface] ); // 2
4615 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4616 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4618 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4619 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4621 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4622 if ( nbFaceNodes > 2 )
4623 quantities.push_back( nbFaceNodes );
4624 else // degenerated face
4625 polyedre_nodes.resize( prevNbNodes );
4627 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4629 } // try to create a polyherdal prism
4632 newElems.push_back( aNewElem );
4633 myLastCreatedElems.push_back(aNewElem);
4634 srcElements.push_back( elem );
4637 // set new prev nodes
4638 for ( iNode = 0; iNode < nbNodes; iNode++ )
4639 prevNod[ iNode ] = nextNod[ iNode ];
4644 //=======================================================================
4646 * \brief Create 1D and 2D elements around swept elements
4647 * \param mapNewNodes - source nodes and ones generated from them
4648 * \param newElemsMap - source elements and ones generated from them
4649 * \param elemNewNodesMap - nodes generated from each node of each element
4650 * \param elemSet - all swept elements
4651 * \param nbSteps - number of sweeping steps
4652 * \param srcElements - to append elem for each generated element
4654 //=======================================================================
4656 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4657 TTElemOfElemListMap & newElemsMap,
4658 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4659 TIDSortedElemSet& elemSet,
4661 SMESH_SequenceOfElemPtr& srcElements)
4663 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4664 SMESHDS_Mesh* aMesh = GetMeshDS();
4666 // Find nodes belonging to only one initial element - sweep them into edges.
4668 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4669 for ( ; nList != mapNewNodes.end(); nList++ )
4671 const SMDS_MeshNode* node =
4672 static_cast<const SMDS_MeshNode*>( nList->first );
4673 if ( newElemsMap.count( node ))
4674 continue; // node was extruded into edge
4675 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4676 int nbInitElems = 0;
4677 const SMDS_MeshElement* el = 0;
4678 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4679 while ( eIt->more() && nbInitElems < 2 ) {
4680 const SMDS_MeshElement* e = eIt->next();
4681 SMDSAbs_ElementType type = e->GetType();
4682 if ( type == SMDSAbs_Volume ||
4686 if ( type > highType ) {
4693 if ( nbInitElems == 1 ) {
4694 bool NotCreateEdge = el && el->IsMediumNode(node);
4695 if(!NotCreateEdge) {
4696 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4697 list<const SMDS_MeshElement*> newEdges;
4698 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4703 // Make a ceiling for each element ie an equal element of last new nodes.
4704 // Find free links of faces - make edges and sweep them into faces.
4706 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4708 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4709 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4710 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4712 const SMDS_MeshElement* elem = itElem->first;
4713 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4715 if(itElem->second.size()==0) continue;
4717 const bool isQuadratic = elem->IsQuadratic();
4719 if ( elem->GetType() == SMDSAbs_Edge ) {
4720 // create a ceiling edge
4721 if ( !isQuadratic ) {
4722 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4723 vecNewNodes[ 1 ]->second.back())) {
4724 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4725 vecNewNodes[ 1 ]->second.back()));
4726 srcElements.push_back( elem );
4730 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4731 vecNewNodes[ 1 ]->second.back(),
4732 vecNewNodes[ 2 ]->second.back())) {
4733 myLastCreatedElems.push_back(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4734 vecNewNodes[ 1 ]->second.back(),
4735 vecNewNodes[ 2 ]->second.back()));
4736 srcElements.push_back( elem );
4740 if ( elem->GetType() != SMDSAbs_Face )
4743 bool hasFreeLinks = false;
4745 TIDSortedElemSet avoidSet;
4746 avoidSet.insert( elem );
4748 set<const SMDS_MeshNode*> aFaceLastNodes;
4749 int iNode, nbNodes = vecNewNodes.size();
4750 if ( !isQuadratic ) {
4751 // loop on the face nodes
4752 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4753 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4754 // look for free links of the face
4755 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4756 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4757 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4758 // check if a link n1-n2 is free
4759 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4760 hasFreeLinks = true;
4761 // make a new edge and a ceiling for a new edge
4762 const SMDS_MeshElement* edge;
4763 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4764 myLastCreatedElems.push_back( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4765 srcElements.push_back( myLastCreatedElems.back() );
4767 n1 = vecNewNodes[ iNode ]->second.back();
4768 n2 = vecNewNodes[ iNext ]->second.back();
4769 if ( !aMesh->FindEdge( n1, n2 )) {
4770 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4771 srcElements.push_back( edge );
4776 else { // elem is quadratic face
4777 int nbn = nbNodes/2;
4778 for ( iNode = 0; iNode < nbn; iNode++ ) {
4779 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4780 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4781 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4782 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4783 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4784 // check if a link is free
4785 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4786 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4787 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4788 hasFreeLinks = true;
4789 // make an edge and a ceiling for a new edge
4791 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4792 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4793 srcElements.push_back( elem );
4795 n1 = vecNewNodes[ iNode ]->second.back();
4796 n2 = vecNewNodes[ iNext ]->second.back();
4797 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4798 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4799 myLastCreatedElems.push_back(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4800 srcElements.push_back( elem );
4804 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4805 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4809 // sweep free links into faces
4811 if ( hasFreeLinks ) {
4812 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4813 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4815 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4816 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4817 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4818 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4819 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4821 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4822 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4823 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4825 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4826 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4827 std::advance( v, volNb );
4828 // find indices of free faces of a volume and their source edges
4829 list< int > freeInd;
4830 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4831 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4832 int iF, nbF = vTool.NbFaces();
4833 for ( iF = 0; iF < nbF; iF ++ ) {
4834 if ( vTool.IsFreeFace( iF ) &&
4835 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4836 initNodeSet != faceNodeSet) // except an initial face
4838 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4840 if ( faceNodeSet == initNodeSetNoCenter )
4842 freeInd.push_back( iF );
4843 // find source edge of a free face iF
4844 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4845 vector<const SMDS_MeshNode*>::iterator lastCommom;
4846 commonNodes.resize( nbNodes, 0 );
4847 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4848 initNodeSet.begin(), initNodeSet.end(),
4849 commonNodes.begin());
4850 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4851 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4853 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4855 if ( !srcEdges.back() )
4857 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4858 << iF << " of volume #" << vTool.ID() << endl;
4863 if ( freeInd.empty() )
4866 // create wall faces for all steps;
4867 // if such a face has been already created by sweep of edge,
4868 // assure that its orientation is OK
4869 for ( int iStep = 0; iStep < nbSteps; iStep++ )
4871 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4872 vTool.SetExternalNormal();
4873 const int nextShift = vTool.IsForward() ? +1 : -1;
4874 list< int >::iterator ind = freeInd.begin();
4875 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4876 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4878 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4879 int nbn = vTool.NbFaceNodes( *ind );
4880 const SMDS_MeshElement * f = 0;
4881 if ( nbn == 3 ) ///// triangle
4883 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4885 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4887 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4889 nodes[ 1 + nextShift ] };
4891 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4893 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4897 else if ( nbn == 4 ) ///// quadrangle
4899 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4901 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4903 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4904 nodes[ 2 ], nodes[ 2+nextShift ] };
4906 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4908 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4909 newOrder[ 2 ], newOrder[ 3 ]));
4912 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4914 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4916 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4918 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4920 nodes[2 + 2*nextShift],
4921 nodes[3 - 2*nextShift],
4923 nodes[3 + 2*nextShift]};
4925 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4927 myLastCreatedElems.push_back(aMesh->AddFace( newOrder[ 0 ],
4935 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4937 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4938 nodes[1], nodes[3], nodes[5], nodes[7] );
4940 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4942 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4943 nodes[4 - 2*nextShift],
4945 nodes[4 + 2*nextShift],
4947 nodes[5 - 2*nextShift],
4949 nodes[5 + 2*nextShift] };
4951 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4953 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4954 newOrder[ 2 ], newOrder[ 3 ],
4955 newOrder[ 4 ], newOrder[ 5 ],
4956 newOrder[ 6 ], newOrder[ 7 ]));
4959 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4961 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4962 SMDSAbs_Face, /*noMedium=*/false);
4964 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4966 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4967 nodes[4 - 2*nextShift],
4969 nodes[4 + 2*nextShift],
4971 nodes[5 - 2*nextShift],
4973 nodes[5 + 2*nextShift],
4976 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4978 myLastCreatedElems.push_back(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4979 newOrder[ 2 ], newOrder[ 3 ],
4980 newOrder[ 4 ], newOrder[ 5 ],
4981 newOrder[ 6 ], newOrder[ 7 ],
4985 else //////// polygon
4987 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4988 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4990 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4992 if ( !vTool.IsForward() )
4993 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4995 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4997 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5001 while ( srcElements.size() < myLastCreatedElems.size() )
5002 srcElements.push_back( *srcEdge );
5004 } // loop on free faces
5006 // go to the next volume
5008 while ( iVol++ < nbVolumesByStep ) v++;
5011 } // loop on volumes of one step
5012 } // sweep free links into faces
5014 // Make a ceiling face with a normal external to a volume
5016 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5017 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5018 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5020 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5021 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5022 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5026 lastVol.SetExternalNormal();
5027 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5028 const int nbn = lastVol.NbFaceNodes( iF );
5029 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5030 if ( !hasFreeLinks ||
5031 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5033 const vector<int>& interlace =
5034 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5035 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5037 AddElement( nodeVec, anyFace.Init( elem ));
5039 while ( srcElements.size() < myLastCreatedElems.size() )
5040 srcElements.push_back( elem );
5043 } // loop on swept elements
5046 //=======================================================================
5047 //function : RotationSweep
5049 //=======================================================================
5051 SMESH_MeshEditor::PGroupIDs
5052 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5053 const gp_Ax1& theAxis,
5054 const double theAngle,
5055 const int theNbSteps,
5056 const double theTol,
5057 const bool theMakeGroups,
5058 const bool theMakeWalls)
5062 setElemsFirst( theElemSets );
5063 myLastCreatedElems.reserve( theElemSets[0].size() * theNbSteps );
5064 myLastCreatedNodes.reserve( theElemSets[1].size() * theNbSteps );
5066 // source elements for each generated one
5067 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5068 srcElems.reserve( theElemSets[0].size() );
5069 srcNodes.reserve( theElemSets[1].size() );
5072 aTrsf.SetRotation( theAxis, theAngle );
5074 aTrsf2.SetRotation( theAxis, theAngle/2. );
5076 gp_Lin aLine( theAxis );
5077 double aSqTol = theTol * theTol;
5079 SMESHDS_Mesh* aMesh = GetMeshDS();
5081 TNodeOfNodeListMap mapNewNodes;
5082 TElemOfVecOfNnlmiMap mapElemNewNodes;
5083 TTElemOfElemListMap newElemsMap;
5085 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5086 myMesh->NbFaces(ORDER_QUADRATIC) +
5087 myMesh->NbVolumes(ORDER_QUADRATIC) );
5088 // loop on theElemSets
5089 TIDSortedElemSet::iterator itElem;
5090 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5092 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5093 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5094 const SMDS_MeshElement* elem = *itElem;
5095 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5097 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5098 newNodesItVec.reserve( elem->NbNodes() );
5100 // loop on elem nodes
5101 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5102 while ( itN->more() )
5104 const SMDS_MeshNode* node = cast2Node( itN->next() );
5106 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5108 aXYZ.Coord( coord[0], coord[1], coord[2] );
5109 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5111 // check if a node has been already sweeped
5112 TNodeOfNodeListMapItr nIt =
5113 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5114 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5115 if ( listNewNodes.empty() )
5117 // check if we are to create medium nodes between corner ones
5118 bool needMediumNodes = false;
5119 if ( isQuadraticMesh )
5121 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5122 while (it->more() && !needMediumNodes )
5124 const SMDS_MeshElement* invElem = it->next();
5125 if ( invElem != elem && !theElems.count( invElem )) continue;
5126 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5127 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5128 needMediumNodes = true;
5133 const SMDS_MeshNode * newNode = node;
5134 for ( int i = 0; i < theNbSteps; i++ ) {
5136 if ( needMediumNodes ) // create a medium node
5138 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5139 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5140 myLastCreatedNodes.push_back(newNode);
5141 srcNodes.push_back( node );
5142 listNewNodes.push_back( newNode );
5143 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5146 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5148 // create a corner node
5149 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5150 myLastCreatedNodes.push_back(newNode);
5151 srcNodes.push_back( node );
5152 listNewNodes.push_back( newNode );
5155 listNewNodes.push_back( newNode );
5156 // if ( needMediumNodes )
5157 // listNewNodes.push_back( newNode );
5161 newNodesItVec.push_back( nIt );
5163 // make new elements
5164 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5169 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5171 PGroupIDs newGroupIDs;
5172 if ( theMakeGroups )
5173 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5178 //=======================================================================
5179 //function : ExtrusParam
5180 //purpose : standard construction
5181 //=======================================================================
5183 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5184 const int theNbSteps,
5185 const std::list<double>& theScales,
5186 const gp_XYZ* theBasePoint,
5188 const double theTolerance):
5190 myBaseP( Precision::Infinite(), 0, 0 ),
5191 myFlags( theFlags ),
5192 myTolerance( theTolerance ),
5193 myElemsToUse( NULL )
5195 mySteps = new TColStd_HSequenceOfReal;
5196 const double stepSize = theStep.Magnitude();
5197 for (int i=1; i<=theNbSteps; i++ )
5198 mySteps->Append( stepSize );
5200 int nbScales = theScales.size();
5203 if ( IsLinearVariation() && nbScales < theNbSteps )
5205 myScales.reserve( theNbSteps );
5206 std::list<double>::const_iterator scale = theScales.begin();
5207 double prevScale = 1.0;
5208 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
5210 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
5211 int stDelta = Max( 1, iStep - myScales.size());
5212 double scDelta = ( *scale - prevScale ) / stDelta;
5213 for ( int iStep = 0; iStep < stDelta; ++iStep )
5215 myScales.push_back( prevScale + scDelta );
5216 prevScale = myScales.back();
5223 myScales.assign( theScales.begin(), theScales.end() );
5228 myBaseP = *theBasePoint;
5231 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5232 ( theTolerance > 0 ))
5234 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5238 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5242 //=======================================================================
5243 //function : ExtrusParam
5244 //purpose : steps are given explicitly
5245 //=======================================================================
5247 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5248 Handle(TColStd_HSequenceOfReal) theSteps,
5250 const double theTolerance):
5252 mySteps( theSteps ),
5253 myFlags( theFlags ),
5254 myTolerance( theTolerance ),
5255 myElemsToUse( NULL )
5257 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5258 ( theTolerance > 0 ))
5260 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5264 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5268 //=======================================================================
5269 //function : ExtrusParam
5270 //purpose : for extrusion by normal
5271 //=======================================================================
5273 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5274 const int theNbSteps,
5278 mySteps( new TColStd_HSequenceOfReal ),
5279 myFlags( theFlags ),
5281 myElemsToUse( NULL )
5283 for (int i = 0; i < theNbSteps; i++ )
5284 mySteps->Append( theStepSize );
5288 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5292 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5296 //=======================================================================
5297 //function : ExtrusParam::SetElementsToUse
5298 //purpose : stores elements to use for extrusion by normal, depending on
5299 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5300 // define myBaseP for scaling
5301 //=======================================================================
5303 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5304 const TIDSortedElemSet& nodes )
5306 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5308 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5310 myBaseP.SetCoord( 0.,0.,0. );
5311 TIDSortedElemSet newNodes;
5313 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5314 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5316 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5317 TIDSortedElemSet::const_iterator itElem = elements.begin();
5318 for ( ; itElem != elements.end(); itElem++ )
5320 const SMDS_MeshElement* elem = *itElem;
5321 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5322 while ( itN->more() ) {
5323 const SMDS_MeshElement* node = itN->next();
5324 if ( newNodes.insert( node ).second )
5325 myBaseP += SMESH_NodeXYZ( node );
5329 myBaseP /= newNodes.size();
5333 //=======================================================================
5334 //function : ExtrusParam::beginStepIter
5335 //purpose : prepare iteration on steps
5336 //=======================================================================
5338 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5340 myWithMediumNodes = withMediumNodes;
5344 //=======================================================================
5345 //function : ExtrusParam::moreSteps
5346 //purpose : are there more steps?
5347 //=======================================================================
5349 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5351 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5353 //=======================================================================
5354 //function : ExtrusParam::nextStep
5355 //purpose : returns the next step
5356 //=======================================================================
5358 double SMESH_MeshEditor::ExtrusParam::nextStep()
5361 if ( !myCurSteps.empty() )
5363 res = myCurSteps.back();
5364 myCurSteps.pop_back();
5366 else if ( myNextStep <= mySteps->Length() )
5368 myCurSteps.push_back( mySteps->Value( myNextStep ));
5370 if ( myWithMediumNodes )
5372 myCurSteps.back() /= 2.;
5373 myCurSteps.push_back( myCurSteps.back() );
5380 //=======================================================================
5381 //function : ExtrusParam::makeNodesByDir
5382 //purpose : create nodes for standard extrusion
5383 //=======================================================================
5385 int SMESH_MeshEditor::ExtrusParam::
5386 makeNodesByDir( SMESHDS_Mesh* mesh,
5387 const SMDS_MeshNode* srcNode,
5388 std::list<const SMDS_MeshNode*> & newNodes,
5389 const bool makeMediumNodes)
5391 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5394 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5396 p += myDir.XYZ() * nextStep();
5397 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5398 newNodes.push_back( newNode );
5401 if ( !myScales.empty() )
5403 if ( makeMediumNodes && myMediumScales.empty() )
5405 myMediumScales.resize( myScales.size() );
5406 double prevFactor = 1.;
5407 for ( size_t i = 0; i < myScales.size(); ++i )
5409 myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
5410 prevFactor = myScales[i];
5413 typedef std::vector<double>::iterator ScaleIt;
5414 ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
5416 size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
5418 gp_XYZ center = myBaseP;
5419 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5421 for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
5423 center += myDir.XYZ() * nextStep();
5425 iSc += int( makeMediumNodes );
5426 ScaleIt& scale = scales[ iSc % 2 ];
5428 gp_XYZ xyz = SMESH_NodeXYZ( *nIt );
5429 xyz = ( *scale * ( xyz - center )) + center;
5430 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5438 //=======================================================================
5439 //function : ExtrusParam::makeNodesByDirAndSew
5440 //purpose : create nodes for standard extrusion with sewing
5441 //=======================================================================
5443 int SMESH_MeshEditor::ExtrusParam::
5444 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5445 const SMDS_MeshNode* srcNode,
5446 std::list<const SMDS_MeshNode*> & newNodes,
5447 const bool makeMediumNodes)
5449 gp_XYZ P1 = SMESH_NodeXYZ( srcNode );
5452 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5454 P1 += myDir.XYZ() * nextStep();
5456 // try to search in sequence of existing nodes
5457 // if myNodes.size()>0 we 'nave to use given sequence
5458 // else - use all nodes of mesh
5459 const SMDS_MeshNode * node = 0;
5460 if ( myNodes.Length() > 0 )
5462 for ( int i = 1; i <= myNodes.Length(); i++ )
5464 SMESH_NodeXYZ P2 = myNodes.Value(i);
5465 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5467 node = myNodes.Value(i);
5474 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5477 SMESH_NodeXYZ P2 = itn->next();
5478 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5487 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5489 newNodes.push_back( node );
5496 //=======================================================================
5497 //function : ExtrusParam::makeNodesByNormal2D
5498 //purpose : create nodes for extrusion using normals of faces
5499 //=======================================================================
5501 int SMESH_MeshEditor::ExtrusParam::
5502 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5503 const SMDS_MeshNode* srcNode,
5504 std::list<const SMDS_MeshNode*> & newNodes,
5505 const bool makeMediumNodes)
5507 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5509 gp_XYZ p = SMESH_NodeXYZ( srcNode );
5511 // get normals to faces sharing srcNode
5512 vector< gp_XYZ > norms, baryCenters;
5513 gp_XYZ norm, avgNorm( 0,0,0 );
5514 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5515 while ( faceIt->more() )
5517 const SMDS_MeshElement* face = faceIt->next();
5518 if ( myElemsToUse && !myElemsToUse->count( face ))
5520 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5522 norms.push_back( norm );
5524 if ( !alongAvgNorm )
5528 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5529 bc += SMESH_NodeXYZ( nIt->next() );
5530 baryCenters.push_back( bc / nbN );
5535 if ( norms.empty() ) return 0;
5537 double normSize = avgNorm.Modulus();
5538 if ( normSize < std::numeric_limits<double>::min() )
5541 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5544 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5547 avgNorm /= normSize;
5550 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5553 double stepSize = nextStep();
5555 if ( norms.size() > 1 )
5557 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5559 // translate plane of a face
5560 baryCenters[ iF ] += norms[ iF ] * stepSize;
5562 // find point of intersection of the face plane located at baryCenters[ iF ]
5563 // and avgNorm located at pNew
5564 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5565 double dot = ( norms[ iF ] * avgNorm );
5566 if ( dot < std::numeric_limits<double>::min() )
5567 dot = stepSize * 1e-3;
5568 double step = -( norms[ iF ] * pNew + d ) / dot;
5569 pNew += step * avgNorm;
5574 pNew += stepSize * avgNorm;
5578 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5579 newNodes.push_back( newNode );
5584 //=======================================================================
5585 //function : ExtrusParam::makeNodesByNormal1D
5586 //purpose : create nodes for extrusion using normals of edges
5587 //=======================================================================
5589 int SMESH_MeshEditor::ExtrusParam::
5590 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5591 const SMDS_MeshNode* srcNode,
5592 std::list<const SMDS_MeshNode*> & newNodes,
5593 const bool makeMediumNodes)
5595 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5599 //=======================================================================
5600 //function : ExtrusionSweep
5602 //=======================================================================
5604 SMESH_MeshEditor::PGroupIDs
5605 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5606 const gp_Vec& theStep,
5607 const int theNbSteps,
5608 TTElemOfElemListMap& newElemsMap,
5610 const double theTolerance)
5612 ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
5613 return ExtrusionSweep( theElems, aParams, newElemsMap );
5617 //=======================================================================
5618 //function : ExtrusionSweep
5620 //=======================================================================
5622 SMESH_MeshEditor::PGroupIDs
5623 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5624 ExtrusParam& theParams,
5625 TTElemOfElemListMap& newElemsMap)
5629 setElemsFirst( theElemSets );
5630 myLastCreatedElems.reserve( theElemSets[0].size() * theParams.NbSteps() );
5631 myLastCreatedNodes.reserve( theElemSets[1].size() * theParams.NbSteps() );
5633 // source elements for each generated one
5634 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5635 srcElems.reserve( theElemSets[0].size() );
5636 srcNodes.reserve( theElemSets[1].size() );
5638 const int nbSteps = theParams.NbSteps();
5639 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5641 TNodeOfNodeListMap mapNewNodes;
5642 TElemOfVecOfNnlmiMap mapElemNewNodes;
5644 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5645 myMesh->NbFaces(ORDER_QUADRATIC) +
5646 myMesh->NbVolumes(ORDER_QUADRATIC) );
5648 TIDSortedElemSet::iterator itElem;
5649 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5651 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5652 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5654 // check element type
5655 const SMDS_MeshElement* elem = *itElem;
5656 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5659 const size_t nbNodes = elem->NbNodes();
5660 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5661 newNodesItVec.reserve( nbNodes );
5663 // loop on elem nodes
5664 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5665 while ( itN->more() )
5667 // check if a node has been already sweeped
5668 const SMDS_MeshNode* node = cast2Node( itN->next() );
5669 TNodeOfNodeListMap::iterator nIt =
5670 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5671 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5672 if ( listNewNodes.empty() )
5676 // check if we are to create medium nodes between corner ones
5677 bool needMediumNodes = false;
5678 if ( isQuadraticMesh )
5680 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5681 while (it->more() && !needMediumNodes )
5683 const SMDS_MeshElement* invElem = it->next();
5684 if ( invElem != elem && !theElems.count( invElem )) continue;
5685 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5686 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5687 needMediumNodes = true;
5690 // create nodes for all steps
5691 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5693 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5694 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5696 myLastCreatedNodes.push_back( *newNodesIt );
5697 srcNodes.push_back( node );
5702 break; // newNodesItVec will be shorter than nbNodes
5705 newNodesItVec.push_back( nIt );
5707 // make new elements
5708 if ( newNodesItVec.size() == nbNodes )
5709 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5713 if ( theParams.ToMakeBoundary() ) {
5714 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5716 PGroupIDs newGroupIDs;
5717 if ( theParams.ToMakeGroups() )
5718 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5723 //=======================================================================
5724 //function : ExtrusionAlongTrack
5726 //=======================================================================
5727 SMESH_MeshEditor::Extrusion_Error
5728 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5729 SMESH_subMesh* theTrack,
5730 const SMDS_MeshNode* theN1,
5731 const bool theHasAngles,
5732 list<double>& theAngles,
5733 const bool theLinearVariation,
5734 const bool theHasRefPoint,
5735 const gp_Pnt& theRefPoint,
5736 const bool theMakeGroups)
5741 std::list<double> aPrms;
5742 TIDSortedElemSet::iterator itElem;
5745 TopoDS_Edge aTrackEdge;
5746 TopoDS_Vertex aV1, aV2;
5748 SMDS_ElemIteratorPtr aItE;
5749 SMDS_NodeIteratorPtr aItN;
5750 SMDSAbs_ElementType aTypeE;
5752 TNodeOfNodeListMap mapNewNodes;
5755 aNbE = theElements[0].size() + theElements[1].size();
5758 return EXTR_NO_ELEMENTS;
5760 // 1.1 Track Pattern
5763 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5765 return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
5766 theHasAngles, theAngles, theLinearVariation,
5767 theHasRefPoint, theRefPoint, theMakeGroups );
5769 aItE = pSubMeshDS->GetElements();
5770 while ( aItE->more() ) {
5771 const SMDS_MeshElement* pE = aItE->next();
5772 aTypeE = pE->GetType();
5773 // Pattern must contain links only
5774 if ( aTypeE != SMDSAbs_Edge )
5775 return EXTR_PATH_NOT_EDGE;
5778 list<SMESH_MeshEditor_PathPoint> fullList;
5780 const TopoDS_Shape& aS = theTrack->GetSubShape();
5781 // Sub-shape for the Pattern must be an Edge or Wire
5782 if( aS.ShapeType() == TopAbs_EDGE ) {
5783 aTrackEdge = TopoDS::Edge( aS );
5784 // the Edge must not be degenerated
5785 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5786 return EXTR_BAD_PATH_SHAPE;
5787 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5788 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5789 const SMDS_MeshNode* aN1 = aItN->next();
5790 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5791 const SMDS_MeshNode* aN2 = aItN->next();
5792 // starting node must be aN1 or aN2
5793 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5794 return EXTR_BAD_STARTING_NODE;
5795 aItN = pSubMeshDS->GetNodes();
5796 while ( aItN->more() ) {
5797 const SMDS_MeshNode* pNode = aItN->next();
5798 SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
5799 double aT = pEPos->GetUParameter();
5800 aPrms.push_back( aT );
5802 //Extrusion_Error err =
5803 makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5804 } else if( aS.ShapeType() == TopAbs_WIRE ) {
5805 list< SMESH_subMesh* > LSM;
5806 TopTools_SequenceOfShape Edges;
5807 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5808 while(itSM->more()) {
5809 SMESH_subMesh* SM = itSM->next();
5811 const TopoDS_Shape& aS = SM->GetSubShape();
5814 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5815 int startNid = theN1->GetID();
5816 TColStd_MapOfInteger UsedNums;
5818 int NbEdges = Edges.Length();
5820 for(; i<=NbEdges; i++) {
5822 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5823 for(; itLSM!=LSM.end(); itLSM++) {
5825 if(UsedNums.Contains(k)) continue;
5826 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5827 SMESH_subMesh* locTrack = *itLSM;
5828 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5829 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5830 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5831 const SMDS_MeshNode* aN1 = aItN->next();
5832 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5833 const SMDS_MeshNode* aN2 = aItN->next();
5834 // starting node must be aN1 or aN2
5835 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5836 // 2. Collect parameters on the track edge
5838 aItN = locMeshDS->GetNodes();
5839 while ( aItN->more() ) {
5840 const SMDS_MeshNode* pNode = aItN->next();
5841 SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
5842 double aT = pEPos->GetUParameter();
5843 aPrms.push_back( aT );
5845 list<SMESH_MeshEditor_PathPoint> LPP;
5846 //Extrusion_Error err =
5847 makeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5848 LLPPs.push_back(LPP);
5850 // update startN for search following edge
5851 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5852 else startNid = aN1->GetID();
5856 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5857 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5858 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5859 for(; itPP!=firstList.end(); itPP++) {
5860 fullList.push_back( *itPP );
5862 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5863 fullList.pop_back();
5865 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5866 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5867 itPP = currList.begin();
5868 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5869 gp_Dir D1 = PP1.Tangent();
5870 gp_Dir D2 = PP2.Tangent();
5871 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5872 (D1.Z()+D2.Z())/2 ) );
5873 PP1.SetTangent(Dnew);
5874 fullList.push_back(PP1);
5876 for(; itPP!=firstList.end(); itPP++) {
5877 fullList.push_back( *itPP );
5879 PP1 = fullList.back();
5880 fullList.pop_back();
5882 // if wire not closed
5883 fullList.push_back(PP1);
5887 return EXTR_BAD_PATH_SHAPE;
5890 return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5891 theHasRefPoint, theRefPoint, theMakeGroups);
5895 //=======================================================================
5896 //function : ExtrusionAlongTrack
5898 //=======================================================================
5899 SMESH_MeshEditor::Extrusion_Error
5900 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5901 SMESH_Mesh* theTrack,
5902 const SMDS_MeshNode* theN1,
5903 const bool theHasAngles,
5904 list<double>& theAngles,
5905 const bool theLinearVariation,
5906 const bool theHasRefPoint,
5907 const gp_Pnt& theRefPoint,
5908 const bool theMakeGroups)
5913 std::list<double> aPrms;
5914 TIDSortedElemSet::iterator itElem;
5917 TopoDS_Edge aTrackEdge;
5918 TopoDS_Vertex aV1, aV2;
5920 SMDS_ElemIteratorPtr aItE;
5921 SMDS_NodeIteratorPtr aItN;
5922 SMDSAbs_ElementType aTypeE;
5924 TNodeOfNodeListMap mapNewNodes;
5927 aNbE = theElements[0].size() + theElements[1].size();
5930 return EXTR_NO_ELEMENTS;
5932 // 1.1 Track Pattern
5935 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5937 aItE = pMeshDS->elementsIterator();
5938 while ( aItE->more() ) {
5939 const SMDS_MeshElement* pE = aItE->next();
5940 aTypeE = pE->GetType();
5941 // Pattern must contain links only
5942 if ( aTypeE != SMDSAbs_Edge )
5943 return EXTR_PATH_NOT_EDGE;
5946 list<SMESH_MeshEditor_PathPoint> fullList;
5948 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5950 if ( !theTrack->HasShapeToMesh() ) {
5951 //Mesh without shape
5952 const SMDS_MeshNode* currentNode = NULL;
5953 const SMDS_MeshNode* prevNode = theN1;
5954 std::vector<const SMDS_MeshNode*> aNodesList;
5955 aNodesList.push_back(theN1);
5956 int nbEdges = 0, conn=0;
5957 const SMDS_MeshElement* prevElem = NULL;
5958 const SMDS_MeshElement* currentElem = NULL;
5959 int totalNbEdges = theTrack->NbEdges();
5960 SMDS_ElemIteratorPtr nIt;
5963 if( !theTrack->GetMeshDS()->Contains( theN1 )) {
5964 return EXTR_BAD_STARTING_NODE;
5967 conn = nbEdgeConnectivity(theN1);
5969 return EXTR_PATH_NOT_EDGE;
5971 aItE = theN1->GetInverseElementIterator();
5972 prevElem = aItE->next();
5973 currentElem = prevElem;
5975 if(totalNbEdges == 1 ) {
5976 nIt = currentElem->nodesIterator();
5977 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5978 if(currentNode == prevNode)
5979 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5980 aNodesList.push_back(currentNode);
5982 nIt = currentElem->nodesIterator();
5983 while( nIt->more() ) {
5984 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5985 if(currentNode == prevNode)
5986 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5987 aNodesList.push_back(currentNode);
5989 //case of the closed mesh
5990 if(currentNode == theN1) {
5995 conn = nbEdgeConnectivity(currentNode);
5997 return EXTR_PATH_NOT_EDGE;
5998 }else if( conn == 1 && nbEdges > 0 ) {
6003 prevNode = currentNode;
6004 aItE = currentNode->GetInverseElementIterator();
6005 currentElem = aItE->next();
6006 if( currentElem == prevElem)
6007 currentElem = aItE->next();
6008 nIt = currentElem->nodesIterator();
6009 prevElem = currentElem;
6015 if(nbEdges != totalNbEdges)
6016 return EXTR_PATH_NOT_EDGE;
6018 TopTools_SequenceOfShape Edges;
6019 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6020 int startNid = theN1->GetID();
6021 for ( size_t i = 1; i < aNodesList.size(); i++ )
6023 gp_Pnt p1 = SMESH_NodeXYZ( aNodesList[i-1] );
6024 gp_Pnt p2 = SMESH_NodeXYZ( aNodesList[i] );
6025 TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6026 list<SMESH_MeshEditor_PathPoint> LPP;
6028 makeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6029 LLPPs.push_back(LPP);
6030 if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i ]->GetID();
6031 else startNid = aNodesList[i-1]->GetID();
6034 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6035 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6036 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6037 for(; itPP!=firstList.end(); itPP++) {
6038 fullList.push_back( *itPP );
6041 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6042 SMESH_MeshEditor_PathPoint PP2;
6043 fullList.pop_back();
6045 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6046 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6047 itPP = currList.begin();
6048 PP2 = currList.front();
6049 gp_Dir D1 = PP1.Tangent();
6050 gp_Dir D2 = PP2.Tangent();
6051 gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6052 PP1.SetTangent(Dnew);
6053 fullList.push_back(PP1);
6055 for(; itPP!=currList.end(); itPP++) {
6056 fullList.push_back( *itPP );
6058 PP1 = fullList.back();
6059 fullList.pop_back();
6061 fullList.push_back(PP1);
6063 } // Sub-shape for the Pattern must be an Edge or Wire
6064 else if ( aS.ShapeType() == TopAbs_EDGE )
6066 aTrackEdge = TopoDS::Edge( aS );
6067 // the Edge must not be degenerated
6068 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6069 return EXTR_BAD_PATH_SHAPE;
6070 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6071 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6072 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6073 // starting node must be aN1 or aN2
6074 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6075 return EXTR_BAD_STARTING_NODE;
6076 aItN = pMeshDS->nodesIterator();
6077 while ( aItN->more() ) {
6078 const SMDS_MeshNode* pNode = aItN->next();
6079 if( pNode==aN1 || pNode==aN2 ) continue;
6080 SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
6081 double aT = pEPos->GetUParameter();
6082 aPrms.push_back( aT );
6084 //Extrusion_Error err =
6085 makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6087 else if( aS.ShapeType() == TopAbs_WIRE ) {
6088 list< SMESH_subMesh* > LSM;
6089 TopTools_SequenceOfShape Edges;
6090 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6091 for(; eExp.More(); eExp.Next()) {
6092 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6093 if( SMESH_Algo::isDegenerated(E) ) continue;
6094 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6100 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6101 TopoDS_Vertex aVprev;
6102 TColStd_MapOfInteger UsedNums;
6103 int NbEdges = Edges.Length();
6105 for(; i<=NbEdges; i++) {
6107 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6108 for(; itLSM!=LSM.end(); itLSM++) {
6110 if(UsedNums.Contains(k)) continue;
6111 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6112 SMESH_subMesh* locTrack = *itLSM;
6113 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6114 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6115 bool aN1isOK = false, aN2isOK = false;
6116 if ( aVprev.IsNull() ) {
6117 // if previous vertex is not yet defined, it means that we in the beginning of wire
6118 // and we have to find initial vertex corresponding to starting node theN1
6119 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6120 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6121 // starting node must be aN1 or aN2
6122 aN1isOK = ( aN1 && aN1 == theN1 );
6123 aN2isOK = ( aN2 && aN2 == theN1 );
6126 // we have specified ending vertex of the previous edge on the previous iteration
6127 // and we have just to check that it corresponds to any vertex in current segment
6128 aN1isOK = aVprev.IsSame( aV1 );
6129 aN2isOK = aVprev.IsSame( aV2 );
6131 if ( !aN1isOK && !aN2isOK ) continue;
6132 // 2. Collect parameters on the track edge
6134 aItN = locMeshDS->GetNodes();
6135 while ( aItN->more() ) {
6136 const SMDS_MeshNode* pNode = aItN->next();
6137 SMDS_EdgePositionPtr pEPos = pNode->GetPosition();
6138 double aT = pEPos->GetUParameter();
6139 aPrms.push_back( aT );
6141 list<SMESH_MeshEditor_PathPoint> LPP;
6142 //Extrusion_Error err =
6143 makeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6144 LLPPs.push_back(LPP);
6146 // update startN for search following edge
6147 if ( aN1isOK ) aVprev = aV2;
6152 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6153 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6154 fullList.splice( fullList.end(), firstList );
6156 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6157 fullList.pop_back();
6159 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6160 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6161 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6162 gp_Dir D1 = PP1.Tangent();
6163 gp_Dir D2 = PP2.Tangent();
6164 gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6165 PP1.SetTangent(Dnew);
6166 fullList.push_back(PP1);
6167 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6168 PP1 = fullList.back();
6169 fullList.pop_back();
6171 // if wire not closed
6172 fullList.push_back(PP1);
6176 return EXTR_BAD_PATH_SHAPE;
6179 return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6180 theHasRefPoint, theRefPoint, theMakeGroups);
6184 //=======================================================================
6185 //function : makeEdgePathPoints
6186 //purpose : auxiliary for ExtrusionAlongTrack
6187 //=======================================================================
6188 SMESH_MeshEditor::Extrusion_Error
6189 SMESH_MeshEditor::makeEdgePathPoints(std::list<double>& aPrms,
6190 const TopoDS_Edge& aTrackEdge,
6192 list<SMESH_MeshEditor_PathPoint>& LPP)
6194 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6196 aTolVec2=aTolVec*aTolVec;
6198 TopoDS_Vertex aV1, aV2;
6199 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6200 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6201 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6202 // 2. Collect parameters on the track edge
6203 aPrms.push_front( aT1 );
6204 aPrms.push_back( aT2 );
6207 if( FirstIsStart ) {
6218 SMESH_MeshEditor_PathPoint aPP;
6219 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6220 std::list<double>::iterator aItD = aPrms.begin();
6221 for(; aItD != aPrms.end(); ++aItD) {
6225 aC3D->D1( aT, aP3D, aVec );
6226 aL2 = aVec.SquareMagnitude();
6227 if ( aL2 < aTolVec2 )
6228 return EXTR_CANT_GET_TANGENT;
6229 gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6231 aPP.SetTangent( aTgt );
6232 aPP.SetParameter( aT );
6239 //=======================================================================
6240 //function : makeExtrElements
6241 //purpose : auxiliary for ExtrusionAlongTrack
6242 //=======================================================================
6243 SMESH_MeshEditor::Extrusion_Error
6244 SMESH_MeshEditor::makeExtrElements(TIDSortedElemSet theElemSets[2],
6245 list<SMESH_MeshEditor_PathPoint>& fullList,
6246 const bool theHasAngles,
6247 list<double>& theAngles,
6248 const bool theLinearVariation,
6249 const bool theHasRefPoint,
6250 const gp_Pnt& theRefPoint,
6251 const bool theMakeGroups)
6253 const int aNbTP = fullList.size();
6256 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6257 linearAngleVariation(aNbTP-1, theAngles);
6259 // fill vector of path points with angles
6260 vector<SMESH_MeshEditor_PathPoint> aPPs;
6261 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6262 list<double>::iterator itAngles = theAngles.begin();
6263 aPPs.push_back( *itPP++ );
6264 for( ; itPP != fullList.end(); itPP++) {
6265 aPPs.push_back( *itPP );
6266 if ( theHasAngles && itAngles != theAngles.end() )
6267 aPPs.back().SetAngle( *itAngles++ );
6270 TNodeOfNodeListMap mapNewNodes;
6271 TElemOfVecOfNnlmiMap mapElemNewNodes;
6272 TTElemOfElemListMap newElemsMap;
6273 TIDSortedElemSet::iterator itElem;
6274 // source elements for each generated one
6275 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6277 // 3. Center of rotation aV0
6278 gp_Pnt aV0 = theRefPoint;
6279 if ( !theHasRefPoint )
6281 gp_XYZ aGC( 0.,0.,0. );
6282 TIDSortedElemSet newNodes;
6284 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6286 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6287 itElem = theElements.begin();
6288 for ( ; itElem != theElements.end(); itElem++ )
6290 const SMDS_MeshElement* elem = *itElem;
6291 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6292 while ( itN->more() ) {
6293 const SMDS_MeshElement* node = itN->next();
6294 if ( newNodes.insert( node ).second )
6295 aGC += SMESH_NodeXYZ( node );
6299 aGC /= newNodes.size();
6301 } // if (!theHasRefPoint) {
6303 // 4. Processing the elements
6304 SMESHDS_Mesh* aMesh = GetMeshDS();
6305 list<const SMDS_MeshNode*> emptyList;
6307 setElemsFirst( theElemSets );
6308 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6310 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6311 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6313 const SMDS_MeshElement* elem = *itElem;
6315 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6316 newNodesItVec.reserve( elem->NbNodes() );
6318 // loop on elem nodes
6320 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6321 while ( itN->more() )
6324 // check if a node has been already processed
6325 const SMDS_MeshNode* node = cast2Node( itN->next() );
6326 TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6327 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6328 if ( listNewNodes.empty() )
6331 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6332 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6333 gp_Ax1 anAx1, anAxT1T0;
6334 gp_Dir aDT1x, aDT0x, aDT1T0;
6339 aPN0 = SMESH_NodeXYZ( node );
6341 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6343 aDT0x= aPP0.Tangent();
6345 for ( int j = 1; j < aNbTP; ++j ) {
6346 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6348 aDT1x = aPP1.Tangent();
6349 aAngle1x = aPP1.Angle();
6351 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6353 gp_Vec aV01x( aP0x, aP1x );
6354 aTrsf.SetTranslation( aV01x );
6357 aV1x = aV0x.Transformed( aTrsf );
6358 aPN1 = aPN0.Transformed( aTrsf );
6360 // rotation 1 [ T1,T0 ]
6361 aAngleT1T0=-aDT1x.Angle( aDT0x );
6362 if (fabs(aAngleT1T0) > aTolAng)
6365 anAxT1T0.SetLocation( aV1x );
6366 anAxT1T0.SetDirection( aDT1T0 );
6367 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6369 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6373 if ( theHasAngles ) {
6374 anAx1.SetLocation( aV1x );
6375 anAx1.SetDirection( aDT1x );
6376 aTrsfRot.SetRotation( anAx1, aAngle1x );
6378 aPN1 = aPN1.Transformed( aTrsfRot );
6382 if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6384 // create additional node
6385 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6386 const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6387 myLastCreatedNodes.push_back(newNode);
6388 srcNodes.push_back( node );
6389 listNewNodes.push_back( newNode );
6391 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6392 myLastCreatedNodes.push_back(newNode);
6393 srcNodes.push_back( node );
6394 listNewNodes.push_back( newNode );
6402 else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6404 // if current elem is quadratic and current node is not medium
6405 // we have to check - may be it is needed to insert additional nodes
6406 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6407 if ((int) listNewNodes.size() == aNbTP-1 )
6409 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6410 gp_XYZ P(node->X(), node->Y(), node->Z());
6411 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6413 for(i=0; i<aNbTP-1; i++) {
6414 const SMDS_MeshNode* N = *it;
6415 double x = ( N->X() + P.X() )/2.;
6416 double y = ( N->Y() + P.Y() )/2.;
6417 double z = ( N->Z() + P.Z() )/2.;
6418 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6419 srcNodes.push_back( node );
6420 myLastCreatedNodes.push_back(newN);
6423 P = gp_XYZ(N->X(),N->Y(),N->Z());
6425 listNewNodes.clear();
6426 for(i=0; i<2*(aNbTP-1); i++) {
6427 listNewNodes.push_back(aNodes[i]);
6432 newNodesItVec.push_back( nIt );
6435 // make new elements
6436 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6440 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6442 if ( theMakeGroups )
6443 generateGroups( srcNodes, srcElems, "extruded");
6449 //=======================================================================
6450 //function : linearAngleVariation
6451 //purpose : spread values over nbSteps
6452 //=======================================================================
6454 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6455 list<double>& Angles)
6457 int nbAngles = Angles.size();
6458 if( nbSteps > nbAngles && nbAngles > 0 )
6460 vector<double> theAngles(nbAngles);
6461 theAngles.assign( Angles.begin(), Angles.end() );
6464 double rAn2St = double( nbAngles ) / double( nbSteps );
6465 double angPrev = 0, angle;
6466 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6468 double angCur = rAn2St * ( iSt+1 );
6469 double angCurFloor = floor( angCur );
6470 double angPrevFloor = floor( angPrev );
6471 if ( angPrevFloor == angCurFloor )
6472 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6474 int iP = int( angPrevFloor );
6475 double angPrevCeil = ceil(angPrev);
6476 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6478 int iC = int( angCurFloor );
6479 if ( iC < nbAngles )
6480 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6482 iP = int( angPrevCeil );
6484 angle += theAngles[ iC ];
6486 res.push_back(angle);
6494 //================================================================================
6496 * \brief Move or copy theElements applying theTrsf to their nodes
6497 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6498 * \param theTrsf - transformation to apply
6499 * \param theCopy - if true, create translated copies of theElems
6500 * \param theMakeGroups - if true and theCopy, create translated groups
6501 * \param theTargetMesh - mesh to copy translated elements into
6502 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6504 //================================================================================
6506 SMESH_MeshEditor::PGroupIDs
6507 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6508 const gp_Trsf& theTrsf,
6510 const bool theMakeGroups,
6511 SMESH_Mesh* theTargetMesh)
6514 myLastCreatedElems.reserve( theElems.size() );
6516 bool needReverse = false;
6517 string groupPostfix;
6518 switch ( theTrsf.Form() ) {
6521 groupPostfix = "mirrored";
6524 groupPostfix = "mirrored";
6528 groupPostfix = "mirrored";
6531 groupPostfix = "rotated";
6533 case gp_Translation:
6534 groupPostfix = "translated";
6537 groupPostfix = "scaled";
6539 case gp_CompoundTrsf: // different scale by axis
6540 groupPostfix = "scaled";
6543 needReverse = false;
6544 groupPostfix = "transformed";
6547 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6548 SMESHDS_Mesh* aMesh = GetMeshDS();
6550 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6551 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6552 SMESH_MeshEditor::ElemFeatures elemType;
6554 // map old node to new one
6555 TNodeNodeMap nodeMap;
6557 // elements sharing moved nodes; those of them which have all
6558 // nodes mirrored but are not in theElems are to be reversed
6559 TIDSortedElemSet inverseElemSet;
6561 // source elements for each generated one
6562 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6564 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6565 TIDSortedElemSet orphanNode;
6567 if ( theElems.empty() ) // transform the whole mesh
6570 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6571 while ( eIt->more() ) theElems.insert( eIt->next() );
6573 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6574 while ( nIt->more() )
6576 const SMDS_MeshNode* node = nIt->next();
6577 if ( node->NbInverseElements() == 0)
6578 orphanNode.insert( node );
6582 // loop on elements to transform nodes : first orphan nodes then elems
6583 TIDSortedElemSet::iterator itElem;
6584 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6585 for (int i=0; i<2; i++)
6586 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6588 const SMDS_MeshElement* elem = *itElem;
6592 // loop on elem nodes
6594 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6595 while ( itN->more() )
6597 const SMDS_MeshNode* node = cast2Node( itN->next() );
6598 // check if a node has been already transformed
6599 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6600 nodeMap.insert( make_pair ( node, node ));
6601 if ( !n2n_isnew.second )
6604 node->GetXYZ( coord );
6605 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6606 if ( theTargetMesh ) {
6607 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6608 n2n_isnew.first->second = newNode;
6609 myLastCreatedNodes.push_back(newNode);
6610 srcNodes.push_back( node );
6612 else if ( theCopy ) {
6613 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6614 n2n_isnew.first->second = newNode;
6615 myLastCreatedNodes.push_back(newNode);
6616 srcNodes.push_back( node );
6619 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6620 // node position on shape becomes invalid
6621 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6622 ( SMDS_SpacePosition::originSpacePosition() );
6625 // keep inverse elements
6626 if ( !theCopy && !theTargetMesh && needReverse ) {
6627 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6628 while ( invElemIt->more() ) {
6629 const SMDS_MeshElement* iel = invElemIt->next();
6630 inverseElemSet.insert( iel );
6634 } // loop on elems in { &orphanNode, &theElems };
6636 // either create new elements or reverse mirrored ones
6637 if ( !theCopy && !needReverse && !theTargetMesh )
6640 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6642 // Replicate or reverse elements
6644 std::vector<int> iForw;
6645 vector<const SMDS_MeshNode*> nodes;
6646 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6648 const SMDS_MeshElement* elem = *itElem;
6649 if ( !elem ) continue;
6651 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6652 size_t nbNodes = elem->NbNodes();
6653 if ( geomType == SMDSGeom_NONE ) continue; // node
6655 nodes.resize( nbNodes );
6657 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6659 const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem );
6663 bool allTransformed = true;
6664 int nbFaces = aPolyedre->NbFaces();
6665 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6667 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6668 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6670 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6671 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6672 if ( nodeMapIt == nodeMap.end() )
6673 allTransformed = false; // not all nodes transformed
6675 nodes.push_back((*nodeMapIt).second);
6677 if ( needReverse && allTransformed )
6678 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6680 if ( !allTransformed )
6681 continue; // not all nodes transformed
6683 else // ----------------------- the rest element types
6685 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6686 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6687 const vector<int>& i = needReverse ? iRev : iForw;
6689 // find transformed nodes
6691 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6692 while ( itN->more() ) {
6693 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6694 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6695 if ( nodeMapIt == nodeMap.end() )
6696 break; // not all nodes transformed
6697 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6699 if ( iNode != nbNodes )
6700 continue; // not all nodes transformed
6704 // copy in this or a new mesh
6705 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6706 srcElems.push_back( elem );
6709 // reverse element as it was reversed by transformation
6711 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6714 } // loop on elements
6716 if ( editor && editor != this )
6717 myLastCreatedElems.swap( editor->myLastCreatedElems );
6719 PGroupIDs newGroupIDs;
6721 if ( ( theMakeGroups && theCopy ) ||
6722 ( theMakeGroups && theTargetMesh ) )
6723 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6728 //================================================================================
6730 * \brief Make an offset mesh from a source 2D mesh
6731 * \param [in] theElements - source faces
6732 * \param [in] theValue - offset value
6733 * \param [out] theTgtMesh - a mesh to add offset elements to
6734 * \param [in] theMakeGroups - to generate groups
6735 * \return PGroupIDs - IDs of created groups
6737 //================================================================================
6739 SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Offset( TIDSortedElemSet & theElements,
6740 const double theValue,
6741 SMESH_Mesh* theTgtMesh,
6742 const bool theMakeGroups,
6743 const bool theCopyElements,
6744 const bool theFixSelfIntersection)
6746 SMESHDS_Mesh* meshDS = GetMeshDS();
6747 SMESHDS_Mesh* tgtMeshDS = theTgtMesh->GetMeshDS();
6748 SMESH_MeshEditor tgtEditor( theTgtMesh );
6750 SMDS_ElemIteratorPtr eIt;
6751 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6752 else eIt = SMESHUtils::elemSetIterator( theElements );
6754 SMESH_MeshAlgos::TElemIntPairVec new2OldFaces;
6755 SMESH_MeshAlgos::TNodeIntPairVec new2OldNodes;
6756 std::unique_ptr< SMDS_Mesh > offsetMesh
6757 ( SMESH_MeshAlgos::MakeOffset( eIt, *meshDS, theValue,
6758 theFixSelfIntersection,
6759 new2OldFaces, new2OldNodes ));
6760 if ( offsetMesh->NbElements() == 0 )
6761 return PGroupIDs(); // MakeOffset() failed
6764 if ( theTgtMesh == myMesh && !theCopyElements )
6766 // clear the source elements
6767 if ( theElements.empty() ) eIt = meshDS->elementsIterator( SMDSAbs_Face );
6768 else eIt = SMESHUtils::elemSetIterator( theElements );
6769 while ( eIt->more() )
6770 meshDS->RemoveFreeElement( eIt->next(), 0 );
6773 // offsetMesh->Modified();
6774 // offsetMesh->CompactMesh(); // make IDs start from 1
6776 // source elements for each generated one
6777 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6778 srcElems.reserve( new2OldFaces.size() );
6779 srcNodes.reserve( new2OldNodes.size() );
6782 myLastCreatedElems.reserve( new2OldFaces.size() );
6783 myLastCreatedNodes.reserve( new2OldNodes.size() );
6785 // copy offsetMesh to theTgtMesh
6787 int idShift = meshDS->MaxNodeID();
6788 for ( size_t i = 0; i < new2OldNodes.size(); ++i )
6789 if ( const SMDS_MeshNode* n = new2OldNodes[ i ].first )
6792 if ( n->NbInverseElements() > 0 )
6795 const SMDS_MeshNode* n2 =
6796 tgtMeshDS->AddNodeWithID( n->X(), n->Y(), n->Z(), idShift + n->GetID() );
6797 myLastCreatedNodes.push_back( n2 );
6798 srcNodes.push_back( meshDS->FindNode( new2OldNodes[ i ].second ));
6802 ElemFeatures elemType;
6803 for ( size_t i = 0; i < new2OldFaces.size(); ++i )
6804 if ( const SMDS_MeshElement* f = new2OldFaces[ i ].first )
6807 elemType.myNodes.clear();
6808 for ( SMDS_NodeIteratorPtr nIt = f->nodeIterator(); nIt->more(); )
6810 const SMDS_MeshNode* n2 = nIt->next();
6811 elemType.myNodes.push_back( tgtMeshDS->FindNode( idShift + n2->GetID() ));
6813 tgtEditor.AddElement( elemType.myNodes, elemType );
6814 srcElems.push_back( meshDS->FindElement( new2OldFaces[ i ].second ));
6817 myLastCreatedElems.swap( tgtEditor.myLastCreatedElems );
6819 PGroupIDs newGroupIDs;
6820 if ( theMakeGroups )
6821 newGroupIDs = generateGroups( srcNodes, srcElems, "offset", theTgtMesh, false );
6826 //=======================================================================
6828 * \brief Create groups of elements made during transformation
6829 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6830 * \param elemGens - elements making corresponding myLastCreatedElems
6831 * \param postfix - to push_back to names of new groups
6832 * \param targetMesh - mesh to create groups in
6833 * \param topPresent - is there are "top" elements that are created by sweeping
6835 //=======================================================================
6837 SMESH_MeshEditor::PGroupIDs
6838 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6839 const SMESH_SequenceOfElemPtr& elemGens,
6840 const std::string& postfix,
6841 SMESH_Mesh* targetMesh,
6842 const bool topPresent)
6844 PGroupIDs newGroupIDs( new list<int> );
6845 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6847 // Sort existing groups by types and collect their names
6849 // containers to store an old group and generated new ones;
6850 // 1st new group is for result elems of different type than a source one;
6851 // 2nd new group is for same type result elems ("top" group at extrusion)
6853 using boost::make_tuple;
6854 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6855 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6856 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6858 set< string > groupNames;
6860 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6861 if ( !groupIt->more() ) return newGroupIDs;
6863 int newGroupID = mesh->GetGroupIds().back()+1;
6864 while ( groupIt->more() )
6866 SMESH_Group * group = groupIt->next();
6867 if ( !group ) continue;
6868 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6869 if ( !groupDS || groupDS->IsEmpty() ) continue;
6870 groupNames.insert ( group->GetName() );
6871 groupDS->SetStoreName( group->GetName() );
6872 const SMDSAbs_ElementType type = groupDS->GetType();
6873 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6874 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6875 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6876 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6879 // Loop on nodes and elements to add them in new groups
6881 vector< const SMDS_MeshElement* > resultElems;
6882 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6884 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6885 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6886 if ( gens.size() != elems.size() )
6887 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6889 // loop on created elements
6890 for (size_t iElem = 0; iElem < elems.size(); ++iElem )
6892 const SMDS_MeshElement* sourceElem = gens[ iElem ];
6893 if ( !sourceElem ) {
6894 MESSAGE("generateGroups(): NULL source element");
6897 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6898 if ( groupsOldNew.empty() ) { // no groups of this type at all
6899 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6900 ++iElem; // skip all elements made by sourceElem
6903 // collect all elements made by the iElem-th sourceElem
6904 resultElems.clear();
6905 if ( const SMDS_MeshElement* resElem = elems[ iElem ])
6906 if ( resElem != sourceElem )
6907 resultElems.push_back( resElem );
6908 while ( iElem+1 < gens.size() && gens[ iElem+1 ] == sourceElem )
6909 if ( const SMDS_MeshElement* resElem = elems[ ++iElem ])
6910 if ( resElem != sourceElem )
6911 resultElems.push_back( resElem );
6913 const SMDS_MeshElement* topElem = 0;
6914 if ( isNodes ) // there must be a top element
6916 topElem = resultElems.back();
6917 resultElems.pop_back();
6921 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6922 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6923 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6925 topElem = *resElemIt;
6926 *resElemIt = 0; // erase *resElemIt
6930 // add resultElems to groups originted from ones the sourceElem belongs to
6931 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6932 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6934 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6935 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6937 // fill in a new group
6938 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6939 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6940 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6942 newGroup.Add( *resElemIt );
6944 // fill a "top" group
6947 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6948 newTopGroup.Add( topElem );
6952 } // loop on created elements
6953 }// loop on nodes and elements
6955 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6957 list<int> topGrouIds;
6958 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6960 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6961 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6962 orderedOldNewGroups[i]->get<2>() };
6963 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6965 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6966 if ( newGroupDS->IsEmpty() )
6968 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6973 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6976 const bool isTop = ( topPresent &&
6977 newGroupDS->GetType() == oldGroupDS->GetType() &&
6980 string name = oldGroupDS->GetStoreName();
6981 { // remove trailing whitespaces (issue 22599)
6982 size_t size = name.size();
6983 while ( size > 1 && isspace( name[ size-1 ]))
6985 if ( size != name.size() )
6987 name.resize( size );
6988 oldGroupDS->SetStoreName( name.c_str() );
6991 if ( !targetMesh ) {
6992 string suffix = ( isTop ? "top": postfix.c_str() );
6996 while ( !groupNames.insert( name ).second ) // name exists
6997 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7002 newGroupDS->SetStoreName( name.c_str() );
7004 // make a SMESH_Groups
7005 mesh->AddGroup( newGroupDS );
7007 topGrouIds.push_back( newGroupDS->GetID() );
7009 newGroupIDs->push_back( newGroupDS->GetID() );
7013 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7018 //================================================================================
7020 * * \brief Return list of group of nodes close to each other within theTolerance
7021 * * Search among theNodes or in the whole mesh if theNodes is empty using
7022 * * an Octree algorithm
7023 * \param [in,out] theNodes - the nodes to treat
7024 * \param [in] theTolerance - the tolerance
7025 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7026 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7027 * corner and medium nodes in separate groups
7029 //================================================================================
7031 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7032 const double theTolerance,
7033 TListOfListOfNodes & theGroupsOfNodes,
7034 bool theSeparateCornersAndMedium)
7038 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7039 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7040 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7041 theSeparateCornersAndMedium = false;
7043 TIDSortedNodeSet& corners = theNodes;
7044 TIDSortedNodeSet medium;
7046 if ( theNodes.empty() ) // get all nodes in the mesh
7048 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7049 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator();
7050 if ( theSeparateCornersAndMedium )
7051 while ( nIt->more() )
7053 const SMDS_MeshNode* n = nIt->next();
7054 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7055 nodeSet->insert( nodeSet->end(), n );
7058 while ( nIt->more() )
7059 theNodes.insert( theNodes.end(), nIt->next() );
7061 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7063 TIDSortedNodeSet::iterator nIt = corners.begin();
7064 while ( nIt != corners.end() )
7065 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7067 medium.insert( medium.end(), *nIt );
7068 corners.erase( nIt++ );
7076 if ( !corners.empty() )
7077 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7078 if ( !medium.empty() )
7079 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7082 //=======================================================================
7083 //function : SimplifyFace
7084 //purpose : split a chain of nodes into several closed chains
7085 //=======================================================================
7087 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7088 vector<const SMDS_MeshNode *>& poly_nodes,
7089 vector<int>& quantities) const
7091 int nbNodes = faceNodes.size();
7092 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7096 size_t prevNbQuant = quantities.size();
7098 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7099 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7100 map< const SMDS_MeshNode*, int >::iterator nInd;
7102 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7103 simpleNodes.push_back( faceNodes[0] );
7104 for ( int iCur = 1; iCur < nbNodes; iCur++ )
7106 if ( faceNodes[ iCur ] != simpleNodes.back() )
7108 int index = simpleNodes.size();
7109 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7110 int prevIndex = nInd->second;
7111 if ( prevIndex < index )
7114 int loopLen = index - prevIndex;
7117 // store the sub-loop
7118 quantities.push_back( loopLen );
7119 for ( int i = prevIndex; i < index; i++ )
7120 poly_nodes.push_back( simpleNodes[ i ]);
7122 simpleNodes.resize( prevIndex+1 );
7126 simpleNodes.push_back( faceNodes[ iCur ]);
7131 if ( simpleNodes.size() > 2 )
7133 quantities.push_back( simpleNodes.size() );
7134 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7137 return quantities.size() - prevNbQuant;
7140 //=======================================================================
7141 //function : MergeNodes
7142 //purpose : In each group, the cdr of nodes are substituted by the first one
7144 //=======================================================================
7146 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7147 const bool theAvoidMakingHoles)
7151 SMESHDS_Mesh* mesh = GetMeshDS();
7153 TNodeNodeMap nodeNodeMap; // node to replace - new node
7154 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7155 list< int > rmElemIds, rmNodeIds;
7156 vector< ElemFeatures > newElemDefs;
7158 // Fill nodeNodeMap and elems
7160 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7161 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7163 list<const SMDS_MeshNode*>& nodes = *grIt;
7164 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7165 const SMDS_MeshNode* nToKeep = *nIt;
7166 for ( ++nIt; nIt != nodes.end(); nIt++ )
7168 const SMDS_MeshNode* nToRemove = *nIt;
7169 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7170 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7171 while ( invElemIt->more() ) {
7172 const SMDS_MeshElement* elem = invElemIt->next();
7178 // Apply recursive replacements (BUG 0020185)
7179 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7180 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7182 const SMDS_MeshNode* nToKeep = nnIt->second;
7183 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7184 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7186 nToKeep = nnIt_i->second;
7187 nnIt->second = nToKeep;
7188 nnIt_i = nodeNodeMap.find( nToKeep );
7192 if ( theAvoidMakingHoles )
7194 // find elements whose topology changes
7196 vector<const SMDS_MeshElement*> pbElems;
7197 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7198 for ( ; eIt != elems.end(); ++eIt )
7200 const SMDS_MeshElement* elem = *eIt;
7201 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7202 while ( itN->more() )
7204 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7205 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7206 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7208 // several nodes of elem stick
7209 pbElems.push_back( elem );
7214 // exclude from merge nodes causing spoiling element
7215 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7217 bool nodesExcluded = false;
7218 for ( size_t i = 0; i < pbElems.size(); ++i )
7220 size_t prevNbMergeNodes = nodeNodeMap.size();
7221 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7222 prevNbMergeNodes < nodeNodeMap.size() )
7223 nodesExcluded = true;
7225 if ( !nodesExcluded )
7230 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7232 const SMDS_MeshNode* nToRemove = nnIt->first;
7233 const SMDS_MeshNode* nToKeep = nnIt->second;
7234 if ( nToRemove != nToKeep )
7236 rmNodeIds.push_back( nToRemove->GetID() );
7237 AddToSameGroups( nToKeep, nToRemove, mesh );
7238 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7239 // w/o creating node in place of merged ones.
7240 SMDS_PositionPtr pos = nToRemove->GetPosition();
7241 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7242 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7243 sm->SetIsAlwaysComputed( true );
7247 // Change element nodes or remove an element
7249 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7250 for ( ; eIt != elems.end(); eIt++ )
7252 const SMDS_MeshElement* elem = *eIt;
7253 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
7255 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7257 rmElemIds.push_back( elem->GetID() );
7259 for ( size_t i = 0; i < newElemDefs.size(); ++i )
7261 if ( i > 0 || !mesh->ChangeElementNodes( elem,
7262 & newElemDefs[i].myNodes[0],
7263 newElemDefs[i].myNodes.size() ))
7267 newElemDefs[i].SetID( elem->GetID() );
7268 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7269 if ( !keepElem ) rmElemIds.pop_back();
7273 newElemDefs[i].SetID( -1 );
7275 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7276 if ( sm && newElem )
7277 sm->AddElement( newElem );
7278 if ( elem != newElem )
7279 ReplaceElemInGroups( elem, newElem, mesh );
7284 // Remove bad elements, then equal nodes (order important)
7285 Remove( rmElemIds, /*isNodes=*/false );
7286 Remove( rmNodeIds, /*isNodes=*/true );
7291 //=======================================================================
7292 //function : applyMerge
7293 //purpose : Compute new connectivity of an element after merging nodes
7294 // \param [in] elems - the element
7295 // \param [out] newElemDefs - definition(s) of result element(s)
7296 // \param [inout] nodeNodeMap - nodes to merge
7297 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
7298 // after merging (but not degenerated), removes nodes causing
7299 // the invalidity from \a nodeNodeMap.
7300 // \return bool - true if the element should be removed
7301 //=======================================================================
7303 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7304 vector< ElemFeatures >& newElemDefs,
7305 TNodeNodeMap& nodeNodeMap,
7306 const bool avoidMakingHoles )
7308 bool toRemove = false; // to remove elem
7309 int nbResElems = 1; // nb new elements
7311 newElemDefs.resize(nbResElems);
7312 newElemDefs[0].Init( elem );
7313 newElemDefs[0].myNodes.clear();
7315 set<const SMDS_MeshNode*> nodeSet;
7316 vector< const SMDS_MeshNode*> curNodes;
7317 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7320 const int nbNodes = elem->NbNodes();
7321 SMDSAbs_EntityType entity = elem->GetEntityType();
7323 curNodes.resize( nbNodes );
7324 uniqueNodes.resize( nbNodes );
7325 iRepl.resize( nbNodes );
7326 int iUnique = 0, iCur = 0, nbRepl = 0;
7328 // Get new seq of nodes
7330 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7331 while ( itN->more() )
7333 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7335 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7336 if ( nnIt != nodeNodeMap.end() ) {
7339 curNodes[ iCur ] = n;
7340 bool isUnique = nodeSet.insert( n ).second;
7342 uniqueNodes[ iUnique++ ] = n;
7344 iRepl[ nbRepl++ ] = iCur;
7348 // Analyse element topology after replacement
7350 int nbUniqueNodes = nodeSet.size();
7351 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7356 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7358 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7359 int nbCorners = nbNodes / 2;
7360 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7362 int iNext = ( iCur + 1 ) % nbCorners;
7363 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7365 int iMedium = iCur + nbCorners;
7366 vector< const SMDS_MeshNode* >::iterator i =
7367 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7369 curNodes[ iMedium ]);
7370 if ( i != uniqueNodes.end() )
7373 for ( ; i+1 != uniqueNodes.end(); ++i )
7382 case SMDSEntity_Polygon:
7383 case SMDSEntity_Quad_Polygon: // Polygon
7385 ElemFeatures* elemType = & newElemDefs[0];
7386 const bool isQuad = elemType->myIsQuad;
7388 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7389 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7391 // a polygon can divide into several elements
7392 vector<const SMDS_MeshNode *> polygons_nodes;
7393 vector<int> quantities;
7394 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7395 newElemDefs.resize( nbResElems );
7396 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7398 ElemFeatures* elemType = & newElemDefs[iface];
7399 if ( iface ) elemType->Init( elem );
7401 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7402 int nbNewNodes = quantities[iface];
7403 face_nodes.assign( polygons_nodes.begin() + inode,
7404 polygons_nodes.begin() + inode + nbNewNodes );
7405 inode += nbNewNodes;
7406 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7408 bool isValid = ( nbNewNodes % 2 == 0 );
7409 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7410 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7411 elemType->SetQuad( isValid );
7412 if ( isValid ) // put medium nodes after corners
7413 SMDS_MeshCell::applyInterlaceRev
7414 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7415 nbNewNodes ), face_nodes );
7417 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7419 nbUniqueNodes = newElemDefs[0].myNodes.size();
7423 case SMDSEntity_Polyhedra: // Polyhedral volume
7425 if ( nbUniqueNodes >= 4 )
7427 // each face has to be analyzed in order to check volume validity
7428 if ( const SMDS_MeshVolume* aPolyedre = SMDS_Mesh::DownCast< SMDS_MeshVolume >( elem ))
7430 int nbFaces = aPolyedre->NbFaces();
7432 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7433 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7434 vector<const SMDS_MeshNode *> faceNodes;
7438 for (int iface = 1; iface <= nbFaces; iface++)
7440 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7441 faceNodes.resize( nbFaceNodes );
7442 for (int inode = 1; inode <= nbFaceNodes; inode++)
7444 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7445 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7446 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7447 faceNode = (*nnIt).second;
7448 faceNodes[inode - 1] = faceNode;
7450 SimplifyFace(faceNodes, poly_nodes, quantities);
7453 if ( quantities.size() > 3 )
7455 // TODO: remove coincident faces
7457 nbUniqueNodes = newElemDefs[0].myNodes.size();
7465 // TODO not all the possible cases are solved. Find something more generic?
7466 case SMDSEntity_Edge: //////// EDGE
7467 case SMDSEntity_Triangle: //// TRIANGLE
7468 case SMDSEntity_Quad_Triangle:
7469 case SMDSEntity_Tetra:
7470 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7474 case SMDSEntity_Quad_Edge:
7478 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7480 if ( nbUniqueNodes < 3 )
7482 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7483 toRemove = true; // opposite nodes stick
7488 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7497 if ( nbUniqueNodes == 6 &&
7499 ( nbRepl == 1 || iRepl[1] >= 4 ))
7505 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7514 if ( nbUniqueNodes == 7 &&
7516 ( nbRepl == 1 || iRepl[1] != 8 ))
7522 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7524 if ( nbUniqueNodes == 4 ) {
7525 // ---------------------------------> tetrahedron
7526 if ( curNodes[3] == curNodes[4] &&
7527 curNodes[3] == curNodes[5] ) {
7531 else if ( curNodes[0] == curNodes[1] &&
7532 curNodes[0] == curNodes[2] ) {
7533 // bottom nodes stick: set a top before
7534 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7535 uniqueNodes[ 0 ] = curNodes [ 5 ];
7536 uniqueNodes[ 1 ] = curNodes [ 4 ];
7537 uniqueNodes[ 2 ] = curNodes [ 3 ];
7540 else if (( curNodes[0] == curNodes[3] ) +
7541 ( curNodes[1] == curNodes[4] ) +
7542 ( curNodes[2] == curNodes[5] ) == 2 ) {
7543 // a lateral face turns into a line
7547 else if ( nbUniqueNodes == 5 ) {
7548 // PENTAHEDRON --------------------> pyramid
7549 if ( curNodes[0] == curNodes[3] )
7551 uniqueNodes[ 0 ] = curNodes[ 1 ];
7552 uniqueNodes[ 1 ] = curNodes[ 4 ];
7553 uniqueNodes[ 2 ] = curNodes[ 5 ];
7554 uniqueNodes[ 3 ] = curNodes[ 2 ];
7555 uniqueNodes[ 4 ] = curNodes[ 0 ];
7558 if ( curNodes[1] == curNodes[4] )
7560 uniqueNodes[ 0 ] = curNodes[ 0 ];
7561 uniqueNodes[ 1 ] = curNodes[ 2 ];
7562 uniqueNodes[ 2 ] = curNodes[ 5 ];
7563 uniqueNodes[ 3 ] = curNodes[ 3 ];
7564 uniqueNodes[ 4 ] = curNodes[ 1 ];
7567 if ( curNodes[2] == curNodes[5] )
7569 uniqueNodes[ 0 ] = curNodes[ 0 ];
7570 uniqueNodes[ 1 ] = curNodes[ 3 ];
7571 uniqueNodes[ 2 ] = curNodes[ 4 ];
7572 uniqueNodes[ 3 ] = curNodes[ 1 ];
7573 uniqueNodes[ 4 ] = curNodes[ 2 ];
7579 case SMDSEntity_Hexa:
7581 //////////////////////////////////// HEXAHEDRON
7582 SMDS_VolumeTool hexa (elem);
7583 hexa.SetExternalNormal();
7584 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7585 //////////////////////// HEX ---> tetrahedron
7586 for ( int iFace = 0; iFace < 6; iFace++ ) {
7587 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7588 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7589 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7590 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7591 // one face turns into a point ...
7592 int pickInd = ind[ 0 ];
7593 int iOppFace = hexa.GetOppFaceIndex( iFace );
7594 ind = hexa.GetFaceNodesIndices( iOppFace );
7596 uniqueNodes.clear();
7597 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7598 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7601 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7603 if ( nbStick == 1 ) {
7604 // ... and the opposite one - into a triangle.
7606 uniqueNodes.push_back( curNodes[ pickInd ]);
7613 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7614 //////////////////////// HEX ---> prism
7615 int nbTria = 0, iTria[3];
7616 const int *ind; // indices of face nodes
7617 // look for triangular faces
7618 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7619 ind = hexa.GetFaceNodesIndices( iFace );
7620 TIDSortedNodeSet faceNodes;
7621 for ( iCur = 0; iCur < 4; iCur++ )
7622 faceNodes.insert( curNodes[ind[iCur]] );
7623 if ( faceNodes.size() == 3 )
7624 iTria[ nbTria++ ] = iFace;
7626 // check if triangles are opposite
7627 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7629 // set nodes of the bottom triangle
7630 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7632 for ( iCur = 0; iCur < 4; iCur++ )
7633 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7634 indB.push_back( ind[iCur] );
7635 if ( !hexa.IsForward() )
7636 std::swap( indB[0], indB[2] );
7637 for ( iCur = 0; iCur < 3; iCur++ )
7638 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7639 // set nodes of the top triangle
7640 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7641 for ( iCur = 0; iCur < 3; ++iCur )
7642 for ( int j = 0; j < 4; ++j )
7643 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7645 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7652 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7653 //////////////////// HEXAHEDRON ---> pyramid
7654 for ( int iFace = 0; iFace < 6; iFace++ ) {
7655 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7656 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7657 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7658 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7659 // one face turns into a point ...
7660 int iOppFace = hexa.GetOppFaceIndex( iFace );
7661 ind = hexa.GetFaceNodesIndices( iOppFace );
7662 uniqueNodes.clear();
7663 for ( iCur = 0; iCur < 4; iCur++ ) {
7664 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7667 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7669 if ( uniqueNodes.size() == 4 ) {
7670 // ... and the opposite one is a quadrangle
7672 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7673 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7681 if ( toRemove && nbUniqueNodes > 4 ) {
7682 ////////////////// HEXAHEDRON ---> polyhedron
7683 hexa.SetExternalNormal();
7684 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7685 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7686 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7687 quantities.reserve( 6 ); quantities.clear();
7688 for ( int iFace = 0; iFace < 6; iFace++ )
7690 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7691 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7692 curNodes[ind[1]] == curNodes[ind[3]] )
7695 break; // opposite nodes stick
7698 for ( iCur = 0; iCur < 4; iCur++ )
7700 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7701 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7703 if ( nodeSet.size() < 3 )
7704 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7706 quantities.push_back( nodeSet.size() );
7708 if ( quantities.size() >= 4 )
7711 nbUniqueNodes = poly_nodes.size();
7712 newElemDefs[0].SetPoly(true);
7716 } // case HEXAHEDRON
7721 } // switch ( entity )
7723 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7725 // erase from nodeNodeMap nodes whose merge spoils elem
7726 vector< const SMDS_MeshNode* > noMergeNodes;
7727 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7728 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7729 nodeNodeMap.erase( noMergeNodes[i] );
7732 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7734 uniqueNodes.resize( nbUniqueNodes );
7736 if ( !toRemove && nbResElems == 0 )
7739 newElemDefs.resize( nbResElems );
7745 // ========================================================
7746 // class : ComparableElement
7747 // purpose : allow comparing elements basing on their nodes
7748 // ========================================================
7750 class ComparableElement : public boost::container::flat_set< int >
7752 typedef boost::container::flat_set< int > int_set;
7754 const SMDS_MeshElement* myElem;
7756 mutable int myGroupID;
7760 ComparableElement( const SMDS_MeshElement* theElem ):
7761 myElem ( theElem ), mySumID( 0 ), myGroupID( -1 )
7763 this->reserve( theElem->NbNodes() );
7764 for ( SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); nodeIt->more(); )
7766 int id = nodeIt->next()->GetID();
7772 const SMDS_MeshElement* GetElem() const { return myElem; }
7774 int& GroupID() const { return myGroupID; }
7775 //int& GroupID() const { return const_cast< int& >( myGroupID ); }
7777 ComparableElement( const ComparableElement& theSource ) // move copy
7779 ComparableElement& src = const_cast< ComparableElement& >( theSource );
7780 (int_set&) (*this ) = boost::move( src );
7781 myElem = src.myElem;
7782 mySumID = src.mySumID;
7783 myGroupID = src.myGroupID;
7786 static int HashCode(const ComparableElement& se, int limit )
7788 return ::HashCode( se.mySumID, limit );
7790 static Standard_Boolean IsEqual(const ComparableElement& se1, const ComparableElement& se2 )
7792 return ( se1 == se2 );
7797 //=======================================================================
7798 //function : FindEqualElements
7799 //purpose : Return list of group of elements built on the same nodes.
7800 // Search among theElements or in the whole mesh if theElements is empty
7801 //=======================================================================
7803 void SMESH_MeshEditor::FindEqualElements( TIDSortedElemSet & theElements,
7804 TListOfListOfElementsID & theGroupsOfElementsID )
7808 SMDS_ElemIteratorPtr elemIt;
7809 if ( theElements.empty() ) elemIt = GetMeshDS()->elementsIterator();
7810 else elemIt = SMESHUtils::elemSetIterator( theElements );
7812 typedef NCollection_Map< ComparableElement, ComparableElement > TMapOfElements;
7813 typedef std::list<int> TGroupOfElems;
7814 TMapOfElements mapOfElements;
7815 std::vector< TGroupOfElems > arrayOfGroups;
7816 TGroupOfElems groupOfElems;
7818 while ( elemIt->more() )
7820 const SMDS_MeshElement* curElem = elemIt->next();
7821 ComparableElement compElem = curElem;
7823 const ComparableElement& elemInSet = mapOfElements.Added( compElem );
7824 if ( elemInSet.GetElem() != curElem ) // coincident elem
7826 int& iG = elemInSet.GroupID();
7829 iG = arrayOfGroups.size();
7830 arrayOfGroups.push_back( groupOfElems );
7831 arrayOfGroups[ iG ].push_back( elemInSet.GetElem()->GetID() );
7833 arrayOfGroups[ iG ].push_back( curElem->GetID() );
7837 groupOfElems.clear();
7838 std::vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7839 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7841 if ( groupIt->size() > 1 ) {
7842 //groupOfElems.sort(); -- theElements are sorted already
7843 theGroupsOfElementsID.emplace_back( *groupIt );
7848 //=======================================================================
7849 //function : MergeElements
7850 //purpose : In each given group, substitute all elements by the first one.
7851 //=======================================================================
7853 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7857 typedef list<int> TListOfIDs;
7858 TListOfIDs rmElemIds; // IDs of elems to remove
7860 SMESHDS_Mesh* aMesh = GetMeshDS();
7862 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7863 while ( groupsIt != theGroupsOfElementsID.end() ) {
7864 TListOfIDs& aGroupOfElemID = *groupsIt;
7865 aGroupOfElemID.sort();
7866 int elemIDToKeep = aGroupOfElemID.front();
7867 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7868 aGroupOfElemID.pop_front();
7869 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7870 while ( idIt != aGroupOfElemID.end() ) {
7871 int elemIDToRemove = *idIt;
7872 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7873 // add the kept element in groups of removed one (PAL15188)
7874 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7875 rmElemIds.push_back( elemIDToRemove );
7881 Remove( rmElemIds, false );
7884 //=======================================================================
7885 //function : MergeEqualElements
7886 //purpose : Remove all but one of elements built on the same nodes.
7887 //=======================================================================
7889 void SMESH_MeshEditor::MergeEqualElements()
7891 TIDSortedElemSet aMeshElements; /* empty input ==
7892 to merge equal elements in the whole mesh */
7893 TListOfListOfElementsID aGroupsOfElementsID;
7894 FindEqualElements( aMeshElements, aGroupsOfElementsID );
7895 MergeElements( aGroupsOfElementsID );
7898 //=======================================================================
7899 //function : findAdjacentFace
7901 //=======================================================================
7903 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7904 const SMDS_MeshNode* n2,
7905 const SMDS_MeshElement* elem)
7907 TIDSortedElemSet elemSet, avoidSet;
7909 avoidSet.insert ( elem );
7910 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7913 //=======================================================================
7914 //function : findSegment
7915 //purpose : Return a mesh segment by two nodes one of which can be medium
7916 //=======================================================================
7918 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7919 const SMDS_MeshNode* n2)
7921 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7922 while ( it->more() )
7924 const SMDS_MeshElement* seg = it->next();
7925 if ( seg->GetNodeIndex( n2 ) >= 0 )
7931 //=======================================================================
7932 //function : FindFreeBorder
7934 //=======================================================================
7936 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7938 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7939 const SMDS_MeshNode* theSecondNode,
7940 const SMDS_MeshNode* theLastNode,
7941 list< const SMDS_MeshNode* > & theNodes,
7942 list< const SMDS_MeshElement* >& theFaces)
7944 if ( !theFirstNode || !theSecondNode )
7946 // find border face between theFirstNode and theSecondNode
7947 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7951 theFaces.push_back( curElem );
7952 theNodes.push_back( theFirstNode );
7953 theNodes.push_back( theSecondNode );
7955 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7956 TIDSortedElemSet foundElems;
7957 bool needTheLast = ( theLastNode != 0 );
7959 while ( nStart != theLastNode ) {
7960 if ( nStart == theFirstNode )
7961 return !needTheLast;
7963 // find all free border faces sharing form nStart
7965 list< const SMDS_MeshElement* > curElemList;
7966 list< const SMDS_MeshNode* > nStartList;
7967 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7968 while ( invElemIt->more() ) {
7969 const SMDS_MeshElement* e = invElemIt->next();
7970 if ( e == curElem || foundElems.insert( e ).second ) {
7972 int iNode = 0, nbNodes = e->NbNodes();
7973 vector<const SMDS_MeshNode*> nodes( nbNodes+1 );
7974 nodes.assign( SMDS_MeshElement::iterator( e->interlacedNodesIterator() ),
7975 SMDS_MeshElement::iterator() );
7976 nodes.push_back( nodes[ 0 ]);
7979 for ( iNode = 0; iNode < nbNodes; iNode++ )
7980 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7981 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7982 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7984 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7985 curElemList.push_back( e );
7989 // analyse the found
7991 int nbNewBorders = curElemList.size();
7992 if ( nbNewBorders == 0 ) {
7993 // no free border furthermore
7994 return !needTheLast;
7996 else if ( nbNewBorders == 1 ) {
7997 // one more element found
7999 nStart = nStartList.front();
8000 curElem = curElemList.front();
8001 theFaces.push_back( curElem );
8002 theNodes.push_back( nStart );
8005 // several continuations found
8006 list< const SMDS_MeshElement* >::iterator curElemIt;
8007 list< const SMDS_MeshNode* >::iterator nStartIt;
8008 // check if one of them reached the last node
8009 if ( needTheLast ) {
8010 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8011 curElemIt!= curElemList.end();
8012 curElemIt++, nStartIt++ )
8013 if ( *nStartIt == theLastNode ) {
8014 theFaces.push_back( *curElemIt );
8015 theNodes.push_back( *nStartIt );
8019 // find the best free border by the continuations
8020 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8021 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8022 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8023 curElemIt!= curElemList.end();
8024 curElemIt++, nStartIt++ )
8026 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8027 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8028 // find one more free border
8029 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8033 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8034 // choice: clear a worse one
8035 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8036 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8037 contNodes[ iWorse ].clear();
8038 contFaces[ iWorse ].clear();
8041 if ( contNodes[0].empty() && contNodes[1].empty() )
8044 // push_back the best free border
8045 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8046 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8047 theNodes.pop_back(); // remove nIgnore
8048 theNodes.pop_back(); // remove nStart
8049 theFaces.pop_back(); // remove curElem
8050 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8051 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8052 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8053 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8056 } // several continuations found
8057 } // while ( nStart != theLastNode )
8062 //=======================================================================
8063 //function : CheckFreeBorderNodes
8064 //purpose : Return true if the tree nodes are on a free border
8065 //=======================================================================
8067 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8068 const SMDS_MeshNode* theNode2,
8069 const SMDS_MeshNode* theNode3)
8071 list< const SMDS_MeshNode* > nodes;
8072 list< const SMDS_MeshElement* > faces;
8073 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8076 //=======================================================================
8077 //function : SewFreeBorder
8079 //warning : for border-to-side sewing theSideSecondNode is considered as
8080 // the last side node and theSideThirdNode is not used
8081 //=======================================================================
8083 SMESH_MeshEditor::Sew_Error
8084 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8085 const SMDS_MeshNode* theBordSecondNode,
8086 const SMDS_MeshNode* theBordLastNode,
8087 const SMDS_MeshNode* theSideFirstNode,
8088 const SMDS_MeshNode* theSideSecondNode,
8089 const SMDS_MeshNode* theSideThirdNode,
8090 const bool theSideIsFreeBorder,
8091 const bool toCreatePolygons,
8092 const bool toCreatePolyedrs)
8096 Sew_Error aResult = SEW_OK;
8098 // ====================================
8099 // find side nodes and elements
8100 // ====================================
8102 list< const SMDS_MeshNode* > nSide[ 2 ];
8103 list< const SMDS_MeshElement* > eSide[ 2 ];
8104 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8105 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8109 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8110 nSide[0], eSide[0])) {
8111 MESSAGE(" Free Border 1 not found " );
8112 aResult = SEW_BORDER1_NOT_FOUND;
8114 if (theSideIsFreeBorder) {
8117 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8118 nSide[1], eSide[1])) {
8119 MESSAGE(" Free Border 2 not found " );
8120 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8123 if ( aResult != SEW_OK )
8126 if (!theSideIsFreeBorder) {
8130 // -------------------------------------------------------------------------
8132 // 1. If nodes to merge are not coincident, move nodes of the free border
8133 // from the coord sys defined by the direction from the first to last
8134 // nodes of the border to the correspondent sys of the side 2
8135 // 2. On the side 2, find the links most co-directed with the correspondent
8136 // links of the free border
8137 // -------------------------------------------------------------------------
8139 // 1. Since sewing may break if there are volumes to split on the side 2,
8140 // we won't move nodes but just compute new coordinates for them
8141 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8142 TNodeXYZMap nBordXYZ;
8143 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8144 list< const SMDS_MeshNode* >::iterator nBordIt;
8146 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8147 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8148 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8149 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8150 double tol2 = 1.e-8;
8151 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8152 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8153 // Need node movement.
8155 // find X and Z axes to create trsf
8156 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8158 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8160 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8163 gp_Ax3 toBordAx( Pb1, Zb, X );
8164 gp_Ax3 fromSideAx( Ps1, Zs, X );
8165 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8167 gp_Trsf toBordSys, fromSide2Sys;
8168 toBordSys.SetTransformation( toBordAx );
8169 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8170 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8173 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8174 const SMDS_MeshNode* n = *nBordIt;
8175 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8176 toBordSys.Transforms( xyz );
8177 fromSide2Sys.Transforms( xyz );
8178 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8182 // just insert nodes XYZ in the nBordXYZ map
8183 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8184 const SMDS_MeshNode* n = *nBordIt;
8185 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8189 // 2. On the side 2, find the links most co-directed with the correspondent
8190 // links of the free border
8192 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8193 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8194 sideNodes.push_back( theSideFirstNode );
8196 bool hasVolumes = false;
8197 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8198 set<long> foundSideLinkIDs, checkedLinkIDs;
8199 SMDS_VolumeTool volume;
8200 //const SMDS_MeshNode* faceNodes[ 4 ];
8202 const SMDS_MeshNode* sideNode;
8203 const SMDS_MeshElement* sideElem = 0;
8204 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8205 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8206 nBordIt = bordNodes.begin();
8208 // border node position and border link direction to compare with
8209 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8210 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8211 // choose next side node by link direction or by closeness to
8212 // the current border node:
8213 bool searchByDir = ( *nBordIt != theBordLastNode );
8215 // find the next node on the Side 2
8217 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8219 checkedLinkIDs.clear();
8220 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8222 // loop on inverse elements of current node (prevSideNode) on the Side 2
8223 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8224 while ( invElemIt->more() )
8226 const SMDS_MeshElement* elem = invElemIt->next();
8227 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8228 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8229 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8230 bool isVolume = volume.Set( elem );
8231 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8232 if ( isVolume ) // --volume
8234 else if ( elem->GetType() == SMDSAbs_Face ) { // --face
8235 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8236 SMDS_NodeIteratorPtr nIt = elem->interlacedNodesIterator();
8237 while ( nIt->more() ) {
8238 nodes[ iNode ] = cast2Node( nIt->next() );
8239 if ( nodes[ iNode++ ] == prevSideNode )
8240 iPrevNode = iNode - 1;
8242 // there are 2 links to check
8247 // loop on links, to be precise, on the second node of links
8248 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8249 const SMDS_MeshNode* n = nodes[ iNode ];
8251 if ( !volume.IsLinked( n, prevSideNode ))
8255 if ( iNode ) // a node before prevSideNode
8256 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8257 else // a node after prevSideNode
8258 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8260 // check if this link was already used
8261 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8262 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8263 if (!isJustChecked &&
8264 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8266 // test a link geometrically
8267 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8268 bool linkIsBetter = false;
8269 double dot = 0.0, dist = 0.0;
8270 if ( searchByDir ) { // choose most co-directed link
8271 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8272 linkIsBetter = ( dot > maxDot );
8274 else { // choose link with the node closest to bordPos
8275 dist = ( nextXYZ - bordPos ).SquareModulus();
8276 linkIsBetter = ( dist < minDist );
8278 if ( linkIsBetter ) {
8287 } // loop on inverse elements of prevSideNode
8290 MESSAGE(" Can't find path by links of the Side 2 ");
8291 return SEW_BAD_SIDE_NODES;
8293 sideNodes.push_back( sideNode );
8294 sideElems.push_back( sideElem );
8295 foundSideLinkIDs.insert ( linkID );
8296 prevSideNode = sideNode;
8298 if ( *nBordIt == theBordLastNode )
8299 searchByDir = false;
8301 // find the next border link to compare with
8302 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8303 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8304 // move to next border node if sideNode is before forward border node (bordPos)
8305 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8306 prevBordNode = *nBordIt;
8308 bordPos = nBordXYZ[ *nBordIt ];
8309 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8310 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8314 while ( sideNode != theSideSecondNode );
8316 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8317 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8318 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8320 } // end nodes search on the side 2
8322 // ============================
8323 // sew the border to the side 2
8324 // ============================
8326 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8327 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8329 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8330 if ( toMergeConformal && toCreatePolygons )
8332 // do not merge quadrangles if polygons are OK (IPAL0052824)
8333 eIt[0] = eSide[0].begin();
8334 eIt[1] = eSide[1].begin();
8335 bool allQuads[2] = { true, true };
8336 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8337 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8338 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8340 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8343 TListOfListOfNodes nodeGroupsToMerge;
8344 if (( toMergeConformal ) ||
8345 ( theSideIsFreeBorder && !theSideThirdNode )) {
8347 // all nodes are to be merged
8349 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8350 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8351 nIt[0]++, nIt[1]++ )
8353 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8354 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8355 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8360 // insert new nodes into the border and the side to get equal nb of segments
8362 // get normalized parameters of nodes on the borders
8363 vector< double > param[ 2 ];
8364 param[0].resize( maxNbNodes );
8365 param[1].resize( maxNbNodes );
8367 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8368 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8369 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8370 const SMDS_MeshNode* nPrev = *nIt;
8371 double bordLength = 0;
8372 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8373 const SMDS_MeshNode* nCur = *nIt;
8374 gp_XYZ segment (nCur->X() - nPrev->X(),
8375 nCur->Y() - nPrev->Y(),
8376 nCur->Z() - nPrev->Z());
8377 double segmentLen = segment.Modulus();
8378 bordLength += segmentLen;
8379 param[ iBord ][ iNode ] = bordLength;
8382 // normalize within [0,1]
8383 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8384 param[ iBord ][ iNode ] /= bordLength;
8388 // loop on border segments
8389 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8390 int i[ 2 ] = { 0, 0 };
8391 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8392 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8394 TElemOfNodeListMap insertMap;
8395 TElemOfNodeListMap::iterator insertMapIt;
8397 // key: elem to insert nodes into
8398 // value: 2 nodes to insert between + nodes to be inserted
8400 bool next[ 2 ] = { false, false };
8402 // find min adjacent segment length after sewing
8403 double nextParam = 10., prevParam = 0;
8404 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8405 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8406 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8407 if ( i[ iBord ] > 0 )
8408 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8410 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8411 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8412 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8414 // choose to insert or to merge nodes
8415 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8416 if ( Abs( du ) <= minSegLen * 0.2 ) {
8419 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8420 const SMDS_MeshNode* n0 = *nIt[0];
8421 const SMDS_MeshNode* n1 = *nIt[1];
8422 nodeGroupsToMerge.back().push_back( n1 );
8423 nodeGroupsToMerge.back().push_back( n0 );
8424 // position of node of the border changes due to merge
8425 param[ 0 ][ i[0] ] += du;
8426 // move n1 for the sake of elem shape evaluation during insertion.
8427 // n1 will be removed by MergeNodes() anyway
8428 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8429 next[0] = next[1] = true;
8434 int intoBord = ( du < 0 ) ? 0 : 1;
8435 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8436 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8437 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8438 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8439 if ( intoBord == 1 ) {
8440 // move node of the border to be on a link of elem of the side
8441 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8442 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8443 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8444 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8445 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8447 insertMapIt = insertMap.find( elem );
8448 bool notFound = ( insertMapIt == insertMap.end() );
8449 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8451 // insert into another link of the same element:
8452 // 1. perform insertion into the other link of the elem
8453 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8454 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8455 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8456 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8457 // 2. perform insertion into the link of adjacent faces
8458 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8459 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8461 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8462 InsertNodesIntoLink( seg, n12, n22, nodeList );
8464 if (toCreatePolyedrs) {
8465 // perform insertion into the links of adjacent volumes
8466 UpdateVolumes(n12, n22, nodeList);
8468 // 3. find an element appeared on n1 and n2 after the insertion
8469 insertMap.erase( elem );
8470 elem = findAdjacentFace( n1, n2, 0 );
8472 if ( notFound || otherLink ) {
8473 // add element and nodes of the side into the insertMap
8474 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8475 (*insertMapIt).second.push_back( n1 );
8476 (*insertMapIt).second.push_back( n2 );
8478 // add node to be inserted into elem
8479 (*insertMapIt).second.push_back( nIns );
8480 next[ 1 - intoBord ] = true;
8483 // go to the next segment
8484 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8485 if ( next[ iBord ] ) {
8486 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8488 nPrev[ iBord ] = *nIt[ iBord ];
8489 nIt[ iBord ]++; i[ iBord ]++;
8493 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8495 // perform insertion of nodes into elements
8497 for (insertMapIt = insertMap.begin();
8498 insertMapIt != insertMap.end();
8501 const SMDS_MeshElement* elem = (*insertMapIt).first;
8502 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8503 if ( nodeList.size() < 3 ) continue;
8504 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8505 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8507 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8509 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8510 InsertNodesIntoLink( seg, n1, n2, nodeList );
8513 if ( !theSideIsFreeBorder ) {
8514 // look for and insert nodes into the faces adjacent to elem
8515 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8516 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8519 if (toCreatePolyedrs) {
8520 // perform insertion into the links of adjacent volumes
8521 UpdateVolumes(n1, n2, nodeList);
8524 } // end: insert new nodes
8526 MergeNodes ( nodeGroupsToMerge );
8529 // Remove coincident segments
8532 TIDSortedElemSet segments;
8533 SMESH_SequenceOfElemPtr newFaces;
8534 for ( size_t i = 0; i < myLastCreatedElems.size(); ++i )
8536 if ( !myLastCreatedElems[i] ) continue;
8537 if ( myLastCreatedElems[i]->GetType() == SMDSAbs_Edge )
8538 segments.insert( segments.end(), myLastCreatedElems[i] );
8540 newFaces.push_back( myLastCreatedElems[i] );
8542 // get segments adjacent to merged nodes
8543 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8544 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8546 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8547 if ( nodes.front()->IsNull() ) continue;
8548 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8549 while ( segIt->more() )
8550 segments.insert( segIt->next() );
8554 TListOfListOfElementsID equalGroups;
8555 if ( !segments.empty() )
8556 FindEqualElements( segments, equalGroups );
8557 if ( !equalGroups.empty() )
8559 // remove from segments those that will be removed
8560 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8561 for ( ; itGroups != equalGroups.end(); ++itGroups )
8563 list< int >& group = *itGroups;
8564 list< int >::iterator id = group.begin();
8565 for ( ++id; id != group.end(); ++id )
8566 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8567 segments.erase( seg );
8569 // remove equal segments
8570 MergeElements( equalGroups );
8572 // restore myLastCreatedElems
8573 myLastCreatedElems = newFaces;
8574 TIDSortedElemSet::iterator seg = segments.begin();
8575 for ( ; seg != segments.end(); ++seg )
8576 myLastCreatedElems.push_back( *seg );
8582 //=======================================================================
8583 //function : InsertNodesIntoLink
8584 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8585 // and theBetweenNode2 and split theElement
8586 //=======================================================================
8588 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8589 const SMDS_MeshNode* theBetweenNode1,
8590 const SMDS_MeshNode* theBetweenNode2,
8591 list<const SMDS_MeshNode*>& theNodesToInsert,
8592 const bool toCreatePoly)
8594 if ( !theElement ) return;
8596 SMESHDS_Mesh *aMesh = GetMeshDS();
8597 vector<const SMDS_MeshElement*> newElems;
8599 if ( theElement->GetType() == SMDSAbs_Edge )
8601 theNodesToInsert.push_front( theBetweenNode1 );
8602 theNodesToInsert.push_back ( theBetweenNode2 );
8603 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8604 const SMDS_MeshNode* n1 = *n;
8605 for ( ++n; n != theNodesToInsert.end(); ++n )
8607 const SMDS_MeshNode* n2 = *n;
8608 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8609 AddToSameGroups( seg, theElement, aMesh );
8611 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8614 theNodesToInsert.pop_front();
8615 theNodesToInsert.pop_back();
8617 if ( theElement->IsQuadratic() ) // add a not split part
8619 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8620 theElement->end_nodes() );
8621 int iOther = 0, nbN = nodes.size();
8622 for ( ; iOther < nbN; ++iOther )
8623 if ( nodes[iOther] != theBetweenNode1 &&
8624 nodes[iOther] != theBetweenNode2 )
8628 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8629 AddToSameGroups( seg, theElement, aMesh );
8631 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8633 else if ( iOther == 2 )
8635 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8636 AddToSameGroups( seg, theElement, aMesh );
8638 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8641 // treat new elements
8642 for ( size_t i = 0; i < newElems.size(); ++i )
8645 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8646 myLastCreatedElems.push_back( newElems[i] );
8648 ReplaceElemInGroups( theElement, newElems, aMesh );
8649 aMesh->RemoveElement( theElement );
8652 } // if ( theElement->GetType() == SMDSAbs_Edge )
8654 const SMDS_MeshElement* theFace = theElement;
8655 if ( theFace->GetType() != SMDSAbs_Face ) return;
8657 // find indices of 2 link nodes and of the rest nodes
8658 int iNode = 0, il1, il2, i3, i4;
8659 il1 = il2 = i3 = i4 = -1;
8660 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8662 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8663 while ( nodeIt->more() ) {
8664 const SMDS_MeshNode* n = nodeIt->next();
8665 if ( n == theBetweenNode1 )
8667 else if ( n == theBetweenNode2 )
8673 nodes[ iNode++ ] = n;
8675 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8678 // arrange link nodes to go one after another regarding the face orientation
8679 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8680 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8685 aNodesToInsert.reverse();
8687 // check that not link nodes of a quadrangles are in good order
8688 int nbFaceNodes = theFace->NbNodes();
8689 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8695 if (toCreatePoly || theFace->IsPoly()) {
8698 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8700 // add nodes of face up to first node of link
8702 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8703 while ( nodeIt->more() && !isFLN ) {
8704 const SMDS_MeshNode* n = nodeIt->next();
8705 poly_nodes[iNode++] = n;
8706 isFLN = ( n == nodes[il1] );
8708 // add nodes to insert
8709 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8710 for (; nIt != aNodesToInsert.end(); nIt++) {
8711 poly_nodes[iNode++] = *nIt;
8713 // add nodes of face starting from last node of link
8714 while ( nodeIt->more() ) {
8715 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8716 poly_nodes[iNode++] = n;
8720 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8723 else if ( !theFace->IsQuadratic() )
8725 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8726 int nbLinkNodes = 2 + aNodesToInsert.size();
8727 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8728 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8729 linkNodes[ 0 ] = nodes[ il1 ];
8730 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8731 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8732 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8733 linkNodes[ iNode++ ] = *nIt;
8735 // decide how to split a quadrangle: compare possible variants
8736 // and choose which of splits to be a quadrangle
8737 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8738 if ( nbFaceNodes == 3 ) {
8739 iBestQuad = nbSplits;
8742 else if ( nbFaceNodes == 4 ) {
8743 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8744 double aBestRate = DBL_MAX;
8745 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8747 double aBadRate = 0;
8748 // evaluate elements quality
8749 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8750 if ( iSplit == iQuad ) {
8751 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8755 aBadRate += getBadRate( &quad, aCrit );
8758 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8760 nodes[ iSplit < iQuad ? i4 : i3 ]);
8761 aBadRate += getBadRate( &tria, aCrit );
8765 if ( aBadRate < aBestRate ) {
8767 aBestRate = aBadRate;
8772 // create new elements
8774 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8776 if ( iSplit == iBestQuad )
8777 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8782 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8784 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8787 const SMDS_MeshNode* newNodes[ 4 ];
8788 newNodes[ 0 ] = linkNodes[ i1 ];
8789 newNodes[ 1 ] = linkNodes[ i2 ];
8790 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8791 newNodes[ 3 ] = nodes[ i4 ];
8792 if (iSplit == iBestQuad)
8793 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8795 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8797 } // end if(!theFace->IsQuadratic())
8799 else { // theFace is quadratic
8800 // we have to split theFace on simple triangles and one simple quadrangle
8802 int nbshift = tmp*2;
8803 // shift nodes in nodes[] by nbshift
8805 for(i=0; i<nbshift; i++) {
8806 const SMDS_MeshNode* n = nodes[0];
8807 for(j=0; j<nbFaceNodes-1; j++) {
8808 nodes[j] = nodes[j+1];
8810 nodes[nbFaceNodes-1] = n;
8812 il1 = il1 - nbshift;
8813 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8814 // n0 n1 n2 n0 n1 n2
8815 // +-----+-----+ +-----+-----+
8824 // create new elements
8826 if ( nbFaceNodes == 6 ) { // quadratic triangle
8827 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8828 if ( theFace->IsMediumNode(nodes[il1]) ) {
8829 // create quadrangle
8830 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8836 // create quadrangle
8837 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8843 else { // nbFaceNodes==8 - quadratic quadrangle
8844 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8845 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8846 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8847 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8848 // create quadrangle
8849 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8855 // create quadrangle
8856 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8862 // create needed triangles using n1,n2,n3 and inserted nodes
8863 int nbn = 2 + aNodesToInsert.size();
8864 vector<const SMDS_MeshNode*> aNodes(nbn);
8865 aNodes[0 ] = nodes[n1];
8866 aNodes[nbn-1] = nodes[n2];
8867 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8868 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8869 aNodes[iNode++] = *nIt;
8871 for ( i = 1; i < nbn; i++ )
8872 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8875 // remove the old face
8876 for ( size_t i = 0; i < newElems.size(); ++i )
8879 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8880 myLastCreatedElems.push_back( newElems[i] );
8882 ReplaceElemInGroups( theFace, newElems, aMesh );
8883 aMesh->RemoveElement(theFace);
8885 } // InsertNodesIntoLink()
8887 //=======================================================================
8888 //function : UpdateVolumes
8890 //=======================================================================
8892 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8893 const SMDS_MeshNode* theBetweenNode2,
8894 list<const SMDS_MeshNode*>& theNodesToInsert)
8898 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8899 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8900 const SMDS_MeshElement* elem = invElemIt->next();
8902 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8903 SMDS_VolumeTool aVolume (elem);
8904 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8907 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8908 int iface, nbFaces = aVolume.NbFaces();
8909 vector<const SMDS_MeshNode *> poly_nodes;
8910 vector<int> quantities (nbFaces);
8912 for (iface = 0; iface < nbFaces; iface++) {
8913 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8914 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8915 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8917 for (int inode = 0; inode < nbFaceNodes; inode++) {
8918 poly_nodes.push_back(faceNodes[inode]);
8920 if (nbInserted == 0) {
8921 if (faceNodes[inode] == theBetweenNode1) {
8922 if (faceNodes[inode + 1] == theBetweenNode2) {
8923 nbInserted = theNodesToInsert.size();
8925 // add nodes to insert
8926 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8927 for (; nIt != theNodesToInsert.end(); nIt++) {
8928 poly_nodes.push_back(*nIt);
8932 else if (faceNodes[inode] == theBetweenNode2) {
8933 if (faceNodes[inode + 1] == theBetweenNode1) {
8934 nbInserted = theNodesToInsert.size();
8936 // add nodes to insert in reversed order
8937 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8939 for (; nIt != theNodesToInsert.begin(); nIt--) {
8940 poly_nodes.push_back(*nIt);
8942 poly_nodes.push_back(*nIt);
8949 quantities[iface] = nbFaceNodes + nbInserted;
8952 // Replace the volume
8953 SMESHDS_Mesh *aMesh = GetMeshDS();
8955 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
8957 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
8958 myLastCreatedElems.push_back( newElem );
8959 ReplaceElemInGroups( elem, newElem, aMesh );
8961 aMesh->RemoveElement( elem );
8967 //================================================================================
8969 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8971 //================================================================================
8973 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8974 vector<const SMDS_MeshNode *> & nodes,
8975 vector<int> & nbNodeInFaces )
8978 nbNodeInFaces.clear();
8979 SMDS_VolumeTool vTool ( elem );
8980 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8982 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8983 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8984 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8989 //=======================================================================
8991 * \brief Convert elements contained in a sub-mesh to quadratic
8992 * \return int - nb of checked elements
8994 //=======================================================================
8996 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8997 SMESH_MesherHelper& theHelper,
8998 const bool theForce3d)
9000 //MESSAGE("convertElemToQuadratic");
9002 if( !theSm ) return nbElem;
9004 vector<int> nbNodeInFaces;
9005 vector<const SMDS_MeshNode *> nodes;
9006 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9007 while(ElemItr->more())
9010 const SMDS_MeshElement* elem = ElemItr->next();
9011 if( !elem ) continue;
9013 // analyse a necessity of conversion
9014 const SMDSAbs_ElementType aType = elem->GetType();
9015 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9017 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9018 bool hasCentralNodes = false;
9019 if ( elem->IsQuadratic() )
9022 switch ( aGeomType ) {
9023 case SMDSEntity_Quad_Triangle:
9024 case SMDSEntity_Quad_Quadrangle:
9025 case SMDSEntity_Quad_Hexa:
9026 case SMDSEntity_Quad_Penta:
9027 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9029 case SMDSEntity_BiQuad_Triangle:
9030 case SMDSEntity_BiQuad_Quadrangle:
9031 case SMDSEntity_TriQuad_Hexa:
9032 case SMDSEntity_BiQuad_Penta:
9033 alreadyOK = theHelper.GetIsBiQuadratic();
9034 hasCentralNodes = true;
9039 // take into account already present medium nodes
9041 case SMDSAbs_Volume:
9042 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9044 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9046 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9052 // get elem data needed to re-create it
9054 const int id = elem->GetID();
9055 const int nbNodes = elem->NbCornerNodes();
9056 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9057 if ( aGeomType == SMDSEntity_Polyhedra )
9058 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >( elem )->GetQuantities();
9059 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9060 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9062 // remove a linear element
9063 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9065 // remove central nodes of biquadratic elements (biquad->quad conversion)
9066 if ( hasCentralNodes )
9067 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9068 if ( nodes[i]->NbInverseElements() == 0 )
9069 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9071 const SMDS_MeshElement* NewElem = 0;
9077 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9085 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9088 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9091 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9095 case SMDSAbs_Volume :
9099 case SMDSEntity_Tetra:
9100 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9102 case SMDSEntity_Pyramid:
9103 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9105 case SMDSEntity_Penta:
9106 case SMDSEntity_Quad_Penta:
9107 case SMDSEntity_BiQuad_Penta:
9108 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9110 case SMDSEntity_Hexa:
9111 case SMDSEntity_Quad_Hexa:
9112 case SMDSEntity_TriQuad_Hexa:
9113 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9114 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9116 case SMDSEntity_Hexagonal_Prism:
9118 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9125 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9126 if( NewElem && NewElem->getshapeId() < 1 )
9127 theSm->AddElement( NewElem );
9131 //=======================================================================
9132 //function : ConvertToQuadratic
9134 //=======================================================================
9136 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9138 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9139 SMESHDS_Mesh* meshDS = GetMeshDS();
9141 SMESH_MesherHelper aHelper(*myMesh);
9143 aHelper.SetIsQuadratic( true );
9144 aHelper.SetIsBiQuadratic( theToBiQuad );
9145 aHelper.SetElementsOnShape(true);
9146 aHelper.ToFixNodeParameters( true );
9148 // convert elements assigned to sub-meshes
9149 int nbCheckedElems = 0;
9150 if ( myMesh->HasShapeToMesh() )
9152 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9154 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9155 while ( smIt->more() ) {
9156 SMESH_subMesh* sm = smIt->next();
9157 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9158 aHelper.SetSubShape( sm->GetSubShape() );
9159 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9165 // convert elements NOT assigned to sub-meshes
9166 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9167 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9169 aHelper.SetElementsOnShape(false);
9170 SMESHDS_SubMesh *smDS = 0;
9173 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9174 while( aEdgeItr->more() )
9176 const SMDS_MeshEdge* edge = aEdgeItr->next();
9177 if ( !edge->IsQuadratic() )
9179 int id = edge->GetID();
9180 const SMDS_MeshNode* n1 = edge->GetNode(0);
9181 const SMDS_MeshNode* n2 = edge->GetNode(1);
9183 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9185 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9186 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9190 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9195 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9196 while( aFaceItr->more() )
9198 const SMDS_MeshFace* face = aFaceItr->next();
9199 if ( !face ) continue;
9201 const SMDSAbs_EntityType type = face->GetEntityType();
9205 case SMDSEntity_Quad_Triangle:
9206 case SMDSEntity_Quad_Quadrangle:
9207 alreadyOK = !theToBiQuad;
9208 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9210 case SMDSEntity_BiQuad_Triangle:
9211 case SMDSEntity_BiQuad_Quadrangle:
9212 alreadyOK = theToBiQuad;
9213 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9215 default: alreadyOK = false;
9220 const int id = face->GetID();
9221 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9223 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9225 SMDS_MeshFace * NewFace = 0;
9228 case SMDSEntity_Triangle:
9229 case SMDSEntity_Quad_Triangle:
9230 case SMDSEntity_BiQuad_Triangle:
9231 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9232 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9233 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9236 case SMDSEntity_Quadrangle:
9237 case SMDSEntity_Quad_Quadrangle:
9238 case SMDSEntity_BiQuad_Quadrangle:
9239 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9240 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9241 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9245 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9247 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9251 vector<int> nbNodeInFaces;
9252 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9253 while(aVolumeItr->more())
9255 const SMDS_MeshVolume* volume = aVolumeItr->next();
9256 if ( !volume ) continue;
9258 const SMDSAbs_EntityType type = volume->GetEntityType();
9259 if ( volume->IsQuadratic() )
9264 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9265 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9266 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
9267 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9268 default: alreadyOK = true;
9272 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9276 const int id = volume->GetID();
9277 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9278 if ( type == SMDSEntity_Polyhedra )
9279 nbNodeInFaces = static_cast<const SMDS_MeshVolume* >(volume)->GetQuantities();
9280 else if ( type == SMDSEntity_Hexagonal_Prism )
9281 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9283 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9285 SMDS_MeshVolume * NewVolume = 0;
9288 case SMDSEntity_Tetra:
9289 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9291 case SMDSEntity_Hexa:
9292 case SMDSEntity_Quad_Hexa:
9293 case SMDSEntity_TriQuad_Hexa:
9294 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9295 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9296 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9297 if ( nodes[i]->NbInverseElements() == 0 )
9298 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9300 case SMDSEntity_Pyramid:
9301 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9302 nodes[3], nodes[4], id, theForce3d);
9304 case SMDSEntity_Penta:
9305 case SMDSEntity_Quad_Penta:
9306 case SMDSEntity_BiQuad_Penta:
9307 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9308 nodes[3], nodes[4], nodes[5], id, theForce3d);
9309 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9310 if ( nodes[i]->NbInverseElements() == 0 )
9311 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9313 case SMDSEntity_Hexagonal_Prism:
9315 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9317 ReplaceElemInGroups(volume, NewVolume, meshDS);
9322 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9323 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9324 // aHelper.FixQuadraticElements(myError);
9325 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9329 //================================================================================
9331 * \brief Makes given elements quadratic
9332 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9333 * \param theElements - elements to make quadratic
9335 //================================================================================
9337 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9338 TIDSortedElemSet& theElements,
9339 const bool theToBiQuad)
9341 if ( theElements.empty() ) return;
9343 // we believe that all theElements are of the same type
9344 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9346 // get all nodes shared by theElements
9347 TIDSortedNodeSet allNodes;
9348 TIDSortedElemSet::iterator eIt = theElements.begin();
9349 for ( ; eIt != theElements.end(); ++eIt )
9350 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9352 // complete theElements with elements of lower dim whose all nodes are in allNodes
9354 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9355 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9356 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9357 for ( ; nIt != allNodes.end(); ++nIt )
9359 const SMDS_MeshNode* n = *nIt;
9360 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9361 while ( invIt->more() )
9363 const SMDS_MeshElement* e = invIt->next();
9364 const SMDSAbs_ElementType type = e->GetType();
9365 if ( e->IsQuadratic() )
9367 quadAdjacentElems[ type ].insert( e );
9370 switch ( e->GetEntityType() ) {
9371 case SMDSEntity_Quad_Triangle:
9372 case SMDSEntity_Quad_Quadrangle:
9373 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9374 case SMDSEntity_BiQuad_Triangle:
9375 case SMDSEntity_BiQuad_Quadrangle:
9376 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9377 default: alreadyOK = true;
9382 if ( type >= elemType )
9383 continue; // same type or more complex linear element
9385 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9386 continue; // e is already checked
9390 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9391 while ( nodeIt->more() && allIn )
9392 allIn = allNodes.count( nodeIt->next() );
9394 theElements.insert(e );
9398 SMESH_MesherHelper helper(*myMesh);
9399 helper.SetIsQuadratic( true );
9400 helper.SetIsBiQuadratic( theToBiQuad );
9402 // add links of quadratic adjacent elements to the helper
9404 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9405 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9406 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9408 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9410 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9411 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9412 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9414 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9416 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9417 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9418 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9420 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9423 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9425 SMESHDS_Mesh* meshDS = GetMeshDS();
9426 SMESHDS_SubMesh* smDS = 0;
9427 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9429 const SMDS_MeshElement* elem = *eIt;
9432 int nbCentralNodes = 0;
9433 switch ( elem->GetEntityType() ) {
9434 // linear convertible
9435 case SMDSEntity_Edge:
9436 case SMDSEntity_Triangle:
9437 case SMDSEntity_Quadrangle:
9438 case SMDSEntity_Tetra:
9439 case SMDSEntity_Pyramid:
9440 case SMDSEntity_Hexa:
9441 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9442 // quadratic that can become bi-quadratic
9443 case SMDSEntity_Quad_Triangle:
9444 case SMDSEntity_Quad_Quadrangle:
9445 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9447 case SMDSEntity_BiQuad_Triangle:
9448 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9449 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9451 default: alreadyOK = true;
9453 if ( alreadyOK ) continue;
9455 const SMDSAbs_ElementType type = elem->GetType();
9456 const int id = elem->GetID();
9457 const int nbNodes = elem->NbCornerNodes();
9458 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9460 helper.SetSubShape( elem->getshapeId() );
9462 if ( !smDS || !smDS->Contains( elem ))
9463 smDS = meshDS->MeshElements( elem->getshapeId() );
9464 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9466 SMDS_MeshElement * newElem = 0;
9469 case 4: // cases for most frequently used element types go first (for optimization)
9470 if ( type == SMDSAbs_Volume )
9471 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9473 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9476 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9477 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9480 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9483 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9486 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9487 nodes[4], id, theForce3d);
9490 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9491 nodes[4], nodes[5], id, theForce3d);
9495 ReplaceElemInGroups( elem, newElem, meshDS);
9496 if( newElem && smDS )
9497 smDS->AddElement( newElem );
9499 // remove central nodes
9500 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9501 if ( nodes[i]->NbInverseElements() == 0 )
9502 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9504 } // loop on theElements
9507 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9508 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9509 // helper.FixQuadraticElements( myError );
9510 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9514 //=======================================================================
9516 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9517 * \return int - nb of checked elements
9519 //=======================================================================
9521 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9522 SMDS_ElemIteratorPtr theItr,
9523 const int theShapeID)
9526 SMESHDS_Mesh* meshDS = GetMeshDS();
9527 ElemFeatures elemType;
9528 vector<const SMDS_MeshNode *> nodes;
9530 while( theItr->more() )
9532 const SMDS_MeshElement* elem = theItr->next();
9534 if( elem && elem->IsQuadratic())
9537 int nbCornerNodes = elem->NbCornerNodes();
9538 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9540 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9542 //remove a quadratic element
9543 if ( !theSm || !theSm->Contains( elem ))
9544 theSm = meshDS->MeshElements( elem->getshapeId() );
9545 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9547 // remove medium nodes
9548 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9549 if ( nodes[i]->NbInverseElements() == 0 )
9550 meshDS->RemoveFreeNode( nodes[i], theSm );
9552 // add a linear element
9553 nodes.resize( nbCornerNodes );
9554 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9555 ReplaceElemInGroups(elem, newElem, meshDS);
9556 if( theSm && newElem )
9557 theSm->AddElement( newElem );
9563 //=======================================================================
9564 //function : ConvertFromQuadratic
9566 //=======================================================================
9568 bool SMESH_MeshEditor::ConvertFromQuadratic()
9570 int nbCheckedElems = 0;
9571 if ( myMesh->HasShapeToMesh() )
9573 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9575 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9576 while ( smIt->more() ) {
9577 SMESH_subMesh* sm = smIt->next();
9578 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9579 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9585 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9586 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9588 SMESHDS_SubMesh *aSM = 0;
9589 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9597 //================================================================================
9599 * \brief Return true if all medium nodes of the element are in the node set
9601 //================================================================================
9603 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9605 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9606 if ( !nodeSet.count( elem->GetNode(i) ))
9612 //================================================================================
9614 * \brief Makes given elements linear
9616 //================================================================================
9618 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9620 if ( theElements.empty() ) return;
9622 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9623 set<int> mediumNodeIDs;
9624 TIDSortedElemSet::iterator eIt = theElements.begin();
9625 for ( ; eIt != theElements.end(); ++eIt )
9627 const SMDS_MeshElement* e = *eIt;
9628 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9629 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9632 // replace given elements by linear ones
9633 SMDS_ElemIteratorPtr elemIt = SMESHUtils::elemSetIterator( theElements );
9634 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9636 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9637 // except those elements sharing medium nodes of quadratic element whose medium nodes
9638 // are not all in mediumNodeIDs
9640 // get remaining medium nodes
9641 TIDSortedNodeSet mediumNodes;
9642 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9643 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9644 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9645 mediumNodes.insert( mediumNodes.end(), n );
9647 // find more quadratic elements to convert
9648 TIDSortedElemSet moreElemsToConvert;
9649 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9650 for ( ; nIt != mediumNodes.end(); ++nIt )
9652 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9653 while ( invIt->more() )
9655 const SMDS_MeshElement* e = invIt->next();
9656 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9658 // find a more complex element including e and
9659 // whose medium nodes are not in mediumNodes
9660 bool complexFound = false;
9661 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9663 SMDS_ElemIteratorPtr invIt2 =
9664 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9665 while ( invIt2->more() )
9667 const SMDS_MeshElement* eComplex = invIt2->next();
9668 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9670 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9671 if ( nbCommonNodes == e->NbNodes())
9673 complexFound = true;
9674 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9680 if ( !complexFound )
9681 moreElemsToConvert.insert( e );
9685 elemIt = SMESHUtils::elemSetIterator( moreElemsToConvert );
9686 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9689 //=======================================================================
9690 //function : SewSideElements
9692 //=======================================================================
9694 SMESH_MeshEditor::Sew_Error
9695 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9696 TIDSortedElemSet& theSide2,
9697 const SMDS_MeshNode* theFirstNode1,
9698 const SMDS_MeshNode* theFirstNode2,
9699 const SMDS_MeshNode* theSecondNode1,
9700 const SMDS_MeshNode* theSecondNode2)
9704 if ( theSide1.size() != theSide2.size() )
9705 return SEW_DIFF_NB_OF_ELEMENTS;
9707 Sew_Error aResult = SEW_OK;
9709 // 1. Build set of faces representing each side
9710 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9711 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9713 // =======================================================================
9714 // 1. Build set of faces representing each side:
9715 // =======================================================================
9716 // a. build set of nodes belonging to faces
9717 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9718 // c. create temporary faces representing side of volumes if correspondent
9719 // face does not exist
9721 SMESHDS_Mesh* aMesh = GetMeshDS();
9722 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9723 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9724 TIDSortedElemSet faceSet1, faceSet2;
9725 set<const SMDS_MeshElement*> volSet1, volSet2;
9726 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9727 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9728 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9729 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9730 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9731 int iSide, iFace, iNode;
9733 list<const SMDS_MeshElement* > tempFaceList;
9734 for ( iSide = 0; iSide < 2; iSide++ ) {
9735 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9736 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9737 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9738 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9739 set<const SMDS_MeshElement*>::iterator vIt;
9740 TIDSortedElemSet::iterator eIt;
9741 set<const SMDS_MeshNode*>::iterator nIt;
9743 // check that given nodes belong to given elements
9744 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9745 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9746 int firstIndex = -1, secondIndex = -1;
9747 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9748 const SMDS_MeshElement* elem = *eIt;
9749 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9750 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9751 if ( firstIndex > -1 && secondIndex > -1 ) break;
9753 if ( firstIndex < 0 || secondIndex < 0 ) {
9754 // we can simply return until temporary faces created
9755 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9758 // -----------------------------------------------------------
9759 // 1a. Collect nodes of existing faces
9760 // and build set of face nodes in order to detect missing
9761 // faces corresponding to sides of volumes
9762 // -----------------------------------------------------------
9764 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9766 // loop on the given element of a side
9767 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9768 //const SMDS_MeshElement* elem = *eIt;
9769 const SMDS_MeshElement* elem = *eIt;
9770 if ( elem->GetType() == SMDSAbs_Face ) {
9771 faceSet->insert( elem );
9772 set <const SMDS_MeshNode*> faceNodeSet;
9773 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9774 while ( nodeIt->more() ) {
9775 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9776 nodeSet->insert( n );
9777 faceNodeSet.insert( n );
9779 setOfFaceNodeSet.insert( faceNodeSet );
9781 else if ( elem->GetType() == SMDSAbs_Volume )
9782 volSet->insert( elem );
9784 // ------------------------------------------------------------------------------
9785 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9786 // ------------------------------------------------------------------------------
9788 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9789 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9790 while ( fIt->more() ) { // loop on faces sharing a node
9791 const SMDS_MeshElement* f = fIt->next();
9792 if ( faceSet->find( f ) == faceSet->end() ) {
9793 // check if all nodes are in nodeSet and
9794 // complete setOfFaceNodeSet if they are
9795 set <const SMDS_MeshNode*> faceNodeSet;
9796 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9797 bool allInSet = true;
9798 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9799 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9800 if ( nodeSet->find( n ) == nodeSet->end() )
9803 faceNodeSet.insert( n );
9806 faceSet->insert( f );
9807 setOfFaceNodeSet.insert( faceNodeSet );
9813 // -------------------------------------------------------------------------
9814 // 1c. Create temporary faces representing sides of volumes if correspondent
9815 // face does not exist
9816 // -------------------------------------------------------------------------
9818 if ( !volSet->empty() ) {
9819 //int nodeSetSize = nodeSet->size();
9821 // loop on given volumes
9822 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9823 SMDS_VolumeTool vol (*vIt);
9824 // loop on volume faces: find free faces
9825 // --------------------------------------
9826 list<const SMDS_MeshElement* > freeFaceList;
9827 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9828 if ( !vol.IsFreeFace( iFace ))
9830 // check if there is already a face with same nodes in a face set
9831 const SMDS_MeshElement* aFreeFace = 0;
9832 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9833 int nbNodes = vol.NbFaceNodes( iFace );
9834 set <const SMDS_MeshNode*> faceNodeSet;
9835 vol.GetFaceNodes( iFace, faceNodeSet );
9836 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9838 // no such a face is given but it still can exist, check it
9839 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9840 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9843 // create a temporary face
9844 if ( nbNodes == 3 ) {
9845 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9846 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9848 else if ( nbNodes == 4 ) {
9849 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9850 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9853 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9854 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9855 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9858 tempFaceList.push_back( aFreeFace );
9862 freeFaceList.push_back( aFreeFace );
9864 } // loop on faces of a volume
9866 // choose one of several free faces of a volume
9867 // --------------------------------------------
9868 if ( freeFaceList.size() > 1 ) {
9869 // choose a face having max nb of nodes shared by other elems of a side
9870 int maxNbNodes = -1;
9871 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9872 while ( fIt != freeFaceList.end() ) { // loop on free faces
9873 int nbSharedNodes = 0;
9874 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9875 while ( nodeIt->more() ) { // loop on free face nodes
9876 const SMDS_MeshNode* n =
9877 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9878 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9879 while ( invElemIt->more() ) {
9880 const SMDS_MeshElement* e = invElemIt->next();
9881 nbSharedNodes += faceSet->count( e );
9882 nbSharedNodes += elemSet->count( e );
9885 if ( nbSharedNodes > maxNbNodes ) {
9886 maxNbNodes = nbSharedNodes;
9887 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9889 else if ( nbSharedNodes == maxNbNodes ) {
9893 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9896 if ( freeFaceList.size() > 1 )
9898 // could not choose one face, use another way
9899 // choose a face most close to the bary center of the opposite side
9900 gp_XYZ aBC( 0., 0., 0. );
9901 set <const SMDS_MeshNode*> addedNodes;
9902 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9903 eIt = elemSet2->begin();
9904 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9905 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9906 while ( nodeIt->more() ) { // loop on free face nodes
9907 const SMDS_MeshNode* n =
9908 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9909 if ( addedNodes.insert( n ).second )
9910 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9913 aBC /= addedNodes.size();
9914 double minDist = DBL_MAX;
9915 fIt = freeFaceList.begin();
9916 while ( fIt != freeFaceList.end() ) { // loop on free faces
9918 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9919 while ( nodeIt->more() ) { // loop on free face nodes
9920 const SMDS_MeshNode* n =
9921 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9922 gp_XYZ p( n->X(),n->Y(),n->Z() );
9923 dist += ( aBC - p ).SquareModulus();
9925 if ( dist < minDist ) {
9927 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9930 fIt = freeFaceList.erase( fIt++ );
9933 } // choose one of several free faces of a volume
9935 if ( freeFaceList.size() == 1 ) {
9936 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9937 faceSet->insert( aFreeFace );
9938 // complete a node set with nodes of a found free face
9939 // for ( iNode = 0; iNode < ; iNode++ )
9940 // nodeSet->insert( fNodes[ iNode ] );
9943 } // loop on volumes of a side
9945 // // complete a set of faces if new nodes in a nodeSet appeared
9946 // // ----------------------------------------------------------
9947 // if ( nodeSetSize != nodeSet->size() ) {
9948 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9949 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9950 // while ( fIt->more() ) { // loop on faces sharing a node
9951 // const SMDS_MeshElement* f = fIt->next();
9952 // if ( faceSet->find( f ) == faceSet->end() ) {
9953 // // check if all nodes are in nodeSet and
9954 // // complete setOfFaceNodeSet if they are
9955 // set <const SMDS_MeshNode*> faceNodeSet;
9956 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9957 // bool allInSet = true;
9958 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9959 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9960 // if ( nodeSet->find( n ) == nodeSet->end() )
9961 // allInSet = false;
9963 // faceNodeSet.insert( n );
9965 // if ( allInSet ) {
9966 // faceSet->insert( f );
9967 // setOfFaceNodeSet.insert( faceNodeSet );
9973 } // Create temporary faces, if there are volumes given
9976 if ( faceSet1.size() != faceSet2.size() ) {
9977 // delete temporary faces: they are in reverseElements of actual nodes
9978 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9979 // while ( tmpFaceIt->more() )
9980 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9981 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9982 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9983 // aMesh->RemoveElement(*tmpFaceIt);
9984 MESSAGE("Diff nb of faces");
9985 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9988 // ============================================================
9989 // 2. Find nodes to merge:
9990 // bind a node to remove to a node to put instead
9991 // ============================================================
9993 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9994 if ( theFirstNode1 != theFirstNode2 )
9995 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9996 if ( theSecondNode1 != theSecondNode2 )
9997 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9999 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10000 set< long > linkIdSet; // links to process
10001 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10003 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10004 list< NLink > linkList[2];
10005 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10006 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10007 // loop on links in linkList; find faces by links and append links
10008 // of the found faces to linkList
10009 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10010 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10012 NLink link[] = { *linkIt[0], *linkIt[1] };
10013 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10014 if ( !linkIdSet.count( linkID ) )
10017 // by links, find faces in the face sets,
10018 // and find indices of link nodes in the found faces;
10019 // in a face set, there is only one or no face sharing a link
10020 // ---------------------------------------------------------------
10022 const SMDS_MeshElement* face[] = { 0, 0 };
10023 vector<const SMDS_MeshNode*> fnodes[2];
10024 int iLinkNode[2][2];
10025 TIDSortedElemSet avoidSet;
10026 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10027 const SMDS_MeshNode* n1 = link[iSide].first;
10028 const SMDS_MeshNode* n2 = link[iSide].second;
10029 //cout << "Side " << iSide << " ";
10030 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10031 // find a face by two link nodes
10032 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10033 *faceSetPtr[ iSide ], avoidSet,
10034 &iLinkNode[iSide][0],
10035 &iLinkNode[iSide][1] );
10036 if ( face[ iSide ])
10038 //cout << " F " << face[ iSide]->GetID() <<endl;
10039 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10040 // put face nodes to fnodes
10041 SMDS_MeshElement::iterator nIt( face[ iSide ]->interlacedNodesIterator() ), nEnd;
10042 fnodes[ iSide ].assign( nIt, nEnd );
10043 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10047 // check similarity of elements of the sides
10048 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10049 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10050 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10051 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10054 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10056 break; // do not return because it's necessary to remove tmp faces
10059 // set nodes to merge
10060 // -------------------
10062 if ( face[0] && face[1] ) {
10063 const int nbNodes = face[0]->NbNodes();
10064 if ( nbNodes != face[1]->NbNodes() ) {
10065 MESSAGE("Diff nb of face nodes");
10066 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10067 break; // do not return because it s necessary to remove tmp faces
10069 bool reverse[] = { false, false }; // order of nodes in the link
10070 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10071 // analyse link orientation in faces
10072 int i1 = iLinkNode[ iSide ][ 0 ];
10073 int i2 = iLinkNode[ iSide ][ 1 ];
10074 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10076 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10077 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10078 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10080 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10081 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10084 // add other links of the faces to linkList
10085 // -----------------------------------------
10087 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10088 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10089 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10090 if ( !iter_isnew.second ) { // already in a set: no need to process
10091 linkIdSet.erase( iter_isnew.first );
10093 else // new in set == encountered for the first time: add
10095 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10096 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10097 linkList[0].push_back ( NLink( n1, n2 ));
10098 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10103 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10106 } // loop on link lists
10108 if ( aResult == SEW_OK &&
10109 ( //linkIt[0] != linkList[0].end() ||
10110 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10111 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10112 " " << (faceSetPtr[1]->empty()));
10113 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10116 // ====================================================================
10117 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10118 // ====================================================================
10120 // delete temporary faces
10121 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10122 // while ( tmpFaceIt->more() )
10123 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10124 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10125 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10126 aMesh->RemoveElement(*tmpFaceIt);
10128 if ( aResult != SEW_OK)
10131 list< int > nodeIDsToRemove;
10132 vector< const SMDS_MeshNode*> nodes;
10133 ElemFeatures elemType;
10135 // loop on nodes replacement map
10136 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10137 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10138 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10140 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10141 nodeIDsToRemove.push_back( nToRemove->GetID() );
10142 // loop on elements sharing nToRemove
10143 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10144 while ( invElemIt->more() ) {
10145 const SMDS_MeshElement* e = invElemIt->next();
10146 // get a new suite of nodes: make replacement
10147 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10148 nodes.resize( nbNodes );
10149 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10150 while ( nIt->more() ) {
10151 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10152 nnIt = nReplaceMap.find( n );
10153 if ( nnIt != nReplaceMap.end() ) {
10155 n = (*nnIt).second;
10159 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10160 // elemIDsToRemove.push_back( e->GetID() );
10164 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10165 aMesh->RemoveElement( e );
10167 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10169 AddToSameGroups( newElem, e, aMesh );
10170 if ( int aShapeId = e->getshapeId() )
10171 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10177 Remove( nodeIDsToRemove, true );
10182 //================================================================================
10184 * \brief Find corresponding nodes in two sets of faces
10185 * \param theSide1 - first face set
10186 * \param theSide2 - second first face
10187 * \param theFirstNode1 - a boundary node of set 1
10188 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10189 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10190 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10191 * \param nReplaceMap - output map of corresponding nodes
10192 * \return bool - is a success or not
10194 //================================================================================
10197 //#define DEBUG_MATCHING_NODES
10200 SMESH_MeshEditor::Sew_Error
10201 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10202 set<const SMDS_MeshElement*>& theSide2,
10203 const SMDS_MeshNode* theFirstNode1,
10204 const SMDS_MeshNode* theFirstNode2,
10205 const SMDS_MeshNode* theSecondNode1,
10206 const SMDS_MeshNode* theSecondNode2,
10207 TNodeNodeMap & nReplaceMap)
10209 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10211 nReplaceMap.clear();
10212 if ( theFirstNode1 != theFirstNode2 )
10213 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10214 if ( theSecondNode1 != theSecondNode2 )
10215 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10217 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10218 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10220 list< NLink > linkList[2];
10221 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10222 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10224 // loop on links in linkList; find faces by links and append links
10225 // of the found faces to linkList
10226 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10227 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10228 NLink link[] = { *linkIt[0], *linkIt[1] };
10229 if ( linkSet.find( link[0] ) == linkSet.end() )
10232 // by links, find faces in the face sets,
10233 // and find indices of link nodes in the found faces;
10234 // in a face set, there is only one or no face sharing a link
10235 // ---------------------------------------------------------------
10237 const SMDS_MeshElement* face[] = { 0, 0 };
10238 list<const SMDS_MeshNode*> notLinkNodes[2];
10239 //bool reverse[] = { false, false }; // order of notLinkNodes
10241 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10243 const SMDS_MeshNode* n1 = link[iSide].first;
10244 const SMDS_MeshNode* n2 = link[iSide].second;
10245 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10246 set< const SMDS_MeshElement* > facesOfNode1;
10247 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10249 // during a loop of the first node, we find all faces around n1,
10250 // during a loop of the second node, we find one face sharing both n1 and n2
10251 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10252 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10253 while ( fIt->more() ) { // loop on faces sharing a node
10254 const SMDS_MeshElement* f = fIt->next();
10255 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10256 ! facesOfNode1.insert( f ).second ) // f encounters twice
10258 if ( face[ iSide ] ) {
10259 MESSAGE( "2 faces per link " );
10260 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10263 faceSet->erase( f );
10265 // get not link nodes
10266 int nbN = f->NbNodes();
10267 if ( f->IsQuadratic() )
10269 nbNodes[ iSide ] = nbN;
10270 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10271 int i1 = f->GetNodeIndex( n1 );
10272 int i2 = f->GetNodeIndex( n2 );
10273 int iEnd = nbN, iBeg = -1, iDelta = 1;
10274 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10276 std::swap( iEnd, iBeg ); iDelta = -1;
10281 if ( i == iEnd ) i = iBeg + iDelta;
10282 if ( i == i1 ) break;
10283 nodes.push_back ( f->GetNode( i ) );
10289 // check similarity of elements of the sides
10290 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10291 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10292 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10293 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10296 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10300 // set nodes to merge
10301 // -------------------
10303 if ( face[0] && face[1] ) {
10304 if ( nbNodes[0] != nbNodes[1] ) {
10305 MESSAGE("Diff nb of face nodes");
10306 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10308 #ifdef DEBUG_MATCHING_NODES
10309 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10310 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10311 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10313 int nbN = nbNodes[0];
10315 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10316 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10317 for ( int i = 0 ; i < nbN - 2; ++i ) {
10318 #ifdef DEBUG_MATCHING_NODES
10319 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10321 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10325 // add other links of the face 1 to linkList
10326 // -----------------------------------------
10328 const SMDS_MeshElement* f0 = face[0];
10329 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10330 for ( int i = 0; i < nbN; i++ )
10332 const SMDS_MeshNode* n2 = f0->GetNode( i );
10333 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10334 linkSet.insert( SMESH_TLink( n1, n2 ));
10335 if ( !iter_isnew.second ) { // already in a set: no need to process
10336 linkSet.erase( iter_isnew.first );
10338 else // new in set == encountered for the first time: add
10340 #ifdef DEBUG_MATCHING_NODES
10341 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10342 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10344 linkList[0].push_back ( NLink( n1, n2 ));
10345 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10350 } // loop on link lists
10355 namespace // automatically find theAffectedElems for DoubleNodes()
10357 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10359 //--------------------------------------------------------------------------------
10360 // Nodes shared by adjacent FissureBorder's.
10361 // 1 node if FissureBorder separates faces
10362 // 2 nodes if FissureBorder separates volumes
10365 const SMDS_MeshNode* _nodes[2];
10368 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10372 _nbNodes = bool( n1 ) + bool( n2 );
10373 if ( _nbNodes == 2 && n1 > n2 )
10374 std::swap( _nodes[0], _nodes[1] );
10376 bool operator<( const SubBorder& other ) const
10378 for ( int i = 0; i < _nbNodes; ++i )
10380 if ( _nodes[i] < other._nodes[i] ) return true;
10381 if ( _nodes[i] > other._nodes[i] ) return false;
10387 //--------------------------------------------------------------------------------
10388 // Map a SubBorder to all FissureBorder it bounds
10389 struct FissureBorder;
10390 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10391 typedef TBorderLinks::iterator TMappedSub;
10393 //--------------------------------------------------------------------------------
10395 * \brief Element border (volume facet or face edge) at a fissure
10397 struct FissureBorder
10399 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10400 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10402 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10403 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10405 FissureBorder( FissureBorder && from ) // move constructor
10407 std::swap( _nodes, from._nodes );
10408 std::swap( _sortedNodes, from._sortedNodes );
10409 _elems[0] = from._elems[0];
10410 _elems[1] = from._elems[1];
10413 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10414 std::vector< const SMDS_MeshElement* > & adjElems)
10415 : _nodes( elemToDuplicate->NbCornerNodes() )
10417 for ( size_t i = 0; i < _nodes.size(); ++i )
10418 _nodes[i] = elemToDuplicate->GetNode( i );
10420 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10421 findAdjacent( type, adjElems );
10424 FissureBorder( const SMDS_MeshNode** nodes,
10425 const size_t nbNodes,
10426 const SMDSAbs_ElementType adjElemsType,
10427 std::vector< const SMDS_MeshElement* > & adjElems)
10428 : _nodes( nodes, nodes + nbNodes )
10430 findAdjacent( adjElemsType, adjElems );
10433 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10434 std::vector< const SMDS_MeshElement* > & adjElems)
10436 _elems[0] = _elems[1] = 0;
10438 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10439 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10440 _elems[i] = adjElems[i];
10443 bool operator<( const FissureBorder& other ) const
10445 return GetSortedNodes() < other.GetSortedNodes();
10448 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10450 if ( _sortedNodes.empty() && !_nodes.empty() )
10452 FissureBorder* me = const_cast<FissureBorder*>( this );
10453 me->_sortedNodes = me->_nodes;
10454 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10456 return _sortedNodes;
10459 size_t NbSub() const
10461 return _nodes.size();
10464 SubBorder Sub(size_t i) const
10466 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10469 void AddSelfTo( TBorderLinks& borderLinks )
10471 _mappedSubs.resize( NbSub() );
10472 for ( size_t i = 0; i < NbSub(); ++i )
10474 TBorderLinks::iterator s2b =
10475 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10476 s2b->second.push_back( this );
10477 _mappedSubs[ i ] = s2b;
10486 const SMDS_MeshElement* GetMarkedElem() const
10488 if ( _nodes.empty() ) return 0; // cleared
10489 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10490 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10494 gp_XYZ GetNorm() const // normal to the border
10497 if ( _nodes.size() == 2 )
10499 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10500 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10502 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10505 gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10506 norm = bordDir ^ avgNorm;
10510 SMESH_NodeXYZ p0( _nodes[0] );
10511 SMESH_NodeXYZ p1( _nodes[1] );
10512 SMESH_NodeXYZ p2( _nodes[2] );
10513 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10515 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10521 void ChooseSide() // mark an _elem located at positive side of fissure
10523 _elems[0]->setIsMarked( true );
10524 gp_XYZ norm = GetNorm();
10525 double maxX = norm.Coord(1);
10526 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10527 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10530 _elems[0]->setIsMarked( false );
10531 _elems[1]->setIsMarked( true );
10535 }; // struct FissureBorder
10537 //--------------------------------------------------------------------------------
10539 * \brief Classifier of elements at fissure edge
10541 class FissureNormal
10543 std::vector< gp_XYZ > _normals;
10547 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10550 _normals.reserve(2);
10551 _normals.push_back( bord.GetNorm() );
10552 if ( _normals.size() == 2 )
10553 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10556 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10559 switch ( _normals.size() ) {
10562 isIn = !isOut( n, _normals[0], elem );
10567 bool in1 = !isOut( n, _normals[0], elem );
10568 bool in2 = !isOut( n, _normals[1], elem );
10569 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10576 //================================================================================
10578 * \brief Classify an element by a plane passing through a node
10580 //================================================================================
10582 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10584 SMESH_NodeXYZ p = n;
10586 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10588 SMESH_NodeXYZ pi = elem->GetNode( i );
10589 sumDot += norm * ( pi - p );
10591 return sumDot < -1e-100;
10594 //================================================================================
10596 * \brief Find FissureBorder's by nodes to duplicate
10598 //================================================================================
10600 void findFissureBorders( const TIDSortedElemSet& theNodes,
10601 std::vector< FissureBorder > & theFissureBorders )
10603 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10604 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10606 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10607 if ( n->NbInverseElements( elemType ) == 0 )
10609 elemType = SMDSAbs_Face;
10610 if ( n->NbInverseElements( elemType ) == 0 )
10613 // unmark elements touching the fissure
10614 for ( ; nIt != theNodes.end(); ++nIt )
10615 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10617 // loop on elements touching the fissure to get their borders belonging to the fissure
10618 std::set< FissureBorder > fissureBorders;
10619 std::vector< const SMDS_MeshElement* > adjElems;
10620 std::vector< const SMDS_MeshNode* > nodes;
10621 SMDS_VolumeTool volTool;
10622 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10624 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10625 while ( invIt->more() )
10627 const SMDS_MeshElement* eInv = invIt->next();
10628 if ( eInv->isMarked() ) continue;
10629 eInv->setIsMarked( true );
10631 if ( elemType == SMDSAbs_Volume )
10633 volTool.Set( eInv );
10634 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10635 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10637 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10638 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10640 bool allOnFissure = true;
10641 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10642 if (( allOnFissure = theNodes.count( nn[ iN ])))
10643 nodes.push_back( nn[ iN ]);
10644 if ( allOnFissure )
10645 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10646 elemType, adjElems )));
10649 else // elemType == SMDSAbs_Face
10651 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10652 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10653 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10655 nn[1] = eInv->GetNode( iN );
10656 onFissure1 = theNodes.count( nn[1] );
10657 if ( onFissure0 && onFissure1 )
10658 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10660 onFissure0 = onFissure1;
10666 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10667 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10668 for ( ; bord != fissureBorders.end(); ++bord )
10670 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10673 } // findFissureBorders()
10675 //================================================================================
10677 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10678 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10679 * \param [in] theNodesNot - nodes not to duplicate
10680 * \param [out] theAffectedElems - the found elements
10682 //================================================================================
10684 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10685 TIDSortedElemSet& theAffectedElems)
10687 if ( theElemsOrNodes.empty() ) return;
10689 // find FissureBorder's
10691 std::vector< FissureBorder > fissure;
10692 std::vector< const SMDS_MeshElement* > elemsByFacet;
10694 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10695 if ( (*elIt)->GetType() == SMDSAbs_Node )
10697 findFissureBorders( theElemsOrNodes, fissure );
10701 fissure.reserve( theElemsOrNodes.size() );
10702 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10703 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10705 if ( fissure.empty() )
10708 // fill borderLinks
10710 TBorderLinks borderLinks;
10712 for ( size_t i = 0; i < fissure.size(); ++i )
10714 fissure[i].AddSelfTo( borderLinks );
10717 // get theAffectedElems
10719 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10720 for ( size_t i = 0; i < fissure.size(); ++i )
10721 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10723 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10724 false, /*markElem=*/true );
10727 std::vector<const SMDS_MeshNode *> facetNodes;
10728 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10729 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10731 // choose a side of fissure
10732 fissure[0].ChooseSide();
10733 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10735 size_t nbCheckedBorders = 0;
10736 while ( nbCheckedBorders < fissure.size() )
10738 // find a FissureBorder to treat
10739 FissureBorder* bord = 0;
10740 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10741 if ( fissure[i].GetMarkedElem() )
10742 bord = & fissure[i];
10743 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10744 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10746 bord = & fissure[i];
10747 bord->ChooseSide();
10748 theAffectedElems.insert( bord->GetMarkedElem() );
10750 if ( !bord ) return;
10751 ++nbCheckedBorders;
10753 // treat FissureBorder's linked to bord
10754 fissureNodes.clear();
10755 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10756 for ( size_t i = 0; i < bord->NbSub(); ++i )
10758 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
10759 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
10760 std::vector< FissureBorder* >& linkedBorders = l2b->second;
10761 const SubBorder& sb = l2b->first;
10762 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
10764 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
10766 for ( int j = 0; j < sb._nbNodes; ++j )
10767 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
10771 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
10772 // until an elem adjacent to a neighbour FissureBorder is found
10773 facetNodes.clear();
10774 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
10775 facetNodes.resize( sb._nbNodes + 1 );
10779 // check if bordElem is adjacent to a neighbour FissureBorder
10780 for ( size_t j = 0; j < linkedBorders.size(); ++j )
10782 FissureBorder* bord2 = linkedBorders[j];
10783 if ( bord2 == bord ) continue;
10784 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
10787 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
10792 // find the next bordElem
10793 const SMDS_MeshElement* nextBordElem = 0;
10794 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
10796 const SMDS_MeshNode* n = bordElem->GetNode( iN );
10797 if ( fissureNodes.count( n )) continue;
10799 facetNodes[ sb._nbNodes ] = n;
10800 elemsByFacet.clear();
10801 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
10803 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
10804 if ( elemsByFacet[ iE ] != bordElem &&
10805 !elemsByFacet[ iE ]->isMarked() )
10807 theAffectedElems.insert( elemsByFacet[ iE ]);
10808 elemsByFacet[ iE ]->setIsMarked( true );
10809 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
10810 nextBordElem = elemsByFacet[ iE ];
10814 bordElem = nextBordElem;
10816 } // while ( bordElem )
10818 linkedBorders.clear(); // not to treat this link any more
10820 } // loop on SubBorder's of a FissureBorder
10824 } // loop on FissureBorder's
10827 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
10829 // mark nodes of theAffectedElems
10830 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
10832 // unmark nodes of the fissure
10833 elIt = theElemsOrNodes.begin();
10834 if ( (*elIt)->GetType() == SMDSAbs_Node )
10835 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
10837 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
10839 std::vector< gp_XYZ > normVec;
10841 // loop on nodes of the fissure, add elements having marked nodes
10842 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
10844 const SMDS_MeshElement* e = (*elIt);
10845 if ( e->GetType() != SMDSAbs_Node )
10846 e->setIsMarked( true ); // avoid adding a fissure element
10848 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
10850 const SMDS_MeshNode* n = e->GetNode( iN );
10851 if ( fissEdgeNodes2Norm.count( n ))
10854 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
10855 while ( invIt->more() )
10857 const SMDS_MeshElement* eInv = invIt->next();
10858 if ( eInv->isMarked() ) continue;
10859 eInv->setIsMarked( true );
10861 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
10862 while( nIt->more() )
10863 if ( nIt->next()->isMarked())
10865 theAffectedElems.insert( eInv );
10866 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
10867 n->setIsMarked( false );
10874 // add elements on the fissure edge
10875 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
10876 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
10878 const SMDS_MeshNode* edgeNode = n2N->first;
10879 const FissureNormal & normals = n2N->second;
10881 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
10882 while ( invIt->more() )
10884 const SMDS_MeshElement* eInv = invIt->next();
10885 if ( eInv->isMarked() ) continue;
10886 eInv->setIsMarked( true );
10888 // classify eInv using normals
10889 bool toAdd = normals.IsIn( edgeNode, eInv );
10890 if ( toAdd ) // check if all nodes lie on the fissure edge
10892 bool notOnEdge = false;
10893 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
10894 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
10899 theAffectedElems.insert( eInv );
10905 } // findAffectedElems()
10908 //================================================================================
10910 * \brief Create elements equal (on same nodes) to given ones
10911 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10912 * elements of the uppest dimension are duplicated.
10914 //================================================================================
10916 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10918 ClearLastCreated();
10919 SMESHDS_Mesh* mesh = GetMeshDS();
10921 // get an element type and an iterator over elements
10923 SMDSAbs_ElementType type = SMDSAbs_All;
10924 SMDS_ElemIteratorPtr elemIt;
10925 if ( theElements.empty() )
10927 if ( mesh->NbNodes() == 0 )
10929 // get most complex type
10930 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10931 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10932 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10934 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10935 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10938 elemIt = mesh->elementsIterator( type );
10944 //type = (*theElements.begin())->GetType();
10945 elemIt = SMESHUtils::elemSetIterator( theElements );
10948 // un-mark all elements to avoid duplicating just created elements
10949 SMESH_MeshAlgos::MarkElems( mesh->elementsIterator( type ), false );
10951 // duplicate elements
10953 ElemFeatures elemType;
10955 vector< const SMDS_MeshNode* > nodes;
10956 while ( elemIt->more() )
10958 const SMDS_MeshElement* elem = elemIt->next();
10959 if (( type != SMDSAbs_All && elem->GetType() != type ) ||
10960 ( elem->isMarked() ))
10963 elemType.Init( elem, /*basicOnly=*/false );
10964 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10966 if ( const SMDS_MeshElement* newElem = AddElement( nodes, elemType ))
10967 newElem->setIsMarked( true );
10971 //================================================================================
10973 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10974 \param theElems - the list of elements (edges or faces) to be replicated
10975 The nodes for duplication could be found from these elements
10976 \param theNodesNot - list of nodes to NOT replicate
10977 \param theAffectedElems - the list of elements (cells and edges) to which the
10978 replicated nodes should be associated to.
10979 \return TRUE if operation has been completed successfully, FALSE otherwise
10981 //================================================================================
10983 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10984 const TIDSortedElemSet& theNodesNot,
10985 const TIDSortedElemSet& theAffectedElems )
10987 ClearLastCreated();
10989 if ( theElems.size() == 0 )
10992 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10997 TNodeNodeMap anOldNodeToNewNode;
10998 // duplicate elements and nodes
10999 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
11000 // replce nodes by duplications
11001 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
11005 //================================================================================
11007 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11008 \param theMeshDS - mesh instance
11009 \param theElems - the elements replicated or modified (nodes should be changed)
11010 \param theNodesNot - nodes to NOT replicate
11011 \param theNodeNodeMap - relation of old node to new created node
11012 \param theIsDoubleElem - flag os to replicate element or modify
11013 \return TRUE if operation has been completed successfully, FALSE otherwise
11015 //================================================================================
11017 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
11018 const TIDSortedElemSet& theElems,
11019 const TIDSortedElemSet& theNodesNot,
11020 TNodeNodeMap& theNodeNodeMap,
11021 const bool theIsDoubleElem )
11023 // iterate through element and duplicate them (by nodes duplication)
11025 std::vector<const SMDS_MeshNode*> newNodes;
11026 ElemFeatures elemType;
11028 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11029 for ( ; elemItr != theElems.end(); ++elemItr )
11031 const SMDS_MeshElement* anElem = *elemItr;
11035 // duplicate nodes to duplicate element
11036 bool isDuplicate = false;
11037 newNodes.resize( anElem->NbNodes() );
11038 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11040 while ( anIter->more() )
11042 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
11043 const SMDS_MeshNode* aNewNode = aCurrNode;
11044 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
11045 if ( n2n != theNodeNodeMap.end() )
11047 aNewNode = n2n->second;
11049 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
11052 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
11053 copyPosition( aCurrNode, aNewNode );
11054 theNodeNodeMap[ aCurrNode ] = aNewNode;
11055 myLastCreatedNodes.push_back( aNewNode );
11057 isDuplicate |= (aCurrNode != aNewNode);
11058 newNodes[ ind++ ] = aNewNode;
11060 if ( !isDuplicate )
11063 if ( theIsDoubleElem )
11064 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
11066 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
11073 //================================================================================
11075 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11076 \param theNodes - identifiers of nodes to be doubled
11077 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
11078 nodes. If list of element identifiers is empty then nodes are doubled but
11079 they not assigned to elements
11080 \return TRUE if operation has been completed successfully, FALSE otherwise
11082 //================================================================================
11084 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
11085 const std::list< int >& theListOfModifiedElems )
11087 ClearLastCreated();
11089 if ( theListOfNodes.size() == 0 )
11092 SMESHDS_Mesh* aMeshDS = GetMeshDS();
11096 // iterate through nodes and duplicate them
11098 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
11100 std::list< int >::const_iterator aNodeIter;
11101 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
11103 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
11109 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11112 copyPosition( aNode, aNewNode );
11113 anOldNodeToNewNode[ aNode ] = aNewNode;
11114 myLastCreatedNodes.push_back( aNewNode );
11118 // Change nodes of elements
11120 std::vector<const SMDS_MeshNode*> aNodeArr;
11122 std::list< int >::const_iterator anElemIter;
11123 for ( anElemIter = theListOfModifiedElems.begin();
11124 anElemIter != theListOfModifiedElems.end();
11127 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
11131 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
11132 for( size_t i = 0; i < aNodeArr.size(); ++i )
11134 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
11135 anOldNodeToNewNode.find( aNodeArr[ i ]);
11136 if ( n2n != anOldNodeToNewNode.end() )
11137 aNodeArr[ i ] = n2n->second;
11139 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
11147 //================================================================================
11149 \brief Check if element located inside shape
11150 \return TRUE if IN or ON shape, FALSE otherwise
11152 //================================================================================
11154 template<class Classifier>
11155 bool isInside(const SMDS_MeshElement* theElem,
11156 Classifier& theClassifier,
11157 const double theTol)
11159 gp_XYZ centerXYZ (0, 0, 0);
11160 for ( SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); aNodeItr->more(); )
11161 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
11163 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11164 theClassifier.Perform(aPnt, theTol);
11165 TopAbs_State aState = theClassifier.State();
11166 return (aState == TopAbs_IN || aState == TopAbs_ON );
11169 //================================================================================
11171 * \brief Classifier of the 3D point on the TopoDS_Face
11172 * with interaface suitable for isInside()
11174 //================================================================================
11176 struct _FaceClassifier
11178 Extrema_ExtPS _extremum;
11179 BRepAdaptor_Surface _surface;
11180 TopAbs_State _state;
11182 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11184 _extremum.Initialize( _surface,
11185 _surface.FirstUParameter(), _surface.LastUParameter(),
11186 _surface.FirstVParameter(), _surface.LastVParameter(),
11187 _surface.Tolerance(), _surface.Tolerance() );
11189 void Perform(const gp_Pnt& aPnt, double theTol)
11192 _state = TopAbs_OUT;
11193 _extremum.Perform(aPnt);
11194 if ( _extremum.IsDone() )
11195 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11196 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11198 TopAbs_State State() const
11205 //================================================================================
11207 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11208 This method is the first step of DoubleNodeElemGroupsInRegion.
11209 \param theElems - list of groups of elements (edges or faces) to be replicated
11210 \param theNodesNot - list of groups of nodes not to replicated
11211 \param theShape - shape to detect affected elements (element which geometric center
11212 located on or inside shape). If the shape is null, detection is done on faces orientations
11213 (select elements with a gravity center on the side given by faces normals).
11214 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11215 The replicated nodes should be associated to affected elements.
11217 \sa DoubleNodeElemGroupsInRegion()
11219 //================================================================================
11221 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11222 const TIDSortedElemSet& theNodesNot,
11223 const TopoDS_Shape& theShape,
11224 TIDSortedElemSet& theAffectedElems)
11226 if ( theShape.IsNull() )
11228 findAffectedElems( theElems, theAffectedElems );
11232 const double aTol = Precision::Confusion();
11233 std::unique_ptr< BRepClass3d_SolidClassifier> bsc3d;
11234 std::unique_ptr<_FaceClassifier> aFaceClassifier;
11235 if ( theShape.ShapeType() == TopAbs_SOLID )
11237 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11238 bsc3d->PerformInfinitePoint(aTol);
11240 else if (theShape.ShapeType() == TopAbs_FACE )
11242 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11245 // iterates on indicated elements and get elements by back references from their nodes
11246 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11247 for ( ; elemItr != theElems.end(); ++elemItr )
11249 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11250 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11251 while ( nodeItr->more() )
11253 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11254 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11256 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11257 while ( backElemItr->more() )
11259 const SMDS_MeshElement* curElem = backElemItr->next();
11260 if ( curElem && theElems.find(curElem) == theElems.end() &&
11262 isInside( curElem, *bsc3d, aTol ) :
11263 isInside( curElem, *aFaceClassifier, aTol )))
11264 theAffectedElems.insert( curElem );
11272 //================================================================================
11274 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11275 \param theElems - group of of elements (edges or faces) to be replicated
11276 \param theNodesNot - group of nodes not to replicate
11277 \param theShape - shape to detect affected elements (element which geometric center
11278 located on or inside shape).
11279 The replicated nodes should be associated to affected elements.
11280 \return TRUE if operation has been completed successfully, FALSE otherwise
11282 //================================================================================
11284 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11285 const TIDSortedElemSet& theNodesNot,
11286 const TopoDS_Shape& theShape )
11288 if ( theShape.IsNull() )
11291 const double aTol = Precision::Confusion();
11292 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11293 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
11294 if ( theShape.ShapeType() == TopAbs_SOLID )
11296 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11297 bsc3d->PerformInfinitePoint(aTol);
11299 else if (theShape.ShapeType() == TopAbs_FACE )
11301 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11304 // iterates on indicated elements and get elements by back references from their nodes
11305 TIDSortedElemSet anAffected;
11306 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11307 for ( ; elemItr != theElems.end(); ++elemItr )
11309 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11313 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11314 while ( nodeItr->more() )
11316 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11317 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11319 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11320 while ( backElemItr->more() )
11322 const SMDS_MeshElement* curElem = backElemItr->next();
11323 if ( curElem && theElems.find(curElem) == theElems.end() &&
11325 isInside( curElem, *bsc3d, aTol ) :
11326 isInside( curElem, *aFaceClassifier, aTol )))
11327 anAffected.insert( curElem );
11331 return DoubleNodes( theElems, theNodesNot, anAffected );
11335 * \brief compute an oriented angle between two planes defined by four points.
11336 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11337 * @param p0 base of the rotation axe
11338 * @param p1 extremity of the rotation axe
11339 * @param g1 belongs to the first plane
11340 * @param g2 belongs to the second plane
11342 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11344 gp_Vec vref(p0, p1);
11347 gp_Vec n1 = vref.Crossed(v1);
11348 gp_Vec n2 = vref.Crossed(v2);
11350 return n2.AngleWithRef(n1, vref);
11352 catch ( Standard_Failure ) {
11354 return Max( v1.Magnitude(), v2.Magnitude() );
11358 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11359 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11360 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11361 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11362 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11363 * 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.
11364 * 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.
11365 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11366 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11367 * \param theElems - list of groups of volumes, where a group of volume is a set of
11368 * SMDS_MeshElements sorted by Id.
11369 * \param createJointElems - if TRUE, create the elements
11370 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11371 * the boundary between \a theDomains and the rest mesh
11372 * \return TRUE if operation has been completed successfully, FALSE otherwise
11374 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11375 bool createJointElems,
11376 bool onAllBoundaries)
11378 // MESSAGE("----------------------------------------------");
11379 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11380 // MESSAGE("----------------------------------------------");
11382 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11383 meshDS->BuildDownWardConnectivity(true);
11385 SMDS_UnstructuredGrid *grid = meshDS->GetGrid();
11387 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11388 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11389 // build the list of nodes shared by 2 or more domains, with their domain indexes
11391 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11392 std::map<int,int>celldom; // cell vtkId --> domain
11393 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11394 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11395 faceDomains.clear();
11397 cellDomains.clear();
11398 nodeDomains.clear();
11399 std::map<int,int> emptyMap;
11400 std::set<int> emptySet;
11403 //MESSAGE(".. Number of domains :"<<theElems.size());
11405 TIDSortedElemSet theRestDomElems;
11406 const int iRestDom = -1;
11407 const int idom0 = onAllBoundaries ? iRestDom : 0;
11408 const int nbDomains = theElems.size();
11410 // Check if the domains do not share an element
11411 for (int idom = 0; idom < nbDomains-1; idom++)
11413 // MESSAGE("... Check of domain #" << idom);
11414 const TIDSortedElemSet& domain = theElems[idom];
11415 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11416 for (; elemItr != domain.end(); ++elemItr)
11418 const SMDS_MeshElement* anElem = *elemItr;
11419 int idombisdeb = idom + 1 ;
11420 // check if the element belongs to a domain further in the list
11421 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11423 const TIDSortedElemSet& domainbis = theElems[idombis];
11424 if ( domainbis.count( anElem ))
11426 MESSAGE(".... Domain #" << idom);
11427 MESSAGE(".... Domain #" << idombis);
11428 throw SALOME_Exception("The domains are not disjoint.");
11435 for (int idom = 0; idom < nbDomains; idom++)
11438 // --- build a map (face to duplicate --> volume to modify)
11439 // with all the faces shared by 2 domains (group of elements)
11440 // and corresponding volume of this domain, for each shared face.
11441 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11443 //MESSAGE("... Neighbors of domain #" << idom);
11444 const TIDSortedElemSet& domain = theElems[idom];
11445 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11446 for (; elemItr != domain.end(); ++elemItr)
11448 const SMDS_MeshElement* anElem = *elemItr;
11451 int vtkId = anElem->GetVtkID();
11452 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11453 int neighborsVtkIds[NBMAXNEIGHBORS];
11454 int downIds[NBMAXNEIGHBORS];
11455 unsigned char downTypes[NBMAXNEIGHBORS];
11456 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11457 for (int n = 0; n < nbNeighbors; n++)
11459 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
11460 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11461 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11464 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11466 // MESSAGE("Domain " << idombis);
11467 const TIDSortedElemSet& domainbis = theElems[idombis];
11468 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11470 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11472 DownIdType face(downIds[n], downTypes[n]);
11473 if (!faceDomains[face].count(idom))
11475 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11476 celldom[vtkId] = idom;
11477 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11481 theRestDomElems.insert( elem );
11482 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11483 celldom[neighborsVtkIds[n]] = iRestDom;
11491 //MESSAGE("Number of shared faces " << faceDomains.size());
11492 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11494 // --- explore the shared faces domain by domain,
11495 // explore the nodes of the face and see if they belong to a cell in the domain,
11496 // which has only a node or an edge on the border (not a shared face)
11498 for (int idomain = idom0; idomain < nbDomains; idomain++)
11500 //MESSAGE("Domain " << idomain);
11501 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11502 itface = faceDomains.begin();
11503 for (; itface != faceDomains.end(); ++itface)
11505 const std::map<int, int>& domvol = itface->second;
11506 if (!domvol.count(idomain))
11508 DownIdType face = itface->first;
11509 //MESSAGE(" --- face " << face.cellId);
11510 std::set<int> oldNodes;
11512 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11513 std::set<int>::iterator itn = oldNodes.begin();
11514 for (; itn != oldNodes.end(); ++itn)
11517 //MESSAGE(" node " << oldId);
11518 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11519 for (int i=0; i<l.ncells; i++)
11521 int vtkId = l.cells[i];
11522 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->FromVtkToSmds(vtkId));
11523 if (!domain.count(anElem))
11525 int vtkType = grid->GetCellType(vtkId);
11526 int downId = grid->CellIdToDownId(vtkId);
11529 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11530 continue; // not OK at this stage of the algorithm:
11531 //no cells created after BuildDownWardConnectivity
11533 DownIdType aCell(downId, vtkType);
11534 cellDomains[aCell][idomain] = vtkId;
11535 celldom[vtkId] = idomain;
11536 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11542 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11543 // for each shared face, get the nodes
11544 // for each node, for each domain of the face, create a clone of the node
11546 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11547 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11548 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11550 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11551 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11552 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11554 //MESSAGE(".. Duplication of the nodes");
11555 for (int idomain = idom0; idomain < nbDomains; idomain++)
11557 itface = faceDomains.begin();
11558 for (; itface != faceDomains.end(); ++itface)
11560 const std::map<int, int>& domvol = itface->second;
11561 if (!domvol.count(idomain))
11563 DownIdType face = itface->first;
11564 //MESSAGE(" --- face " << face.cellId);
11565 std::set<int> oldNodes;
11567 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11568 std::set<int>::iterator itn = oldNodes.begin();
11569 for (; itn != oldNodes.end(); ++itn)
11572 if (nodeDomains[oldId].empty())
11574 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11575 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11577 std::map<int, int>::const_iterator itdom = domvol.begin();
11578 for (; itdom != domvol.end(); ++itdom)
11580 int idom = itdom->first;
11581 //MESSAGE(" domain " << idom);
11582 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11584 if (nodeDomains[oldId].size() >= 2) // a multiple node
11586 vector<int> orderedDoms;
11587 //MESSAGE("multiple node " << oldId);
11588 if (mutipleNodes.count(oldId))
11589 orderedDoms = mutipleNodes[oldId];
11592 map<int,int>::iterator it = nodeDomains[oldId].begin();
11593 for (; it != nodeDomains[oldId].end(); ++it)
11594 orderedDoms.push_back(it->first);
11596 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11597 //stringstream txt;
11598 //for (int i=0; i<orderedDoms.size(); i++)
11599 // txt << orderedDoms[i] << " ";
11600 //MESSAGE("orderedDoms " << txt.str());
11601 mutipleNodes[oldId] = orderedDoms;
11603 double *coords = grid->GetPoint(oldId);
11604 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11605 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11606 int newId = newNode->GetVtkID();
11607 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11608 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11615 //MESSAGE(".. Creation of elements");
11616 for (int idomain = idom0; idomain < nbDomains; idomain++)
11618 itface = faceDomains.begin();
11619 for (; itface != faceDomains.end(); ++itface)
11621 std::map<int, int> domvol = itface->second;
11622 if (!domvol.count(idomain))
11624 DownIdType face = itface->first;
11625 //MESSAGE(" --- face " << face.cellId);
11626 std::set<int> oldNodes;
11628 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11629 int nbMultipleNodes = 0;
11630 std::set<int>::iterator itn = oldNodes.begin();
11631 for (; itn != oldNodes.end(); ++itn)
11634 if (mutipleNodes.count(oldId))
11637 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11639 //MESSAGE("multiple Nodes detected on a shared face");
11640 int downId = itface->first.cellId;
11641 unsigned char cellType = itface->first.cellType;
11642 // --- shared edge or shared face ?
11643 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11646 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11647 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11648 if (mutipleNodes.count(nodes[i]))
11649 if (!mutipleNodesToFace.count(nodes[i]))
11650 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11652 else // shared face (between two volumes)
11654 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11655 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11656 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11657 for (int ie =0; ie < nbEdges; ie++)
11660 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11661 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11663 vector<int> vn0 = mutipleNodes[nodes[0]];
11664 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11666 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11667 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11668 if ( vn0[i0] == vn1[i1] )
11669 doms.push_back( vn0[ i0 ]);
11670 if ( doms.size() > 2 )
11672 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11673 double *coords = grid->GetPoint(nodes[0]);
11674 gp_Pnt p0(coords[0], coords[1], coords[2]);
11675 coords = grid->GetPoint(nodes[nbNodes - 1]);
11676 gp_Pnt p1(coords[0], coords[1], coords[2]);
11678 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11679 map<int, SMDS_MeshVolume*> domvol; // domain --> a volume with the edge
11680 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11681 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11682 for ( size_t id = 0; id < doms.size(); id++ )
11684 int idom = doms[id];
11685 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11686 for ( int ivol = 0; ivol < nbvol; ivol++ )
11688 int smdsId = meshDS->FromVtkToSmds(vtkVolIds[ivol]);
11689 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11690 if (domain.count(elem))
11692 const SMDS_MeshVolume* svol = SMDS_Mesh::DownCast<SMDS_MeshVolume>(elem);
11693 domvol[idom] = (SMDS_MeshVolume*) svol;
11694 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11695 double values[3] = { 0,0,0 };
11696 vtkIdType npts = 0;
11697 vtkIdType* pts = 0;
11698 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11699 for ( vtkIdType i = 0; i < npts; ++i )
11701 double *coords = grid->GetPoint( pts[i] );
11702 for ( int j = 0; j < 3; ++j )
11703 values[j] += coords[j] / npts;
11707 gref.SetCoord( values[0], values[1], values[2] );
11708 angleDom[idom] = 0;
11712 gp_Pnt g( values[0], values[1], values[2] );
11713 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11714 //MESSAGE(" angle=" << angleDom[idom]);
11720 map<double, int> sortedDom; // sort domains by angle
11721 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11722 sortedDom[ia->second] = ia->first;
11723 vector<int> vnodes;
11725 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11727 vdom.push_back(ib->second);
11728 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11730 for (int ino = 0; ino < nbNodes; ino++)
11731 vnodes.push_back(nodes[ino]);
11732 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11741 // --- iterate on shared faces (volumes to modify, face to extrude)
11742 // get node id's of the face (id SMDS = id VTK)
11743 // create flat element with old and new nodes if requested
11745 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11746 // (domain1 X domain2) = domain1 + MAXINT*domain2
11748 std::map<int, std::map<long,int> > nodeQuadDomains;
11749 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11751 //MESSAGE(".. Creation of elements: simple junction");
11752 if (createJointElems)
11754 string joints2DName = "joints2D";
11755 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str());
11756 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11757 string joints3DName = "joints3D";
11758 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str());
11759 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11761 itface = faceDomains.begin();
11762 for (; itface != faceDomains.end(); ++itface)
11764 DownIdType face = itface->first;
11765 std::set<int> oldNodes;
11766 std::set<int>::iterator itn;
11768 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11770 std::map<int, int> domvol = itface->second;
11771 std::map<int, int>::iterator itdom = domvol.begin();
11772 int dom1 = itdom->first;
11773 int vtkVolId = itdom->second;
11775 int dom2 = itdom->first;
11776 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11778 stringstream grpname;
11781 grpname << dom1 << "_" << dom2;
11783 grpname << dom2 << "_" << dom1;
11784 string namegrp = grpname.str();
11785 if (!mapOfJunctionGroups.count(namegrp))
11786 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str());
11787 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11789 sgrp->Add(vol->GetID());
11790 if (vol->GetType() == SMDSAbs_Volume)
11791 joints3DGrp->Add(vol->GetID());
11792 else if (vol->GetType() == SMDSAbs_Face)
11793 joints2DGrp->Add(vol->GetID());
11797 // --- create volumes on multiple domain intersection if requested
11798 // iterate on mutipleNodesToFace
11799 // iterate on edgesMultiDomains
11801 //MESSAGE(".. Creation of elements: multiple junction");
11802 if (createJointElems)
11804 // --- iterate on mutipleNodesToFace
11806 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11807 for (; itn != mutipleNodesToFace.end(); ++itn)
11809 int node = itn->first;
11810 vector<int> orderDom = itn->second;
11811 vector<vtkIdType> orderedNodes;
11812 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11813 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11814 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11816 stringstream grpname;
11818 grpname << 0 << "_" << 0;
11819 string namegrp = grpname.str();
11820 if (!mapOfJunctionGroups.count(namegrp))
11821 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str());
11822 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11824 sgrp->Add(face->GetID());
11827 // --- iterate on edgesMultiDomains
11829 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11830 for (; ite != edgesMultiDomains.end(); ++ite)
11832 vector<int> nodes = ite->first;
11833 vector<int> orderDom = ite->second;
11834 vector<vtkIdType> orderedNodes;
11835 if (nodes.size() == 2)
11837 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11838 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11839 if ( orderDom.size() == 3 )
11840 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11841 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11843 for (int idom = orderDom.size()-1; idom >=0; idom--)
11844 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11845 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11847 string namegrp = "jointsMultiples";
11848 if (!mapOfJunctionGroups.count(namegrp))
11849 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
11850 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11852 sgrp->Add(vol->GetID());
11856 //INFOS("Quadratic multiple joints not implemented");
11857 // TODO quadratic nodes
11862 // --- list the explicit faces and edges of the mesh that need to be modified,
11863 // i.e. faces and edges built with one or more duplicated nodes.
11864 // associate these faces or edges to their corresponding domain.
11865 // only the first domain found is kept when a face or edge is shared
11867 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11868 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11869 faceOrEdgeDom.clear();
11872 //MESSAGE(".. Modification of elements");
11873 for (int idomain = idom0; idomain < nbDomains; idomain++)
11875 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11876 for (; itnod != nodeDomains.end(); ++itnod)
11878 int oldId = itnod->first;
11879 //MESSAGE(" node " << oldId);
11880 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11881 for (int i = 0; i < l.ncells; i++)
11883 int vtkId = l.cells[i];
11884 int vtkType = grid->GetCellType(vtkId);
11885 int downId = grid->CellIdToDownId(vtkId);
11887 continue; // new cells: not to be modified
11888 DownIdType aCell(downId, vtkType);
11889 int volParents[1000];
11890 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11891 for (int j = 0; j < nbvol; j++)
11892 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11893 if (!feDom.count(vtkId))
11895 feDom[vtkId] = idomain;
11896 faceOrEdgeDom[aCell] = emptyMap;
11897 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11898 //MESSAGE("affect cell " << this->GetMeshDS()->FromVtkToSmds(vtkId) << " domain " << idomain
11899 // << " type " << vtkType << " downId " << downId);
11905 // --- iterate on shared faces (volumes to modify, face to extrude)
11906 // get node id's of the face
11907 // replace old nodes by new nodes in volumes, and update inverse connectivity
11909 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11910 for (int m=0; m<3; m++)
11912 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11913 itface = (*amap).begin();
11914 for (; itface != (*amap).end(); ++itface)
11916 DownIdType face = itface->first;
11917 std::set<int> oldNodes;
11918 std::set<int>::iterator itn;
11920 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11921 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11922 std::map<int, int> localClonedNodeIds;
11924 std::map<int, int> domvol = itface->second;
11925 std::map<int, int>::iterator itdom = domvol.begin();
11926 for (; itdom != domvol.end(); ++itdom)
11928 int idom = itdom->first;
11929 int vtkVolId = itdom->second;
11930 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->FromVtkToSmds(vtkVolId) << " domain " << idom);
11931 localClonedNodeIds.clear();
11932 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11935 if (nodeDomains[oldId].count(idom))
11937 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11938 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11941 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11946 // Remove empty groups (issue 0022812)
11947 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11948 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11950 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11951 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11954 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11955 grid->DeleteLinks();
11963 * \brief Double nodes on some external faces and create flat elements.
11964 * Flat elements are mainly used by some types of mechanic calculations.
11966 * Each group of the list must be constituted of faces.
11967 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11968 * @param theElems - list of groups of faces, where a group of faces is a set of
11969 * SMDS_MeshElements sorted by Id.
11970 * @return TRUE if operation has been completed successfully, FALSE otherwise
11972 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11974 // MESSAGE("-------------------------------------------------");
11975 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11976 // MESSAGE("-------------------------------------------------");
11978 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11980 // --- For each group of faces
11981 // duplicate the nodes, create a flat element based on the face
11982 // replace the nodes of the faces by their clones
11984 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11985 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11986 clonedNodes.clear();
11987 intermediateNodes.clear();
11988 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11989 mapOfJunctionGroups.clear();
11991 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11993 const TIDSortedElemSet& domain = theElems[idom];
11994 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11995 for ( ; elemItr != domain.end(); ++elemItr )
11997 const SMDS_MeshFace* aFace = meshDS->DownCast<SMDS_MeshFace> ( *elemItr );
12000 // MESSAGE("aFace=" << aFace->GetID());
12001 bool isQuad = aFace->IsQuadratic();
12002 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
12004 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
12006 SMDS_NodeIteratorPtr nodeIt = aFace->nodeIterator();
12007 while (nodeIt->more())
12009 const SMDS_MeshNode* node = nodeIt->next();
12010 bool isMedium = ( isQuad && aFace->IsMediumNode( node ));
12012 ln2.push_back(node);
12014 ln0.push_back(node);
12016 const SMDS_MeshNode* clone = 0;
12017 if (!clonedNodes.count(node))
12019 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12020 copyPosition( node, clone );
12021 clonedNodes[node] = clone;
12024 clone = clonedNodes[node];
12027 ln3.push_back(clone);
12029 ln1.push_back(clone);
12031 const SMDS_MeshNode* inter = 0;
12032 if (isQuad && (!isMedium))
12034 if (!intermediateNodes.count(node))
12036 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12037 copyPosition( node, inter );
12038 intermediateNodes[node] = inter;
12041 inter = intermediateNodes[node];
12042 ln4.push_back(inter);
12046 // --- extrude the face
12048 vector<const SMDS_MeshNode*> ln;
12049 SMDS_MeshVolume* vol = 0;
12050 vtkIdType aType = aFace->GetVtkType();
12054 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12055 // MESSAGE("vol prism " << vol->GetID());
12056 ln.push_back(ln1[0]);
12057 ln.push_back(ln1[1]);
12058 ln.push_back(ln1[2]);
12061 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12062 // MESSAGE("vol hexa " << vol->GetID());
12063 ln.push_back(ln1[0]);
12064 ln.push_back(ln1[1]);
12065 ln.push_back(ln1[2]);
12066 ln.push_back(ln1[3]);
12068 case VTK_QUADRATIC_TRIANGLE:
12069 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12070 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12071 // MESSAGE("vol quad prism " << vol->GetID());
12072 ln.push_back(ln1[0]);
12073 ln.push_back(ln1[1]);
12074 ln.push_back(ln1[2]);
12075 ln.push_back(ln3[0]);
12076 ln.push_back(ln3[1]);
12077 ln.push_back(ln3[2]);
12079 case VTK_QUADRATIC_QUAD:
12080 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12081 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12082 // ln4[0], ln4[1], ln4[2], ln4[3]);
12083 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12084 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12085 ln4[0], ln4[1], ln4[2], ln4[3]);
12086 // MESSAGE("vol quad hexa " << vol->GetID());
12087 ln.push_back(ln1[0]);
12088 ln.push_back(ln1[1]);
12089 ln.push_back(ln1[2]);
12090 ln.push_back(ln1[3]);
12091 ln.push_back(ln3[0]);
12092 ln.push_back(ln3[1]);
12093 ln.push_back(ln3[2]);
12094 ln.push_back(ln3[3]);
12104 stringstream grpname;
12107 string namegrp = grpname.str();
12108 if (!mapOfJunctionGroups.count(namegrp))
12109 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str());
12110 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12112 sgrp->Add(vol->GetID());
12115 // --- modify the face
12117 const_cast<SMDS_MeshFace*>( aFace )->ChangeNodes( &ln[0], ln.size() );
12124 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
12125 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
12126 * groups of faces to remove inside the object, (idem edges).
12127 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12129 void SMESH_MeshEditor::CreateHoleSkin(double radius,
12130 const TopoDS_Shape& theShape,
12131 SMESH_NodeSearcher* theNodeSearcher,
12132 const char* groupName,
12133 std::vector<double>& nodesCoords,
12134 std::vector<std::vector<int> >& listOfListOfNodes)
12136 // MESSAGE("--------------------------------");
12137 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12138 // MESSAGE("--------------------------------");
12140 // --- zone of volumes to remove is given :
12141 // 1 either by a geom shape (one or more vertices) and a radius,
12142 // 2 either by a group of nodes (representative of the shape)to use with the radius,
12143 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12144 // In the case 2, the group of nodes is an external group of nodes from another mesh,
12145 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12146 // defined by it's name.
12148 SMESHDS_GroupBase* groupDS = 0;
12149 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12150 while ( groupIt->more() )
12153 SMESH_Group * group = groupIt->next();
12154 if ( !group ) continue;
12155 groupDS = group->GetGroupDS();
12156 if ( !groupDS || groupDS->IsEmpty() ) continue;
12157 std::string grpName = group->GetName();
12158 //MESSAGE("grpName=" << grpName);
12159 if (grpName == groupName)
12165 bool isNodeGroup = false;
12166 bool isNodeCoords = false;
12169 if (groupDS->GetType() != SMDSAbs_Node)
12171 isNodeGroup = true; // a group of nodes exists and it is in this mesh
12174 if (nodesCoords.size() > 0)
12175 isNodeCoords = true; // a list o nodes given by their coordinates
12176 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12178 // --- define groups to build
12180 // --- group of SMDS volumes
12181 string grpvName = groupName;
12182 grpvName += "_vol";
12183 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str());
12186 MESSAGE("group not created " << grpvName);
12189 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12191 // --- group of SMDS faces on the skin
12192 string grpsName = groupName;
12193 grpsName += "_skin";
12194 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str());
12197 MESSAGE("group not created " << grpsName);
12200 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12202 // --- group of SMDS faces internal (several shapes)
12203 string grpiName = groupName;
12204 grpiName += "_internalFaces";
12205 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str());
12208 MESSAGE("group not created " << grpiName);
12211 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12213 // --- group of SMDS faces internal (several shapes)
12214 string grpeiName = groupName;
12215 grpeiName += "_internalEdges";
12216 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str());
12219 MESSAGE("group not created " << grpeiName);
12222 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12224 // --- build downward connectivity
12226 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12227 meshDS->BuildDownWardConnectivity(true);
12228 SMDS_UnstructuredGrid* grid = meshDS->GetGrid();
12230 // --- set of volumes detected inside
12232 std::set<int> setOfInsideVol;
12233 std::set<int> setOfVolToCheck;
12235 std::vector<gp_Pnt> gpnts;
12238 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12240 //MESSAGE("group of nodes provided");
12241 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12242 while ( elemIt->more() )
12244 const SMDS_MeshElement* elem = elemIt->next();
12247 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12250 SMDS_MeshElement* vol = 0;
12251 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12252 while (volItr->more())
12254 vol = (SMDS_MeshElement*)volItr->next();
12255 setOfInsideVol.insert(vol->GetVtkID());
12256 sgrp->Add(vol->GetID());
12260 else if (isNodeCoords)
12262 //MESSAGE("list of nodes coordinates provided");
12265 while ( i < nodesCoords.size()-2 )
12267 double x = nodesCoords[i++];
12268 double y = nodesCoords[i++];
12269 double z = nodesCoords[i++];
12270 gp_Pnt p = gp_Pnt(x, y ,z);
12271 gpnts.push_back(p);
12272 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12276 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12278 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12279 TopTools_IndexedMapOfShape vertexMap;
12280 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12281 gp_Pnt p = gp_Pnt(0,0,0);
12282 if (vertexMap.Extent() < 1)
12285 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12287 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12288 p = BRep_Tool::Pnt(vertex);
12289 gpnts.push_back(p);
12290 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12294 if (gpnts.size() > 0)
12296 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12297 //MESSAGE("startNode->nodeId " << nodeId);
12299 double radius2 = radius*radius;
12300 //MESSAGE("radius2 " << radius2);
12302 // --- volumes on start node
12304 setOfVolToCheck.clear();
12305 SMDS_MeshElement* startVol = 0;
12306 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12307 while (volItr->more())
12309 startVol = (SMDS_MeshElement*)volItr->next();
12310 setOfVolToCheck.insert(startVol->GetVtkID());
12312 if (setOfVolToCheck.empty())
12314 MESSAGE("No volumes found");
12318 // --- starting with central volumes then their neighbors, check if they are inside
12319 // or outside the domain, until no more new neighbor volume is inside.
12320 // Fill the group of inside volumes
12322 std::map<int, double> mapOfNodeDistance2;
12323 mapOfNodeDistance2.clear();
12324 std::set<int> setOfOutsideVol;
12325 while (!setOfVolToCheck.empty())
12327 std::set<int>::iterator it = setOfVolToCheck.begin();
12329 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12330 bool volInside = false;
12331 vtkIdType npts = 0;
12332 vtkIdType* pts = 0;
12333 grid->GetCellPoints(vtkId, npts, pts);
12334 for (int i=0; i<npts; i++)
12336 double distance2 = 0;
12337 if (mapOfNodeDistance2.count(pts[i]))
12339 distance2 = mapOfNodeDistance2[pts[i]];
12340 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12344 double *coords = grid->GetPoint(pts[i]);
12345 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12347 for ( size_t j = 0; j < gpnts.size(); j++ )
12349 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12350 if (d2 < distance2)
12353 if (distance2 < radius2)
12357 mapOfNodeDistance2[pts[i]] = distance2;
12358 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12360 if (distance2 < radius2)
12362 volInside = true; // one or more nodes inside the domain
12363 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12369 setOfInsideVol.insert(vtkId);
12370 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12371 int neighborsVtkIds[NBMAXNEIGHBORS];
12372 int downIds[NBMAXNEIGHBORS];
12373 unsigned char downTypes[NBMAXNEIGHBORS];
12374 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12375 for (int n = 0; n < nbNeighbors; n++)
12376 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12377 setOfVolToCheck.insert(neighborsVtkIds[n]);
12381 setOfOutsideVol.insert(vtkId);
12382 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12384 setOfVolToCheck.erase(vtkId);
12388 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12389 // If yes, add the volume to the inside set
12391 bool addedInside = true;
12392 std::set<int> setOfVolToReCheck;
12393 while (addedInside)
12395 //MESSAGE(" --------------------------- re check");
12396 addedInside = false;
12397 std::set<int>::iterator itv = setOfInsideVol.begin();
12398 for (; itv != setOfInsideVol.end(); ++itv)
12401 int neighborsVtkIds[NBMAXNEIGHBORS];
12402 int downIds[NBMAXNEIGHBORS];
12403 unsigned char downTypes[NBMAXNEIGHBORS];
12404 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12405 for (int n = 0; n < nbNeighbors; n++)
12406 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12407 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12409 setOfVolToCheck = setOfVolToReCheck;
12410 setOfVolToReCheck.clear();
12411 while (!setOfVolToCheck.empty())
12413 std::set<int>::iterator it = setOfVolToCheck.begin();
12415 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12417 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12418 int countInside = 0;
12419 int neighborsVtkIds[NBMAXNEIGHBORS];
12420 int downIds[NBMAXNEIGHBORS];
12421 unsigned char downTypes[NBMAXNEIGHBORS];
12422 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12423 for (int n = 0; n < nbNeighbors; n++)
12424 if (setOfInsideVol.count(neighborsVtkIds[n]))
12426 //MESSAGE("countInside " << countInside);
12427 if (countInside > 1)
12429 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12430 setOfInsideVol.insert(vtkId);
12431 sgrp->Add(meshDS->FromVtkToSmds(vtkId));
12432 addedInside = true;
12435 setOfVolToReCheck.insert(vtkId);
12437 setOfVolToCheck.erase(vtkId);
12441 // --- map of Downward faces at the boundary, inside the global volume
12442 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12443 // fill group of SMDS faces inside the volume (when several volume shapes)
12444 // fill group of SMDS faces on the skin of the global volume (if skin)
12446 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12447 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12448 std::set<int>::iterator it = setOfInsideVol.begin();
12449 for (; it != setOfInsideVol.end(); ++it)
12452 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->FromVtkToSmds(vtkId));
12453 int neighborsVtkIds[NBMAXNEIGHBORS];
12454 int downIds[NBMAXNEIGHBORS];
12455 unsigned char downTypes[NBMAXNEIGHBORS];
12456 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12457 for (int n = 0; n < nbNeighbors; n++)
12459 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12460 if (neighborDim == 3)
12462 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12464 DownIdType face(downIds[n], downTypes[n]);
12465 boundaryFaces[face] = vtkId;
12467 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12468 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12469 if (vtkFaceId >= 0)
12471 sgrpi->Add(meshDS->FromVtkToSmds(vtkFaceId));
12472 // find also the smds edges on this face
12473 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12474 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12475 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12476 for (int i = 0; i < nbEdges; i++)
12478 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12479 if (vtkEdgeId >= 0)
12480 sgrpei->Add(meshDS->FromVtkToSmds(vtkEdgeId));
12484 else if (neighborDim == 2) // skin of the volume
12486 DownIdType face(downIds[n], downTypes[n]);
12487 skinFaces[face] = vtkId;
12488 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12489 if (vtkFaceId >= 0)
12490 sgrps->Add(meshDS->FromVtkToSmds(vtkFaceId));
12495 // --- identify the edges constituting the wire of each subshape on the skin
12496 // define polylines with the nodes of edges, equivalent to wires
12497 // project polylines on subshapes, and partition, to get geom faces
12499 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12500 std::set<int> emptySet;
12502 std::set<int> shapeIds;
12504 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12505 while (itelem->more())
12507 const SMDS_MeshElement *elem = itelem->next();
12508 int shapeId = elem->getshapeId();
12509 int vtkId = elem->GetVtkID();
12510 if (!shapeIdToVtkIdSet.count(shapeId))
12512 shapeIdToVtkIdSet[shapeId] = emptySet;
12513 shapeIds.insert(shapeId);
12515 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12518 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12519 std::set<DownIdType, DownIdCompare> emptyEdges;
12520 emptyEdges.clear();
12522 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12523 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12525 int shapeId = itShape->first;
12526 //MESSAGE(" --- Shape ID --- "<< shapeId);
12527 shapeIdToEdges[shapeId] = emptyEdges;
12529 std::vector<int> nodesEdges;
12531 std::set<int>::iterator its = itShape->second.begin();
12532 for (; its != itShape->second.end(); ++its)
12535 //MESSAGE(" " << vtkId);
12536 int neighborsVtkIds[NBMAXNEIGHBORS];
12537 int downIds[NBMAXNEIGHBORS];
12538 unsigned char downTypes[NBMAXNEIGHBORS];
12539 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12540 for (int n = 0; n < nbNeighbors; n++)
12542 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12544 int smdsId = meshDS->FromVtkToSmds(neighborsVtkIds[n]);
12545 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12546 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12548 DownIdType edge(downIds[n], downTypes[n]);
12549 if (!shapeIdToEdges[shapeId].count(edge))
12551 shapeIdToEdges[shapeId].insert(edge);
12553 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12554 nodesEdges.push_back(vtkNodeId[0]);
12555 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12556 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12562 std::list<int> order;
12564 if (nodesEdges.size() > 0)
12566 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12567 nodesEdges[0] = -1;
12568 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12569 nodesEdges[1] = -1; // do not reuse this edge
12573 int nodeTofind = order.back(); // try first to push back
12575 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12576 if (nodesEdges[i] == nodeTofind)
12578 if ( i == (int) nodesEdges.size() )
12579 found = false; // no follower found on back
12582 if (i%2) // odd ==> use the previous one
12583 if (nodesEdges[i-1] < 0)
12587 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12588 nodesEdges[i-1] = -1;
12590 else // even ==> use the next one
12591 if (nodesEdges[i+1] < 0)
12595 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12596 nodesEdges[i+1] = -1;
12601 // try to push front
12603 nodeTofind = order.front(); // try to push front
12604 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12605 if ( nodesEdges[i] == nodeTofind )
12607 if ( i == (int)nodesEdges.size() )
12609 found = false; // no predecessor found on front
12612 if (i%2) // odd ==> use the previous one
12613 if (nodesEdges[i-1] < 0)
12617 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12618 nodesEdges[i-1] = -1;
12620 else // even ==> use the next one
12621 if (nodesEdges[i+1] < 0)
12625 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12626 nodesEdges[i+1] = -1;
12632 std::vector<int> nodes;
12633 nodes.push_back(shapeId);
12634 std::list<int>::iterator itl = order.begin();
12635 for (; itl != order.end(); itl++)
12637 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12638 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12640 listOfListOfNodes.push_back(nodes);
12643 // partition geom faces with blocFissure
12644 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12645 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12651 //================================================================================
12653 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12654 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12655 * \return TRUE if operation has been completed successfully, FALSE otherwise
12657 //================================================================================
12659 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12661 // iterates on volume elements and detect all free faces on them
12662 SMESHDS_Mesh* aMesh = GetMeshDS();
12666 ElemFeatures faceType( SMDSAbs_Face );
12667 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12668 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12671 const SMDS_MeshVolume* volume = vIt->next();
12672 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12673 vTool.SetExternalNormal();
12674 const int iQuad = volume->IsQuadratic();
12675 faceType.SetQuad( iQuad );
12676 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12678 if (!vTool.IsFreeFace(iface))
12681 vector<const SMDS_MeshNode *> nodes;
12682 int nbFaceNodes = vTool.NbFaceNodes(iface);
12683 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12685 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12686 nodes.push_back(faceNodes[inode]);
12688 if (iQuad) // add medium nodes
12690 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12691 nodes.push_back(faceNodes[inode]);
12692 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12693 nodes.push_back(faceNodes[8]);
12695 // add new face based on volume nodes
12696 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12698 nbExisted++; // face already exists
12702 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12707 return ( nbFree == ( nbExisted + nbCreated ));
12712 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12714 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12716 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12719 //================================================================================
12721 * \brief Creates missing boundary elements
12722 * \param elements - elements whose boundary is to be checked
12723 * \param dimension - defines type of boundary elements to create
12724 * \param group - a group to store created boundary elements in
12725 * \param targetMesh - a mesh to store created boundary elements in
12726 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12727 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12728 * boundary elements will be copied into the targetMesh
12729 * \param toAddExistingBondary - if true, not only new but also pre-existing
12730 * boundary elements will be added into the new group
12731 * \param aroundElements - if true, elements will be created on boundary of given
12732 * elements else, on boundary of the whole mesh.
12733 * \return nb of added boundary elements
12735 //================================================================================
12737 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12738 Bnd_Dimension dimension,
12739 SMESH_Group* group/*=0*/,
12740 SMESH_Mesh* targetMesh/*=0*/,
12741 bool toCopyElements/*=false*/,
12742 bool toCopyExistingBoundary/*=false*/,
12743 bool toAddExistingBondary/*= false*/,
12744 bool aroundElements/*= false*/)
12746 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12747 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12748 // hope that all elements are of the same type, do not check them all
12749 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12750 throw SALOME_Exception(LOCALIZED("wrong element type"));
12753 toCopyElements = toCopyExistingBoundary = false;
12755 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12756 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12757 int nbAddedBnd = 0;
12759 // editor adding present bnd elements and optionally holding elements to add to the group
12760 SMESH_MeshEditor* presentEditor;
12761 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12762 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12764 SMESH_MesherHelper helper( *myMesh );
12765 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12766 SMDS_VolumeTool vTool;
12767 TIDSortedElemSet avoidSet;
12768 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12771 typedef vector<const SMDS_MeshNode*> TConnectivity;
12772 TConnectivity tgtNodes;
12773 ElemFeatures elemKind( missType ), elemToCopy;
12775 vector<const SMDS_MeshElement*> presentBndElems;
12776 vector<TConnectivity> missingBndElems;
12777 vector<int> freeFacets;
12778 TConnectivity nodes, elemNodes;
12780 SMDS_ElemIteratorPtr eIt;
12781 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12782 else eIt = SMESHUtils::elemSetIterator( elements );
12784 while ( eIt->more() )
12786 const SMDS_MeshElement* elem = eIt->next();
12787 const int iQuad = elem->IsQuadratic();
12788 elemKind.SetQuad( iQuad );
12790 // ------------------------------------------------------------------------------------
12791 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12792 // ------------------------------------------------------------------------------------
12793 presentBndElems.clear();
12794 missingBndElems.clear();
12795 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12796 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12798 const SMDS_MeshElement* otherVol = 0;
12799 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12801 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12802 ( !aroundElements || elements.count( otherVol )))
12804 freeFacets.push_back( iface );
12806 if ( missType == SMDSAbs_Face )
12807 vTool.SetExternalNormal();
12808 for ( size_t i = 0; i < freeFacets.size(); ++i )
12810 int iface = freeFacets[i];
12811 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12812 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12813 if ( missType == SMDSAbs_Edge ) // boundary edges
12815 nodes.resize( 2+iQuad );
12816 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12818 for ( size_t j = 0; j < nodes.size(); ++j )
12819 nodes[ j ] = nn[ i+j ];
12820 if ( const SMDS_MeshElement* edge =
12821 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12822 presentBndElems.push_back( edge );
12824 missingBndElems.push_back( nodes );
12827 else // boundary face
12830 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12831 nodes.push_back( nn[inode] ); // add corner nodes
12833 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12834 nodes.push_back( nn[inode] ); // add medium nodes
12835 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12837 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12839 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12840 SMDSAbs_Face, /*noMedium=*/false ))
12841 presentBndElems.push_back( f );
12843 missingBndElems.push_back( nodes );
12845 if ( targetMesh != myMesh )
12847 // add 1D elements on face boundary to be added to a new mesh
12848 const SMDS_MeshElement* edge;
12849 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12852 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12854 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12855 if ( edge && avoidSet.insert( edge ).second )
12856 presentBndElems.push_back( edge );
12862 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12864 avoidSet.clear(), avoidSet.insert( elem );
12865 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesIterator() ),
12866 SMDS_MeshElement::iterator() );
12867 elemNodes.push_back( elemNodes[0] );
12868 nodes.resize( 2 + iQuad );
12869 const int nbLinks = elem->NbCornerNodes();
12870 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12872 nodes[0] = elemNodes[iN];
12873 nodes[1] = elemNodes[iN+1+iQuad];
12874 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12875 continue; // not free link
12877 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12878 if ( const SMDS_MeshElement* edge =
12879 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12880 presentBndElems.push_back( edge );
12882 missingBndElems.push_back( nodes );
12886 // ---------------------------------
12887 // 2. Add missing boundary elements
12888 // ---------------------------------
12889 if ( targetMesh != myMesh )
12890 // instead of making a map of nodes in this mesh and targetMesh,
12891 // we create nodes with same IDs.
12892 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12894 TConnectivity& srcNodes = missingBndElems[i];
12895 tgtNodes.resize( srcNodes.size() );
12896 for ( inode = 0; inode < srcNodes.size(); ++inode )
12897 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12898 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12900 /*noMedium=*/false))
12902 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12906 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12908 TConnectivity& nodes = missingBndElems[ i ];
12909 if ( /*aroundElements && */tgtEditor.GetMeshDS()->FindElement( nodes,
12911 /*noMedium=*/false))
12913 SMDS_MeshElement* newElem =
12914 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12915 nbAddedBnd += bool( newElem );
12917 // try to set a new element to a shape
12918 if ( myMesh->HasShapeToMesh() )
12921 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12922 const size_t nbN = nodes.size() / (iQuad+1 );
12923 for ( inode = 0; inode < nbN && ok; ++inode )
12925 pair<int, TopAbs_ShapeEnum> i_stype =
12926 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12927 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12928 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12930 if ( ok && mediumShapes.size() > 1 )
12932 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12933 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12934 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12936 if (( ok = ( stype_i->first != stype_i_0.first )))
12937 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12938 aMesh->IndexToShape( stype_i_0.second ));
12941 if ( ok && mediumShapes.begin()->first == missShapeType )
12942 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12946 // ----------------------------------
12947 // 3. Copy present boundary elements
12948 // ----------------------------------
12949 if ( toCopyExistingBoundary )
12950 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12952 const SMDS_MeshElement* e = presentBndElems[i];
12953 tgtNodes.resize( e->NbNodes() );
12954 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12955 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12956 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12958 else // store present elements to add them to a group
12959 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12961 presentEditor->myLastCreatedElems.push_back( presentBndElems[ i ]);
12964 } // loop on given elements
12966 // ---------------------------------------------
12967 // 4. Fill group with boundary elements
12968 // ---------------------------------------------
12971 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12972 for ( size_t i = 0; i < tgtEditor.myLastCreatedElems.size(); ++i )
12973 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems[ i ]);
12975 tgtEditor.myLastCreatedElems.clear();
12976 tgtEditor2.myLastCreatedElems.clear();
12978 // -----------------------
12979 // 5. Copy given elements
12980 // -----------------------
12981 if ( toCopyElements && targetMesh != myMesh )
12983 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12984 else eIt = SMESHUtils::elemSetIterator( elements );
12985 while (eIt->more())
12987 const SMDS_MeshElement* elem = eIt->next();
12988 tgtNodes.resize( elem->NbNodes() );
12989 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12990 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12991 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12993 tgtEditor.myLastCreatedElems.clear();
12999 //================================================================================
13001 * \brief Copy node position and set \a to node on the same geometry
13003 //================================================================================
13005 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
13006 const SMDS_MeshNode* to )
13008 if ( !from || !to ) return;
13010 SMDS_PositionPtr pos = from->GetPosition();
13011 if ( !pos || from->getshapeId() < 1 ) return;
13013 switch ( pos->GetTypeOfPosition() )
13015 case SMDS_TOP_3DSPACE: break;
13017 case SMDS_TOP_FACE:
13019 SMDS_FacePositionPtr fPos = pos;
13020 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
13021 fPos->GetUParameter(), fPos->GetVParameter() );
13024 case SMDS_TOP_EDGE:
13026 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
13027 SMDS_EdgePositionPtr ePos = pos;
13028 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
13031 case SMDS_TOP_VERTEX:
13033 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
13036 case SMDS_TOP_UNSPEC: