1 // Copyright (C) 2007-2015 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_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MeshAlgos.hxx"
46 #include "SMESH_MesherHelper.hxx"
47 #include "SMESH_OctreeNode.hxx"
48 #include "SMESH_subMesh.hxx"
50 #include <Basics_OCCTVersion.hxx>
52 #include "utilities.h"
55 #include <BRepAdaptor_Surface.hxx>
56 #include <BRepBuilderAPI_MakeEdge.hxx>
57 #include <BRepClass3d_SolidClassifier.hxx>
58 #include <BRep_Tool.hxx>
60 #include <Extrema_GenExtPS.hxx>
61 #include <Extrema_POnCurv.hxx>
62 #include <Extrema_POnSurf.hxx>
63 #include <Geom2d_Curve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Surface.hxx>
67 #include <Precision.hxx>
68 #include <TColStd_ListOfInteger.hxx>
69 #include <TopAbs_State.hxx>
71 #include <TopExp_Explorer.hxx>
72 #include <TopTools_ListIteratorOfListOfShape.hxx>
73 #include <TopTools_ListOfShape.hxx>
74 #include <TopTools_SequenceOfShape.hxx>
76 #include <TopoDS_Face.hxx>
77 #include <TopoDS_Solid.hxx>
83 #include <gp_Trsf.hxx>
97 #include <boost/tuple/tuple.hpp>
99 #include <Standard_Failure.hxx>
100 #include <Standard_ErrorHandler.hxx>
102 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
105 using namespace SMESH::Controls;
109 template < class ELEM_SET >
110 SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
112 typedef SMDS_SetIterator
113 < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
114 return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
118 //=======================================================================
119 //function : SMESH_MeshEditor
121 //=======================================================================
123 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
124 :myMesh( theMesh ) // theMesh may be NULL
128 //================================================================================
130 * \brief Clears myLastCreatedNodes and myLastCreatedElems
132 //================================================================================
134 void SMESH_MeshEditor::ClearLastCreated()
136 myLastCreatedNodes.Clear();
137 myLastCreatedElems.Clear();
140 //================================================================================
142 * \brief Initializes members by an existing element
143 * \param [in] elem - the source element
144 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
146 //================================================================================
148 SMESH_MeshEditor::ElemFeatures&
149 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
153 myType = elem->GetType();
154 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
156 myIsPoly = elem->IsPoly();
159 myIsQuad = elem->IsQuadratic();
160 if ( myType == SMDSAbs_Volume && !basicOnly )
162 vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
163 myPolyhedQuantities.swap( quant );
167 else if ( myType == SMDSAbs_Ball && !basicOnly )
169 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
175 //=======================================================================
179 //=======================================================================
182 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
183 const ElemFeatures& features)
185 SMDS_MeshElement* e = 0;
186 int nbnode = node.size();
187 SMESHDS_Mesh* mesh = GetMeshDS();
188 const int ID = features.myID;
190 switch ( features.myType ) {
192 if ( !features.myIsPoly ) {
194 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
195 else e = mesh->AddFace (node[0], node[1], node[2] );
197 else if (nbnode == 4) {
198 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
199 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
201 else if (nbnode == 6) {
202 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
203 node[4], node[5], ID);
204 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
207 else if (nbnode == 7) {
208 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
209 node[4], node[5], node[6], ID);
210 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
211 node[4], node[5], node[6] );
213 else if (nbnode == 8) {
214 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
215 node[4], node[5], node[6], node[7], ID);
216 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
217 node[4], node[5], node[6], node[7] );
219 else if (nbnode == 9) {
220 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
221 node[4], node[5], node[6], node[7], node[8], ID);
222 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
223 node[4], node[5], node[6], node[7], node[8] );
226 else if ( !features.myIsQuad )
228 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
229 else e = mesh->AddPolygonalFace (node );
231 else if ( nbnode % 2 == 0 ) // just a protection
233 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
234 else e = mesh->AddQuadPolygonalFace (node );
239 if ( !features.myIsPoly ) {
241 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
242 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
244 else if (nbnode == 5) {
245 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
247 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
250 else if (nbnode == 6) {
251 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
252 node[4], node[5], ID);
253 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
256 else if (nbnode == 8) {
257 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
258 node[4], node[5], node[6], node[7], ID);
259 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
260 node[4], node[5], node[6], node[7] );
262 else if (nbnode == 10) {
263 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
264 node[4], node[5], node[6], node[7],
265 node[8], node[9], ID);
266 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
267 node[4], node[5], node[6], node[7],
270 else if (nbnode == 12) {
271 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
272 node[4], node[5], node[6], node[7],
273 node[8], node[9], node[10], node[11], ID);
274 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
275 node[4], node[5], node[6], node[7],
276 node[8], node[9], node[10], node[11] );
278 else if (nbnode == 13) {
279 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
280 node[4], node[5], node[6], node[7],
281 node[8], node[9], node[10],node[11],
283 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
284 node[4], node[5], node[6], node[7],
285 node[8], node[9], node[10],node[11],
288 else if (nbnode == 15) {
289 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
290 node[4], node[5], node[6], node[7],
291 node[8], node[9], node[10],node[11],
292 node[12],node[13],node[14],ID);
293 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
294 node[4], node[5], node[6], node[7],
295 node[8], node[9], node[10],node[11],
296 node[12],node[13],node[14] );
298 else if (nbnode == 20) {
299 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
300 node[4], node[5], node[6], node[7],
301 node[8], node[9], node[10],node[11],
302 node[12],node[13],node[14],node[15],
303 node[16],node[17],node[18],node[19],ID);
304 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
305 node[4], node[5], node[6], node[7],
306 node[8], node[9], node[10],node[11],
307 node[12],node[13],node[14],node[15],
308 node[16],node[17],node[18],node[19] );
310 else if (nbnode == 27) {
311 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
312 node[4], node[5], node[6], node[7],
313 node[8], node[9], node[10],node[11],
314 node[12],node[13],node[14],node[15],
315 node[16],node[17],node[18],node[19],
316 node[20],node[21],node[22],node[23],
317 node[24],node[25],node[26], ID);
318 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
319 node[4], node[5], node[6], node[7],
320 node[8], node[9], node[10],node[11],
321 node[12],node[13],node[14],node[15],
322 node[16],node[17],node[18],node[19],
323 node[20],node[21],node[22],node[23],
324 node[24],node[25],node[26] );
327 else if ( !features.myIsQuad )
329 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
330 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
334 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
335 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
341 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
342 else e = mesh->AddEdge (node[0], node[1] );
344 else if ( nbnode == 3 ) {
345 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
346 else e = mesh->AddEdge (node[0], node[1], node[2] );
350 case SMDSAbs_0DElement:
352 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
353 else e = mesh->Add0DElement (node[0] );
358 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
359 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
363 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
364 else e = mesh->AddBall (node[0], features.myBallDiameter );
369 if ( e ) myLastCreatedElems.Append( e );
373 //=======================================================================
377 //=======================================================================
379 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
380 const ElemFeatures& features)
382 vector<const SMDS_MeshNode*> nodes;
383 nodes.reserve( nodeIDs.size() );
384 vector<int>::const_iterator id = nodeIDs.begin();
385 while ( id != nodeIDs.end() ) {
386 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
387 nodes.push_back( node );
391 return AddElement( nodes, features );
394 //=======================================================================
396 //purpose : Remove a node or an element.
397 // Modify a compute state of sub-meshes which become empty
398 //=======================================================================
400 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
403 myLastCreatedElems.Clear();
404 myLastCreatedNodes.Clear();
406 SMESHDS_Mesh* aMesh = GetMeshDS();
407 set< SMESH_subMesh *> smmap;
410 list<int>::const_iterator it = theIDs.begin();
411 for ( ; it != theIDs.end(); it++ ) {
412 const SMDS_MeshElement * elem;
414 elem = aMesh->FindNode( *it );
416 elem = aMesh->FindElement( *it );
420 // Notify VERTEX sub-meshes about modification
422 const SMDS_MeshNode* node = cast2Node( elem );
423 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
424 if ( int aShapeID = node->getshapeId() )
425 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
428 // Find sub-meshes to notify about modification
429 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
430 // while ( nodeIt->more() ) {
431 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
432 // const SMDS_PositionPtr& aPosition = node->GetPosition();
433 // if ( aPosition.get() ) {
434 // if ( int aShapeID = aPosition->GetShapeId() ) {
435 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
436 // smmap.insert( sm );
443 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
445 aMesh->RemoveElement( elem );
449 // Notify sub-meshes about modification
450 if ( !smmap.empty() ) {
451 set< SMESH_subMesh *>::iterator smIt;
452 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
453 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
456 // // Check if the whole mesh becomes empty
457 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
458 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
463 //================================================================================
465 * \brief Create 0D elements on all nodes of the given object except those
466 * nodes on which a 0D element already exists.
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
471 //================================================================================
473 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
474 TIDSortedElemSet& all0DElems )
476 SMDS_ElemIteratorPtr elemIt;
477 vector< const SMDS_MeshElement* > allNodes;
478 if ( elements.empty() )
480 allNodes.reserve( GetMeshDS()->NbNodes() );
481 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
482 while ( elemIt->more() )
483 allNodes.push_back( elemIt->next() );
485 elemIt = elemSetIterator( allNodes );
489 elemIt = elemSetIterator( elements );
492 while ( elemIt->more() )
494 const SMDS_MeshElement* e = elemIt->next();
495 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
496 while ( nodeIt->more() )
498 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
499 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
501 all0DElems.insert( it0D->next() );
503 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
504 all0DElems.insert( myLastCreatedElems.Last() );
510 //=======================================================================
511 //function : FindShape
512 //purpose : Return an index of the shape theElem is on
513 // or zero if a shape not found
514 //=======================================================================
516 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
518 myLastCreatedElems.Clear();
519 myLastCreatedNodes.Clear();
521 SMESHDS_Mesh * aMesh = GetMeshDS();
522 if ( aMesh->ShapeToMesh().IsNull() )
525 int aShapeID = theElem->getshapeId();
529 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
530 if ( sm->Contains( theElem ))
533 if ( theElem->GetType() == SMDSAbs_Node ) {
534 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
537 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
540 TopoDS_Shape aShape; // the shape a node of theElem is on
541 if ( theElem->GetType() != SMDSAbs_Node )
543 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
544 while ( nodeIt->more() ) {
545 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
546 if ((aShapeID = node->getshapeId()) > 0) {
547 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
548 if ( sm->Contains( theElem ))
550 if ( aShape.IsNull() )
551 aShape = aMesh->IndexToShape( aShapeID );
557 // None of nodes is on a proper shape,
558 // find the shape among ancestors of aShape on which a node is
559 if ( !aShape.IsNull() ) {
560 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
561 for ( ; ancIt.More(); ancIt.Next() ) {
562 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
563 if ( sm && sm->Contains( theElem ))
564 return aMesh->ShapeToIndex( ancIt.Value() );
569 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
570 while ( const SMESHDS_SubMesh* sm = smIt->next() )
571 if ( sm->Contains( theElem ))
578 //=======================================================================
579 //function : IsMedium
581 //=======================================================================
583 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
584 const SMDSAbs_ElementType typeToCheck)
586 bool isMedium = false;
587 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
588 while (it->more() && !isMedium ) {
589 const SMDS_MeshElement* elem = it->next();
590 isMedium = elem->IsMediumNode(node);
595 //=======================================================================
596 //function : shiftNodesQuadTria
597 //purpose : Shift nodes in the array corresponded to quadratic triangle
598 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
599 //=======================================================================
601 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
603 const SMDS_MeshNode* nd1 = aNodes[0];
604 aNodes[0] = aNodes[1];
605 aNodes[1] = aNodes[2];
607 const SMDS_MeshNode* nd2 = aNodes[3];
608 aNodes[3] = aNodes[4];
609 aNodes[4] = aNodes[5];
613 //=======================================================================
614 //function : nbEdgeConnectivity
615 //purpose : return number of the edges connected with the theNode.
616 // if theEdges has connections with the other type of the
617 // elements, return -1
618 //=======================================================================
620 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
622 // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
624 // while(elemIt->more()) {
629 return theNode->NbInverseElements();
632 //=======================================================================
633 //function : getNodesFromTwoTria
635 //=======================================================================
637 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
638 const SMDS_MeshElement * theTria2,
639 vector< const SMDS_MeshNode*>& N1,
640 vector< const SMDS_MeshNode*>& N2)
642 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
643 if ( N1.size() < 6 ) return false;
644 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
645 if ( N2.size() < 6 ) return false;
647 int sames[3] = {-1,-1,-1};
659 if(nbsames!=2) return false;
661 shiftNodesQuadTria(N1);
663 shiftNodesQuadTria(N1);
666 i = sames[0] + sames[1] + sames[2];
668 shiftNodesQuadTria(N2);
670 // now we receive following N1 and N2 (using numeration as in the image below)
671 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
672 // i.e. first nodes from both arrays form a new diagonal
676 //=======================================================================
677 //function : InverseDiag
678 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
679 // but having other common link.
680 // Return False if args are improper
681 //=======================================================================
683 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
684 const SMDS_MeshElement * theTria2 )
686 MESSAGE("InverseDiag");
687 myLastCreatedElems.Clear();
688 myLastCreatedNodes.Clear();
690 if (!theTria1 || !theTria2)
693 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
694 if (!F1) return false;
695 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
696 if (!F2) return false;
697 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
698 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
700 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
701 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
705 // put nodes in array and find out indices of the same ones
706 const SMDS_MeshNode* aNodes [6];
707 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
709 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
710 while ( it->more() ) {
711 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
713 if ( i > 2 ) // theTria2
714 // find same node of theTria1
715 for ( int j = 0; j < 3; j++ )
716 if ( aNodes[ i ] == aNodes[ j ]) {
725 return false; // theTria1 is not a triangle
726 it = theTria2->nodesIterator();
728 if ( i == 6 && it->more() )
729 return false; // theTria2 is not a triangle
732 // find indices of 1,2 and of A,B in theTria1
733 int iA = -1, iB = 0, i1 = 0, i2 = 0;
734 for ( i = 0; i < 6; i++ ) {
735 if ( sameInd [ i ] == -1 ) {
740 if ( iA >= 0) iB = i;
744 // nodes 1 and 2 should not be the same
745 if ( aNodes[ i1 ] == aNodes[ i2 ] )
749 aNodes[ iA ] = aNodes[ i2 ];
751 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
753 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
754 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
758 } // end if(F1 && F2)
760 // check case of quadratic faces
761 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
762 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
764 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
765 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
769 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
770 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
778 vector< const SMDS_MeshNode* > N1;
779 vector< const SMDS_MeshNode* > N2;
780 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
782 // now we receive following N1 and N2 (using numeration as above image)
783 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
784 // i.e. first nodes from both arrays determ new diagonal
786 vector< const SMDS_MeshNode*> N1new( N1.size() );
787 vector< const SMDS_MeshNode*> N2new( N2.size() );
788 N1new.back() = N1.back(); // central node of biquadratic
789 N2new.back() = N2.back();
790 N1new[0] = N1[0]; N2new[0] = N1[0];
791 N1new[1] = N2[0]; N2new[1] = N1[1];
792 N1new[2] = N2[1]; N2new[2] = N2[0];
793 N1new[3] = N1[4]; N2new[3] = N1[3];
794 N1new[4] = N2[3]; N2new[4] = N2[5];
795 N1new[5] = N1[5]; N2new[5] = N1[4];
796 // change nodes in faces
797 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
798 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
800 // move the central node of biquadratic triangle
801 SMESH_MesherHelper helper( *GetMesh() );
802 for ( int is2nd = 0; is2nd < 2; ++is2nd )
804 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
805 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
806 if ( nodes.size() < 7 )
808 helper.SetSubShape( tria->getshapeId() );
809 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
813 xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
814 SMESH_TNodeXYZ( nodes[4] ) +
815 SMESH_TNodeXYZ( nodes[5] )) / 3.;
820 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
821 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
822 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
824 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
825 xyz = S->Value( uv.X(), uv.Y() );
826 xyz.Transform( loc );
827 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
828 nodes[6]->getshapeId() > 0 )
829 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
831 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
836 //=======================================================================
837 //function : findTriangles
838 //purpose : find triangles sharing theNode1-theNode2 link
839 //=======================================================================
841 static bool findTriangles(const SMDS_MeshNode * theNode1,
842 const SMDS_MeshNode * theNode2,
843 const SMDS_MeshElement*& theTria1,
844 const SMDS_MeshElement*& theTria2)
846 if ( !theNode1 || !theNode2 ) return false;
848 theTria1 = theTria2 = 0;
850 set< const SMDS_MeshElement* > emap;
851 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
853 const SMDS_MeshElement* elem = it->next();
854 if ( elem->NbCornerNodes() == 3 )
857 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
859 const SMDS_MeshElement* elem = it->next();
860 if ( emap.count( elem )) {
868 // theTria1 must be element with minimum ID
869 if ( theTria2->GetID() < theTria1->GetID() )
870 std::swap( theTria2, theTria1 );
878 //=======================================================================
879 //function : InverseDiag
880 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
881 // with ones built on the same 4 nodes but having other common link.
882 // Return false if proper faces not found
883 //=======================================================================
885 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
886 const SMDS_MeshNode * theNode2)
888 myLastCreatedElems.Clear();
889 myLastCreatedNodes.Clear();
891 MESSAGE( "::InverseDiag()" );
893 const SMDS_MeshElement *tr1, *tr2;
894 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
897 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
898 if (!F1) return false;
899 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
900 if (!F2) return false;
901 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
902 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
904 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
905 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
909 // put nodes in array
910 // and find indices of 1,2 and of A in tr1 and of B in tr2
911 int i, iA1 = 0, i1 = 0;
912 const SMDS_MeshNode* aNodes1 [3];
913 SMDS_ElemIteratorPtr it;
914 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
915 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
916 if ( aNodes1[ i ] == theNode1 )
917 iA1 = i; // node A in tr1
918 else if ( aNodes1[ i ] != theNode2 )
922 const SMDS_MeshNode* aNodes2 [3];
923 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
924 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
925 if ( aNodes2[ i ] == theNode2 )
926 iB2 = i; // node B in tr2
927 else if ( aNodes2[ i ] != theNode1 )
931 // nodes 1 and 2 should not be the same
932 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
936 aNodes1[ iA1 ] = aNodes2[ i2 ];
938 aNodes2[ iB2 ] = aNodes1[ i1 ];
940 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
941 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
946 // check case of quadratic faces
947 return InverseDiag(tr1,tr2);
950 //=======================================================================
951 //function : getQuadrangleNodes
952 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
953 // fusion of triangles tr1 and tr2 having shared link on
954 // theNode1 and theNode2
955 //=======================================================================
957 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
958 const SMDS_MeshNode * theNode1,
959 const SMDS_MeshNode * theNode2,
960 const SMDS_MeshElement * tr1,
961 const SMDS_MeshElement * tr2 )
963 if( tr1->NbNodes() != tr2->NbNodes() )
965 // find the 4-th node to insert into tr1
966 const SMDS_MeshNode* n4 = 0;
967 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
969 while ( !n4 && i<3 ) {
970 const SMDS_MeshNode * n = cast2Node( it->next() );
972 bool isDiag = ( n == theNode1 || n == theNode2 );
976 // Make an array of nodes to be in a quadrangle
977 int iNode = 0, iFirstDiag = -1;
978 it = tr1->nodesIterator();
981 const SMDS_MeshNode * n = cast2Node( it->next() );
983 bool isDiag = ( n == theNode1 || n == theNode2 );
985 if ( iFirstDiag < 0 )
987 else if ( iNode - iFirstDiag == 1 )
988 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
990 else if ( n == n4 ) {
991 return false; // tr1 and tr2 should not have all the same nodes
993 theQuadNodes[ iNode++ ] = n;
995 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
996 theQuadNodes[ iNode ] = n4;
1001 //=======================================================================
1002 //function : DeleteDiag
1003 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
1004 // with a quadrangle built on the same 4 nodes.
1005 // Return false if proper faces not found
1006 //=======================================================================
1008 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1009 const SMDS_MeshNode * theNode2)
1011 myLastCreatedElems.Clear();
1012 myLastCreatedNodes.Clear();
1014 MESSAGE( "::DeleteDiag()" );
1016 const SMDS_MeshElement *tr1, *tr2;
1017 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1020 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1021 if (!F1) return false;
1022 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1023 if (!F2) return false;
1024 SMESHDS_Mesh * aMesh = GetMeshDS();
1026 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1027 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1029 const SMDS_MeshNode* aNodes [ 4 ];
1030 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1033 const SMDS_MeshElement* newElem = 0;
1034 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1035 myLastCreatedElems.Append(newElem);
1036 AddToSameGroups( newElem, tr1, aMesh );
1037 int aShapeId = tr1->getshapeId();
1040 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1042 aMesh->RemoveElement( tr1 );
1043 aMesh->RemoveElement( tr2 );
1048 // check case of quadratic faces
1049 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1051 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1055 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1056 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1064 vector< const SMDS_MeshNode* > N1;
1065 vector< const SMDS_MeshNode* > N2;
1066 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1068 // now we receive following N1 and N2 (using numeration as above image)
1069 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1070 // i.e. first nodes from both arrays determ new diagonal
1072 const SMDS_MeshNode* aNodes[8];
1082 const SMDS_MeshElement* newElem = 0;
1083 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1084 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1085 myLastCreatedElems.Append(newElem);
1086 AddToSameGroups( newElem, tr1, aMesh );
1087 int aShapeId = tr1->getshapeId();
1090 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1092 aMesh->RemoveElement( tr1 );
1093 aMesh->RemoveElement( tr2 );
1095 // remove middle node (9)
1096 GetMeshDS()->RemoveNode( N1[4] );
1101 //=======================================================================
1102 //function : Reorient
1103 //purpose : Reverse theElement orientation
1104 //=======================================================================
1106 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1108 MESSAGE("Reorient");
1109 myLastCreatedElems.Clear();
1110 myLastCreatedNodes.Clear();
1114 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1115 if ( !it || !it->more() )
1118 const SMDSAbs_ElementType type = theElem->GetType();
1119 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1122 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1123 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1125 const SMDS_VtkVolume* aPolyedre =
1126 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1128 MESSAGE("Warning: bad volumic element");
1131 const int nbFaces = aPolyedre->NbFaces();
1132 vector<const SMDS_MeshNode *> poly_nodes;
1133 vector<int> quantities (nbFaces);
1135 // reverse each face of the polyedre
1136 for (int iface = 1; iface <= nbFaces; iface++) {
1137 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1138 quantities[iface - 1] = nbFaceNodes;
1140 for (inode = nbFaceNodes; inode >= 1; inode--) {
1141 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1142 poly_nodes.push_back(curNode);
1145 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1147 else // other elements
1149 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1150 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1151 if ( interlace.empty() )
1153 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1157 SMDS_MeshCell::applyInterlace( interlace, nodes );
1159 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1164 //================================================================================
1166 * \brief Reorient faces.
1167 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1168 * \param theDirection - desired direction of normal of \a theFace
1169 * \param theFace - one of \a theFaces that sould be oriented according to
1170 * \a theDirection and whose orientation defines orientation of other faces
1171 * \return number of reoriented faces.
1173 //================================================================================
1175 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1176 const gp_Dir& theDirection,
1177 const SMDS_MeshElement * theFace)
1180 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1182 if ( theFaces.empty() )
1184 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1185 while ( fIt->more() )
1186 theFaces.insert( theFaces.end(), fIt->next() );
1189 // orient theFace according to theDirection
1191 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1192 if ( normal * theDirection.XYZ() < 0 )
1193 nbReori += Reorient( theFace );
1195 // Orient other faces
1197 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1198 TIDSortedElemSet avoidSet;
1199 set< SMESH_TLink > checkedLinks;
1200 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1202 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1203 theFaces.erase( theFace );
1204 startFaces.insert( theFace );
1206 int nodeInd1, nodeInd2;
1207 const SMDS_MeshElement* otherFace;
1208 vector< const SMDS_MeshElement* > facesNearLink;
1209 vector< std::pair< int, int > > nodeIndsOfFace;
1211 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1212 while ( !startFaces.empty() )
1214 startFace = startFaces.begin();
1215 theFace = *startFace;
1216 startFaces.erase( startFace );
1217 if ( !visitedFaces.insert( theFace ).second )
1221 avoidSet.insert(theFace);
1223 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1225 const int nbNodes = theFace->NbCornerNodes();
1226 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1228 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1229 linkIt_isNew = checkedLinks.insert( link );
1230 if ( !linkIt_isNew.second )
1232 // link has already been checked and won't be encountered more
1233 // if the group (theFaces) is manifold
1234 //checkedLinks.erase( linkIt_isNew.first );
1238 facesNearLink.clear();
1239 nodeIndsOfFace.clear();
1240 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1242 &nodeInd1, &nodeInd2 )))
1243 if ( otherFace != theFace)
1245 facesNearLink.push_back( otherFace );
1246 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1247 avoidSet.insert( otherFace );
1249 if ( facesNearLink.size() > 1 )
1251 // NON-MANIFOLD mesh shell !
1252 // select a face most co-directed with theFace,
1253 // other faces won't be visited this time
1255 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1256 double proj, maxProj = -1;
1257 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1258 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1259 if (( proj = Abs( NF * NOF )) > maxProj ) {
1261 otherFace = facesNearLink[i];
1262 nodeInd1 = nodeIndsOfFace[i].first;
1263 nodeInd2 = nodeIndsOfFace[i].second;
1266 // not to visit rejected faces
1267 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1268 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1269 visitedFaces.insert( facesNearLink[i] );
1271 else if ( facesNearLink.size() == 1 )
1273 otherFace = facesNearLink[0];
1274 nodeInd1 = nodeIndsOfFace.back().first;
1275 nodeInd2 = nodeIndsOfFace.back().second;
1277 if ( otherFace && otherFace != theFace)
1279 // link must be reverse in otherFace if orientation ot otherFace
1280 // is same as that of theFace
1281 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1283 nbReori += Reorient( otherFace );
1285 startFaces.insert( otherFace );
1288 std::swap( link.first, link.second ); // reverse the link
1294 //================================================================================
1296 * \brief Reorient faces basing on orientation of adjacent volumes.
1297 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1298 * \param theVolumes - reference volumes.
1299 * \param theOutsideNormal - to orient faces to have their normal
1300 * pointing either \a outside or \a inside the adjacent volumes.
1301 * \return number of reoriented faces.
1303 //================================================================================
1305 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1306 TIDSortedElemSet & theVolumes,
1307 const bool theOutsideNormal)
1311 SMDS_ElemIteratorPtr faceIt;
1312 if ( theFaces.empty() )
1313 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1315 faceIt = elemSetIterator( theFaces );
1317 vector< const SMDS_MeshNode* > faceNodes;
1318 TIDSortedElemSet checkedVolumes;
1319 set< const SMDS_MeshNode* > faceNodesSet;
1320 SMDS_VolumeTool volumeTool;
1322 while ( faceIt->more() ) // loop on given faces
1324 const SMDS_MeshElement* face = faceIt->next();
1325 if ( face->GetType() != SMDSAbs_Face )
1328 const int nbCornersNodes = face->NbCornerNodes();
1329 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1331 checkedVolumes.clear();
1332 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1333 while ( vIt->more() )
1335 const SMDS_MeshElement* volume = vIt->next();
1337 if ( !checkedVolumes.insert( volume ).second )
1339 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1342 // is volume adjacent?
1343 bool allNodesCommon = true;
1344 for ( int iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1345 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1346 if ( !allNodesCommon )
1349 // get nodes of a corresponding volume facet
1350 faceNodesSet.clear();
1351 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1352 volumeTool.Set( volume );
1353 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1354 if ( facetID < 0 ) continue;
1355 volumeTool.SetExternalNormal();
1356 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1358 // compare order of faceNodes and facetNodes
1359 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1361 for ( int i = 0; i < 2; ++i )
1363 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1364 for ( int iN = 0; iN < nbCornersNodes; ++iN )
1365 if ( faceNodes[ iN ] == n )
1371 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1372 if ( isOutside != theOutsideNormal )
1373 nbReori += Reorient( face );
1375 } // loop on given faces
1380 //=======================================================================
1381 //function : getBadRate
1383 //=======================================================================
1385 static double getBadRate (const SMDS_MeshElement* theElem,
1386 SMESH::Controls::NumericalFunctorPtr& theCrit)
1388 SMESH::Controls::TSequenceOfXYZ P;
1389 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1391 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1392 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1395 //=======================================================================
1396 //function : QuadToTri
1397 //purpose : Cut quadrangles into triangles.
1398 // theCrit is used to select a diagonal to cut
1399 //=======================================================================
1401 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1402 SMESH::Controls::NumericalFunctorPtr theCrit)
1404 myLastCreatedElems.Clear();
1405 myLastCreatedNodes.Clear();
1407 if ( !theCrit.get() )
1410 SMESHDS_Mesh * aMesh = GetMeshDS();
1412 Handle(Geom_Surface) surface;
1413 SMESH_MesherHelper helper( *GetMesh() );
1415 TIDSortedElemSet::iterator itElem;
1416 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1418 const SMDS_MeshElement* elem = *itElem;
1419 if ( !elem || elem->GetType() != SMDSAbs_Face )
1421 if ( elem->NbCornerNodes() != 4 )
1424 // retrieve element nodes
1425 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1427 // compare two sets of possible triangles
1428 double aBadRate1, aBadRate2; // to what extent a set is bad
1429 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1430 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1431 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1433 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1434 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1435 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1437 const int aShapeId = FindShape( elem );
1438 const SMDS_MeshElement* newElem1 = 0;
1439 const SMDS_MeshElement* newElem2 = 0;
1441 if ( !elem->IsQuadratic() ) // split liner quadrangle
1443 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1444 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1445 if ( aBadRate1 <= aBadRate2 ) {
1446 // tr1 + tr2 is better
1447 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1448 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1451 // tr3 + tr4 is better
1452 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1453 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1456 else // split quadratic quadrangle
1458 helper.SetIsQuadratic( true );
1459 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1461 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1462 if ( aNodes.size() == 9 )
1464 helper.SetIsBiQuadratic( true );
1465 if ( aBadRate1 <= aBadRate2 )
1466 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1468 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1470 // create a new element
1471 if ( aBadRate1 <= aBadRate2 ) {
1472 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1473 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1476 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1477 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1481 // care of a new element
1483 myLastCreatedElems.Append(newElem1);
1484 myLastCreatedElems.Append(newElem2);
1485 AddToSameGroups( newElem1, elem, aMesh );
1486 AddToSameGroups( newElem2, elem, aMesh );
1488 // put a new triangle on the same shape
1490 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1491 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1493 aMesh->RemoveElement( elem );
1498 //=======================================================================
1500 * \brief Split each of given quadrangles into 4 triangles.
1501 * \param theElems - The faces to be splitted. If empty all faces are split.
1503 //=======================================================================
1505 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1507 myLastCreatedElems.Clear();
1508 myLastCreatedNodes.Clear();
1510 SMESH_MesherHelper helper( *GetMesh() );
1511 helper.SetElementsOnShape( true );
1513 SMDS_ElemIteratorPtr faceIt;
1514 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1515 else faceIt = elemSetIterator( theElems );
1518 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1520 vector< const SMDS_MeshNode* > nodes;
1521 SMESHDS_SubMesh* subMeshDS;
1523 Handle(Geom_Surface) surface;
1524 TopLoc_Location loc;
1526 while ( faceIt->more() )
1528 const SMDS_MeshElement* quad = faceIt->next();
1529 if ( !quad || quad->NbCornerNodes() != 4 )
1532 // get a surface the quad is on
1534 if ( quad->getshapeId() < 1 )
1537 helper.SetSubShape( 0 );
1540 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1542 helper.SetSubShape( quad->getshapeId() );
1543 if ( !helper.GetSubShape().IsNull() &&
1544 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1546 F = TopoDS::Face( helper.GetSubShape() );
1547 surface = BRep_Tool::Surface( F, loc );
1548 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1552 helper.SetSubShape( 0 );
1557 // create a central node
1559 const SMDS_MeshNode* nCentral;
1560 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1562 if ( nodes.size() == 9 )
1564 nCentral = nodes.back();
1571 for ( ; iN < nodes.size(); ++iN )
1572 xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1574 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1575 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1577 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1578 xyz[0], xyz[1], xyz[2], xyz[3],
1579 xyz[4], xyz[5], xyz[6], xyz[7] );
1583 for ( ; iN < nodes.size(); ++iN )
1584 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1586 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1587 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1589 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1590 uv[0], uv[1], uv[2], uv[3],
1591 uv[4], uv[5], uv[6], uv[7] );
1593 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1597 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1598 uv[8].X(), uv[8].Y() );
1599 myLastCreatedNodes.Append( nCentral );
1602 // create 4 triangles
1604 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1606 helper.SetIsQuadratic ( nodes.size() > 4 );
1607 helper.SetIsBiQuadratic( nodes.size() == 9 );
1608 if ( helper.GetIsQuadratic() )
1609 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1611 for ( int i = 0; i < 4; ++i )
1613 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1616 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1617 myLastCreatedElems.Append( tria );
1622 //=======================================================================
1623 //function : BestSplit
1624 //purpose : Find better diagonal for cutting.
1625 //=======================================================================
1627 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1628 SMESH::Controls::NumericalFunctorPtr theCrit)
1630 myLastCreatedElems.Clear();
1631 myLastCreatedNodes.Clear();
1636 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1639 if( theQuad->NbNodes()==4 ||
1640 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1642 // retrieve element nodes
1643 const SMDS_MeshNode* aNodes [4];
1644 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1646 //while (itN->more())
1648 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1650 // compare two sets of possible triangles
1651 double aBadRate1, aBadRate2; // to what extent a set is bad
1652 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1653 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1654 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1656 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1657 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1658 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1659 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1660 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1661 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1662 return 1; // diagonal 1-3
1664 return 2; // diagonal 2-4
1671 // Methods of splitting volumes into tetra
1673 const int theHexTo5_1[5*4+1] =
1675 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1677 const int theHexTo5_2[5*4+1] =
1679 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1681 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1683 const int theHexTo6_1[6*4+1] =
1685 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
1687 const int theHexTo6_2[6*4+1] =
1689 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
1691 const int theHexTo6_3[6*4+1] =
1693 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
1695 const int theHexTo6_4[6*4+1] =
1697 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
1699 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1701 const int thePyraTo2_1[2*4+1] =
1703 0, 1, 2, 4, 0, 2, 3, 4, -1
1705 const int thePyraTo2_2[2*4+1] =
1707 1, 2, 3, 4, 1, 3, 0, 4, -1
1709 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1711 const int thePentaTo3_1[3*4+1] =
1713 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1715 const int thePentaTo3_2[3*4+1] =
1717 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1719 const int thePentaTo3_3[3*4+1] =
1721 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1723 const int thePentaTo3_4[3*4+1] =
1725 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1727 const int thePentaTo3_5[3*4+1] =
1729 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1731 const int thePentaTo3_6[3*4+1] =
1733 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1735 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1736 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1738 // Methods of splitting hexahedron into prisms
1740 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1742 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
1744 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1746 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
1748 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1750 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
1753 const int theHexTo2Prisms_BT_1[6*2+1] =
1755 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1757 const int theHexTo2Prisms_BT_2[6*2+1] =
1759 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1761 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1763 const int theHexTo2Prisms_LR_1[6*2+1] =
1765 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1767 const int theHexTo2Prisms_LR_2[6*2+1] =
1769 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1771 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1773 const int theHexTo2Prisms_FB_1[6*2+1] =
1775 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1777 const int theHexTo2Prisms_FB_2[6*2+1] =
1779 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1781 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1784 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1787 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1788 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1789 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1790 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1796 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1797 bool _baryNode; //!< additional node is to be created at cell barycenter
1798 bool _ownConn; //!< to delete _connectivity in destructor
1799 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1801 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1802 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1803 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1804 bool hasFacet( const TTriangleFacet& facet ) const
1806 if ( _nbCorners == 4 )
1808 const int* tetConn = _connectivity;
1809 for ( ; tetConn[0] >= 0; tetConn += 4 )
1810 if (( facet.contains( tetConn[0] ) +
1811 facet.contains( tetConn[1] ) +
1812 facet.contains( tetConn[2] ) +
1813 facet.contains( tetConn[3] )) == 3 )
1816 else // prism, _nbCorners == 6
1818 const int* prismConn = _connectivity;
1819 for ( ; prismConn[0] >= 0; prismConn += 6 )
1821 if (( facet.contains( prismConn[0] ) &&
1822 facet.contains( prismConn[1] ) &&
1823 facet.contains( prismConn[2] ))
1825 ( facet.contains( prismConn[3] ) &&
1826 facet.contains( prismConn[4] ) &&
1827 facet.contains( prismConn[5] )))
1835 //=======================================================================
1837 * \brief return TSplitMethod for the given element to split into tetrahedra
1839 //=======================================================================
1841 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1843 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1845 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1846 // an edge and a face barycenter; tertaherdons are based on triangles and
1847 // a volume barycenter
1848 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1850 // Find out how adjacent volumes are split
1852 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1853 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1854 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1856 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1857 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1858 if ( nbNodes < 4 ) continue;
1860 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1861 const int* nInd = vol.GetFaceNodesIndices( iF );
1864 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1865 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1866 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1867 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1871 int iCom = 0; // common node of triangle faces to split into
1872 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1874 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1875 nInd[ iQ * ( (iCom+1)%nbNodes )],
1876 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1877 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1878 nInd[ iQ * ( (iCom+2)%nbNodes )],
1879 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1880 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1882 triaSplits.push_back( t012 );
1883 triaSplits.push_back( t023 );
1888 if ( !triaSplits.empty() )
1889 hasAdjacentSplits = true;
1892 // Among variants of split method select one compliant with adjacent volumes
1894 TSplitMethod method;
1895 if ( !vol.Element()->IsPoly() && !is24TetMode )
1897 int nbVariants = 2, nbTet = 0;
1898 const int** connVariants = 0;
1899 switch ( vol.Element()->GetEntityType() )
1901 case SMDSEntity_Hexa:
1902 case SMDSEntity_Quad_Hexa:
1903 case SMDSEntity_TriQuad_Hexa:
1904 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1905 connVariants = theHexTo5, nbTet = 5;
1907 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1909 case SMDSEntity_Pyramid:
1910 case SMDSEntity_Quad_Pyramid:
1911 connVariants = thePyraTo2; nbTet = 2;
1913 case SMDSEntity_Penta:
1914 case SMDSEntity_Quad_Penta:
1915 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1920 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1922 // check method compliancy with adjacent tetras,
1923 // all found splits must be among facets of tetras described by this method
1924 method = TSplitMethod( nbTet, connVariants[variant] );
1925 if ( hasAdjacentSplits && method._nbSplits > 0 )
1927 bool facetCreated = true;
1928 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1930 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1931 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1932 facetCreated = method.hasFacet( *facet );
1934 if ( !facetCreated )
1935 method = TSplitMethod(0); // incompatible method
1939 if ( method._nbSplits < 1 )
1941 // No standard method is applicable, use a generic solution:
1942 // each facet of a volume is split into triangles and
1943 // each of triangles and a volume barycenter form a tetrahedron.
1945 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1947 int* connectivity = new int[ maxTetConnSize + 1 ];
1948 method._connectivity = connectivity;
1949 method._ownConn = true;
1950 method._baryNode = !isHex27; // to create central node or not
1953 int baryCenInd = vol.NbNodes() - int( isHex27 );
1954 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1956 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1957 const int* nInd = vol.GetFaceNodesIndices( iF );
1958 // find common node of triangle facets of tetra to create
1959 int iCommon = 0; // index in linear numeration
1960 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1961 if ( !triaSplits.empty() )
1964 const TTriangleFacet* facet = &triaSplits.front();
1965 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1966 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1967 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1970 else if ( nbNodes > 3 && !is24TetMode )
1972 // find the best method of splitting into triangles by aspect ratio
1973 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1974 map< double, int > badness2iCommon;
1975 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1976 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1977 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1980 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1982 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1983 nodes[ iQ*((iLast-1)%nbNodes)],
1984 nodes[ iQ*((iLast )%nbNodes)]);
1985 badness += getBadRate( &tria, aspectRatio );
1987 badness2iCommon.insert( make_pair( badness, iCommon ));
1989 // use iCommon with lowest badness
1990 iCommon = badness2iCommon.begin()->second;
1992 if ( iCommon >= nbNodes )
1993 iCommon = 0; // something wrong
1995 // fill connectivity of tetrahedra based on a current face
1996 int nbTet = nbNodes - 2;
1997 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2002 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2003 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2007 method._faceBaryNode[ iF ] = 0;
2008 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2011 for ( int i = 0; i < nbTet; ++i )
2013 int i1 = i, i2 = (i+1) % nbNodes;
2014 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2015 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2016 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2017 connectivity[ connSize++ ] = faceBaryCenInd;
2018 connectivity[ connSize++ ] = baryCenInd;
2023 for ( int i = 0; i < nbTet; ++i )
2025 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2026 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2027 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2028 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2029 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2030 connectivity[ connSize++ ] = baryCenInd;
2033 method._nbSplits += nbTet;
2035 } // loop on volume faces
2037 connectivity[ connSize++ ] = -1;
2039 } // end of generic solution
2043 //=======================================================================
2045 * \brief return TSplitMethod to split haxhedron into prisms
2047 //=======================================================================
2049 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2050 const int methodFlags,
2051 const int facetToSplit)
2053 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2055 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2057 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2059 static TSplitMethod to4methods[4]; // order BT, LR, FB
2060 if ( to4methods[iF]._nbSplits == 0 )
2064 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2065 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2066 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2069 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2070 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2071 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2074 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2075 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2076 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2078 default: return to4methods[3];
2080 to4methods[iF]._nbSplits = 4;
2081 to4methods[iF]._nbCorners = 6;
2083 return to4methods[iF];
2085 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2087 TSplitMethod method;
2089 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2091 const int nbVariants = 2, nbSplits = 2;
2092 const int** connVariants = 0;
2094 case 0: connVariants = theHexTo2Prisms_BT; break;
2095 case 1: connVariants = theHexTo2Prisms_LR; break;
2096 case 2: connVariants = theHexTo2Prisms_FB; break;
2097 default: return method;
2100 // look for prisms adjacent via facetToSplit and an opposite one
2101 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2103 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2104 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2105 if ( nbNodes != 4 ) return method;
2107 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2108 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2109 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2111 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2113 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2118 // there are adjacent prism
2119 for ( int variant = 0; variant < nbVariants; ++variant )
2121 // check method compliancy with adjacent prisms,
2122 // the found prism facets must be among facets of prisms described by current method
2123 method._nbSplits = nbSplits;
2124 method._nbCorners = 6;
2125 method._connectivity = connVariants[ variant ];
2126 if ( method.hasFacet( *t ))
2131 // No adjacent prisms. Select a variant with a best aspect ratio.
2133 double badness[2] = { 0, 0 };
2134 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2135 const SMDS_MeshNode** nodes = vol.GetNodes();
2136 for ( int variant = 0; variant < nbVariants; ++variant )
2137 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2139 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2140 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2142 method._connectivity = connVariants[ variant ];
2143 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2144 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2145 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2147 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2150 badness[ variant ] += getBadRate( &tria, aspectRatio );
2152 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2154 method._nbSplits = nbSplits;
2155 method._nbCorners = 6;
2156 method._connectivity = connVariants[ iBetter ];
2161 //================================================================================
2163 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2165 //================================================================================
2167 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2168 const SMDSAbs_GeometryType geom ) const
2170 // find the tetrahedron including the three nodes of facet
2171 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2172 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2173 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2174 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2175 while ( volIt1->more() )
2177 const SMDS_MeshElement* v = volIt1->next();
2178 if ( v->GetGeomType() != geom )
2180 const int lastCornerInd = v->NbCornerNodes() - 1;
2181 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2182 continue; // medium node not allowed
2183 const int ind2 = v->GetNodeIndex( n2 );
2184 if ( ind2 < 0 || lastCornerInd < ind2 )
2186 const int ind3 = v->GetNodeIndex( n3 );
2187 if ( ind3 < 0 || lastCornerInd < ind3 )
2194 //=======================================================================
2196 * \brief A key of a face of volume
2198 //=======================================================================
2200 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2202 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2204 TIDSortedNodeSet sortedNodes;
2205 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2206 int nbNodes = vol.NbFaceNodes( iF );
2207 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2208 for ( int i = 0; i < nbNodes; i += iQ )
2209 sortedNodes.insert( fNodes[i] );
2210 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2211 first.first = (*(n++))->GetID();
2212 first.second = (*(n++))->GetID();
2213 second.first = (*(n++))->GetID();
2214 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2219 //=======================================================================
2220 //function : SplitVolumes
2221 //purpose : Split volume elements into tetrahedra or prisms.
2222 // If facet ID < 0, element is split into tetrahedra,
2223 // else a hexahedron is split into prisms so that the given facet is
2224 // split into triangles
2225 //=======================================================================
2227 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2228 const int theMethodFlags)
2230 SMDS_VolumeTool volTool;
2231 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2232 fHelper.ToFixNodeParameters( true );
2234 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2235 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2237 SMESH_SequenceOfElemPtr newNodes, newElems;
2239 // map face of volume to it's baricenrtic node
2240 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2242 vector<const SMDS_MeshElement* > splitVols;
2244 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2245 for ( ; elem2facet != theElems.end(); ++elem2facet )
2247 const SMDS_MeshElement* elem = elem2facet->first;
2248 const int facetToSplit = elem2facet->second;
2249 if ( elem->GetType() != SMDSAbs_Volume )
2251 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2252 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2255 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2257 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2258 getTetraSplitMethod( volTool, theMethodFlags ) :
2259 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2260 if ( splitMethod._nbSplits < 1 ) continue;
2262 // find submesh to add new tetras to
2263 if ( !subMesh || !subMesh->Contains( elem ))
2265 int shapeID = FindShape( elem );
2266 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2267 subMesh = GetMeshDS()->MeshElements( shapeID );
2270 if ( elem->IsQuadratic() )
2273 // add quadratic links to the helper
2274 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2276 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2277 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2278 for ( int iN = 0; iN < nbN; iN += iQ )
2279 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2281 helper.SetIsQuadratic( true );
2286 helper.SetIsQuadratic( false );
2288 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2289 volTool.GetNodes() + elem->NbNodes() );
2290 helper.SetElementsOnShape( true );
2291 if ( splitMethod._baryNode )
2293 // make a node at barycenter
2294 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2295 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2296 nodes.push_back( gcNode );
2297 newNodes.Append( gcNode );
2299 if ( !splitMethod._faceBaryNode.empty() )
2301 // make or find baricentric nodes of faces
2302 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2303 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2305 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2306 volFace2BaryNode.insert
2307 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2310 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2311 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2313 nodes.push_back( iF_n->second = f_n->second );
2318 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2319 const int* volConn = splitMethod._connectivity;
2320 if ( splitMethod._nbCorners == 4 ) // tetra
2321 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2322 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2323 nodes[ volConn[1] ],
2324 nodes[ volConn[2] ],
2325 nodes[ volConn[3] ]));
2327 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2328 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2329 nodes[ volConn[1] ],
2330 nodes[ volConn[2] ],
2331 nodes[ volConn[3] ],
2332 nodes[ volConn[4] ],
2333 nodes[ volConn[5] ]));
2335 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2337 // Split faces on sides of the split volume
2339 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2340 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2342 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2343 if ( nbNodes < 4 ) continue;
2345 // find an existing face
2346 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2347 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2348 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2349 /*noMedium=*/false))
2352 helper.SetElementsOnShape( false );
2353 vector< const SMDS_MeshElement* > triangles;
2355 // find submesh to add new triangles in
2356 if ( !fSubMesh || !fSubMesh->Contains( face ))
2358 int shapeID = FindShape( face );
2359 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2361 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2362 if ( iF_n != splitMethod._faceBaryNode.end() )
2364 const SMDS_MeshNode *baryNode = iF_n->second;
2365 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2367 const SMDS_MeshNode* n1 = fNodes[iN];
2368 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2369 const SMDS_MeshNode *n3 = baryNode;
2370 if ( !volTool.IsFaceExternal( iF ))
2372 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2374 if ( fSubMesh ) // update position of the bary node on geometry
2377 subMesh->RemoveNode( baryNode, false );
2378 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2379 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2380 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2382 fHelper.SetSubShape( s );
2383 gp_XY uv( 1e100, 1e100 );
2385 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2386 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2389 // node is too far from the surface
2390 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2391 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2392 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2399 // among possible triangles create ones discribed by split method
2400 const int* nInd = volTool.GetFaceNodesIndices( iF );
2401 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2402 int iCom = 0; // common node of triangle faces to split into
2403 list< TTriangleFacet > facets;
2404 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2406 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2407 nInd[ iQ * ( (iCom+1)%nbNodes )],
2408 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2409 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2410 nInd[ iQ * ( (iCom+2)%nbNodes )],
2411 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2412 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2414 facets.push_back( t012 );
2415 facets.push_back( t023 );
2416 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2417 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2418 nInd[ iQ * ((iLast-1)%nbNodes )],
2419 nInd[ iQ * ((iLast )%nbNodes )]));
2423 list< TTriangleFacet >::iterator facet = facets.begin();
2424 if ( facet == facets.end() )
2426 for ( ; facet != facets.end(); ++facet )
2428 if ( !volTool.IsFaceExternal( iF ))
2429 swap( facet->_n2, facet->_n3 );
2430 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2431 volNodes[ facet->_n2 ],
2432 volNodes[ facet->_n3 ]));
2435 for ( int i = 0; i < triangles.size(); ++i )
2437 if ( !triangles[i] ) continue;
2439 fSubMesh->AddElement( triangles[i]);
2440 newElems.Append( triangles[i] );
2442 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2443 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2445 } // while a face based on facet nodes exists
2446 } // loop on volume faces to split them into triangles
2448 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2450 if ( geomType == SMDSEntity_TriQuad_Hexa )
2452 // remove medium nodes that could become free
2453 for ( int i = 20; i < volTool.NbNodes(); ++i )
2454 if ( volNodes[i]->NbInverseElements() == 0 )
2455 GetMeshDS()->RemoveNode( volNodes[i] );
2457 } // loop on volumes to split
2459 myLastCreatedNodes = newNodes;
2460 myLastCreatedElems = newElems;
2463 //=======================================================================
2464 //function : GetHexaFacetsToSplit
2465 //purpose : For hexahedra that will be split into prisms, finds facets to
2466 // split into triangles. Only hexahedra adjacent to the one closest
2467 // to theFacetNormal.Location() are returned.
2468 //param [in,out] theHexas - the hexahedra
2469 //param [in] theFacetNormal - facet normal
2470 //param [out] theFacets - the hexahedra and found facet IDs
2471 //=======================================================================
2473 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2474 const gp_Ax1& theFacetNormal,
2475 TFacetOfElem & theFacets)
2477 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2479 // Find a hexa closest to the location of theFacetNormal
2481 const SMDS_MeshElement* startHex;
2483 // get SMDS_ElemIteratorPtr on theHexas
2484 typedef const SMDS_MeshElement* TValue;
2485 typedef TIDSortedElemSet::iterator TSetIterator;
2486 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2487 typedef SMDS_MeshElement::GeomFilter TFilter;
2488 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2489 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2490 ( new TElemSetIter( theHexas.begin(),
2492 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2494 SMESH_ElementSearcher* searcher =
2495 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2497 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2502 throw SALOME_Exception( THIS_METHOD "startHex not found");
2505 // Select a facet of startHex by theFacetNormal
2507 SMDS_VolumeTool vTool( startHex );
2508 double norm[3], dot, maxDot = 0;
2510 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2511 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2513 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2521 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2523 // Fill theFacets starting from facetID of startHex
2525 // facets used for seach of volumes adjacent to already treated ones
2526 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2527 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2528 TFacetMap facetsToCheck;
2530 set<const SMDS_MeshNode*> facetNodes;
2531 const SMDS_MeshElement* curHex;
2533 const bool allHex = ( theHexas.size() == myMesh->NbHexas() );
2537 // move in two directions from startHex via facetID
2538 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2541 int curFacet = facetID;
2542 if ( is2nd ) // do not treat startHex twice
2544 vTool.Set( curHex );
2545 if ( vTool.IsFreeFace( curFacet, &curHex ))
2551 vTool.GetFaceNodes( curFacet, facetNodes );
2552 vTool.Set( curHex );
2553 curFacet = vTool.GetFaceIndex( facetNodes );
2558 // store a facet to split
2559 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2561 theFacets.insert( make_pair( curHex, -1 ));
2564 if ( !allHex && !theHexas.count( curHex ))
2567 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2568 theFacets.insert( make_pair( curHex, curFacet ));
2569 if ( !facetIt2isNew.second )
2572 // remember not-to-split facets in facetsToCheck
2573 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2574 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2576 if ( iF == curFacet && iF == oppFacet )
2578 TVolumeFaceKey facetKey ( vTool, iF );
2579 TElemFacets elemFacet( facetIt2isNew.first, iF );
2580 pair< TFacetMap::iterator, bool > it2isnew =
2581 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2582 if ( !it2isnew.second )
2583 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2585 // pass to a volume adjacent via oppFacet
2586 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2592 // get a new curFacet
2593 vTool.GetFaceNodes( oppFacet, facetNodes );
2594 vTool.Set( curHex );
2595 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2598 } // move in two directions from startHex via facetID
2600 // Find a new startHex by facetsToCheck
2604 TFacetMap::iterator fIt = facetsToCheck.begin();
2605 while ( !startHex && fIt != facetsToCheck.end() )
2607 const TElemFacets& elemFacets = fIt->second;
2608 const SMDS_MeshElement* hex = elemFacets.first->first;
2609 int splitFacet = elemFacets.first->second;
2610 int lateralFacet = elemFacets.second;
2611 facetsToCheck.erase( fIt );
2612 fIt = facetsToCheck.begin();
2615 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2616 curHex->GetGeomType() != SMDSGeom_HEXA )
2618 if ( !allHex && !theHexas.count( curHex ))
2623 // find a facet of startHex to split
2625 set<const SMDS_MeshNode*> lateralNodes;
2626 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2627 vTool.GetFaceNodes( splitFacet, facetNodes );
2628 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2629 vTool.Set( startHex );
2630 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2632 // look for a facet of startHex having common nodes with facetNodes
2633 // but not lateralFacet
2634 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2636 if ( iF == lateralFacet )
2638 int nbCommonNodes = 0;
2639 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2640 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2641 nbCommonNodes += facetNodes.count( nn[ iN ]);
2643 if ( nbCommonNodes >= 2 )
2650 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2652 } // while ( startHex )
2659 //================================================================================
2661 * \brief Selects nodes of several elements according to a given interlace
2662 * \param [in] srcNodes - nodes to select from
2663 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2664 * \param [in] interlace - indices of nodes for all elements
2665 * \param [in] nbElems - nb of elements
2666 * \param [in] nbNodes - nb of nodes in each element
2667 * \param [in] mesh - the mesh
2668 * \param [out] elemQueue - a list to push elements found by the selected nodes
2669 * \param [in] type - type of elements to look for
2671 //================================================================================
2673 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2674 vector< const SMDS_MeshNode* >* tgtNodesVec,
2675 const int* interlace,
2678 SMESHDS_Mesh* mesh = 0,
2679 list< const SMDS_MeshElement* >* elemQueue=0,
2680 SMDSAbs_ElementType type=SMDSAbs_All)
2682 for ( int iE = 0; iE < nbElems; ++iE )
2684 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2685 const int* select = & interlace[iE*nbNodes];
2686 elemNodes.resize( nbNodes );
2687 for ( int iN = 0; iN < nbNodes; ++iN )
2688 elemNodes[iN] = srcNodes[ select[ iN ]];
2690 const SMDS_MeshElement* e;
2692 for ( int iE = 0; iE < nbElems; ++iE )
2693 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2694 elemQueue->push_back( e );
2698 //=======================================================================
2700 * Split bi-quadratic elements into linear ones without creation of additional nodes
2701 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2702 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2703 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2704 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2705 * will be split in order to keep the mesh conformal.
2706 * \param elems - elements to split
2708 //=======================================================================
2710 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2712 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2713 vector<const SMDS_MeshElement* > splitElems;
2714 list< const SMDS_MeshElement* > elemQueue;
2715 list< const SMDS_MeshElement* >::iterator elemIt;
2717 SMESHDS_Mesh * mesh = GetMeshDS();
2718 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2719 int nbElems, nbNodes;
2721 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2722 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2725 elemQueue.push_back( *elemSetIt );
2726 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2728 const SMDS_MeshElement* elem = *elemIt;
2729 switch( elem->GetEntityType() )
2731 case SMDSEntity_TriQuad_Hexa: // HEX27
2733 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2734 nbElems = nbNodes = 8;
2735 elemType = & hexaType;
2737 // get nodes for new elements
2738 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2739 { 1,9,20,8, 17,22,26,21 },
2740 { 2,10,20,9, 18,23,26,22 },
2741 { 3,11,20,10, 19,24,26,23 },
2742 { 16,21,26,24, 4,12,25,15 },
2743 { 17,22,26,21, 5,13,25,12 },
2744 { 18,23,26,22, 6,14,25,13 },
2745 { 19,24,26,23, 7,15,25,14 }};
2746 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2748 // add boundary faces to elemQueue
2749 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2750 { 4,5,6,7, 12,13,14,15, 25 },
2751 { 0,1,5,4, 8,17,12,16, 21 },
2752 { 1,2,6,5, 9,18,13,17, 22 },
2753 { 2,3,7,6, 10,19,14,18, 23 },
2754 { 3,0,4,7, 11,16,15,19, 24 }};
2755 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2757 // add boundary segments to elemQueue
2758 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2759 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2760 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2761 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2764 case SMDSEntity_BiQuad_Triangle: // TRIA7
2766 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2769 elemType = & quadType;
2771 // get nodes for new elements
2772 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2773 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2775 // add boundary segments to elemQueue
2776 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2777 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2780 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2782 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2785 elemType = & quadType;
2787 // get nodes for new elements
2788 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2789 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2791 // add boundary segments to elemQueue
2792 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2793 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2796 case SMDSEntity_Quad_Edge:
2798 if ( elemIt == elemQueue.begin() )
2799 continue; // an elem is in theElems
2800 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2803 elemType = & segType;
2805 // get nodes for new elements
2806 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2807 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2811 } // switch( elem->GetEntityType() )
2813 // Create new elements
2815 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2819 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2820 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2821 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2822 //elemType->SetID( -1 );
2824 for ( int iE = 0; iE < nbElems; ++iE )
2825 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2828 ReplaceElemInGroups( elem, splitElems, mesh );
2831 for ( size_t i = 0; i < splitElems.size(); ++i )
2832 subMesh->AddElement( splitElems[i] );
2837 //=======================================================================
2838 //function : AddToSameGroups
2839 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2840 //=======================================================================
2842 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2843 const SMDS_MeshElement* elemInGroups,
2844 SMESHDS_Mesh * aMesh)
2846 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2847 if (!groups.empty()) {
2848 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2849 for ( ; grIt != groups.end(); grIt++ ) {
2850 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2851 if ( group && group->Contains( elemInGroups ))
2852 group->SMDSGroup().Add( elemToAdd );
2858 //=======================================================================
2859 //function : RemoveElemFromGroups
2860 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2861 //=======================================================================
2862 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2863 SMESHDS_Mesh * aMesh)
2865 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2866 if (!groups.empty())
2868 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2869 for (; GrIt != groups.end(); GrIt++)
2871 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2872 if (!grp || grp->IsEmpty()) continue;
2873 grp->SMDSGroup().Remove(removeelem);
2878 //================================================================================
2880 * \brief Replace elemToRm by elemToAdd in the all groups
2882 //================================================================================
2884 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2885 const SMDS_MeshElement* elemToAdd,
2886 SMESHDS_Mesh * aMesh)
2888 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2889 if (!groups.empty()) {
2890 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2891 for ( ; grIt != groups.end(); grIt++ ) {
2892 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2893 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2894 group->SMDSGroup().Add( elemToAdd );
2899 //================================================================================
2901 * \brief Replace elemToRm by elemToAdd in the all groups
2903 //================================================================================
2905 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2906 const vector<const SMDS_MeshElement*>& elemToAdd,
2907 SMESHDS_Mesh * aMesh)
2909 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2910 if (!groups.empty())
2912 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2913 for ( ; grIt != groups.end(); grIt++ ) {
2914 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2915 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2916 for ( int i = 0; i < elemToAdd.size(); ++i )
2917 group->SMDSGroup().Add( elemToAdd[ i ] );
2922 //=======================================================================
2923 //function : QuadToTri
2924 //purpose : Cut quadrangles into triangles.
2925 // theCrit is used to select a diagonal to cut
2926 //=======================================================================
2928 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2929 const bool the13Diag)
2931 myLastCreatedElems.Clear();
2932 myLastCreatedNodes.Clear();
2934 MESSAGE( "::QuadToTri()" );
2936 SMESHDS_Mesh * aMesh = GetMeshDS();
2938 Handle(Geom_Surface) surface;
2939 SMESH_MesherHelper helper( *GetMesh() );
2941 TIDSortedElemSet::iterator itElem;
2942 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2943 const SMDS_MeshElement* elem = *itElem;
2944 if ( !elem || elem->GetType() != SMDSAbs_Face )
2946 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2947 if(!isquad) continue;
2949 if(elem->NbNodes()==4) {
2950 // retrieve element nodes
2951 const SMDS_MeshNode* aNodes [4];
2952 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2954 while ( itN->more() )
2955 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2957 int aShapeId = FindShape( elem );
2958 const SMDS_MeshElement* newElem1 = 0;
2959 const SMDS_MeshElement* newElem2 = 0;
2961 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2962 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2965 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2966 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2968 myLastCreatedElems.Append(newElem1);
2969 myLastCreatedElems.Append(newElem2);
2970 // put a new triangle on the same shape and add to the same groups
2973 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2974 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2976 AddToSameGroups( newElem1, elem, aMesh );
2977 AddToSameGroups( newElem2, elem, aMesh );
2978 aMesh->RemoveElement( elem );
2981 // Quadratic quadrangle
2983 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2985 // get surface elem is on
2986 int aShapeId = FindShape( elem );
2987 if ( aShapeId != helper.GetSubShapeID() ) {
2991 shape = aMesh->IndexToShape( aShapeId );
2992 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2993 TopoDS_Face face = TopoDS::Face( shape );
2994 surface = BRep_Tool::Surface( face );
2995 if ( !surface.IsNull() )
2996 helper.SetSubShape( shape );
3000 const SMDS_MeshNode* aNodes [8];
3001 const SMDS_MeshNode* inFaceNode = 0;
3002 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3004 while ( itN->more() ) {
3005 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3006 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
3007 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
3009 inFaceNode = aNodes[ i-1 ];
3013 // find middle point for (0,1,2,3)
3014 // and create a node in this point;
3016 if ( surface.IsNull() ) {
3018 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
3022 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
3025 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
3027 p = surface->Value( uv.X(), uv.Y() ).XYZ();
3029 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
3030 myLastCreatedNodes.Append(newN);
3032 // create a new element
3033 const SMDS_MeshElement* newElem1 = 0;
3034 const SMDS_MeshElement* newElem2 = 0;
3036 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3037 aNodes[6], aNodes[7], newN );
3038 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3039 newN, aNodes[4], aNodes[5] );
3042 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3043 aNodes[7], aNodes[4], newN );
3044 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3045 newN, aNodes[5], aNodes[6] );
3047 myLastCreatedElems.Append(newElem1);
3048 myLastCreatedElems.Append(newElem2);
3049 // put a new triangle on the same shape and add to the same groups
3052 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3053 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3055 AddToSameGroups( newElem1, elem, aMesh );
3056 AddToSameGroups( newElem2, elem, aMesh );
3057 aMesh->RemoveElement( elem );
3064 //=======================================================================
3065 //function : getAngle
3067 //=======================================================================
3069 double getAngle(const SMDS_MeshElement * tr1,
3070 const SMDS_MeshElement * tr2,
3071 const SMDS_MeshNode * n1,
3072 const SMDS_MeshNode * n2)
3074 double angle = 2. * M_PI; // bad angle
3077 SMESH::Controls::TSequenceOfXYZ P1, P2;
3078 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3079 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3082 if(!tr1->IsQuadratic())
3083 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3085 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3086 if ( N1.SquareMagnitude() <= gp::Resolution() )
3088 if(!tr2->IsQuadratic())
3089 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3091 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3092 if ( N2.SquareMagnitude() <= gp::Resolution() )
3095 // find the first diagonal node n1 in the triangles:
3096 // take in account a diagonal link orientation
3097 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3098 for ( int t = 0; t < 2; t++ ) {
3099 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3100 int i = 0, iDiag = -1;
3101 while ( it->more()) {
3102 const SMDS_MeshElement *n = it->next();
3103 if ( n == n1 || n == n2 ) {
3107 if ( i - iDiag == 1 )
3108 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3117 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3120 angle = N1.Angle( N2 );
3125 // =================================================
3126 // class generating a unique ID for a pair of nodes
3127 // and able to return nodes by that ID
3128 // =================================================
3132 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3133 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3136 long GetLinkID (const SMDS_MeshNode * n1,
3137 const SMDS_MeshNode * n2) const
3139 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3142 bool GetNodes (const long theLinkID,
3143 const SMDS_MeshNode* & theNode1,
3144 const SMDS_MeshNode* & theNode2) const
3146 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3147 if ( !theNode1 ) return false;
3148 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3149 if ( !theNode2 ) return false;
3155 const SMESHDS_Mesh* myMesh;
3160 //=======================================================================
3161 //function : TriToQuad
3162 //purpose : Fuse neighbour triangles into quadrangles.
3163 // theCrit is used to select a neighbour to fuse with.
3164 // theMaxAngle is a max angle between element normals at which
3165 // fusion is still performed.
3166 //=======================================================================
3168 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3169 SMESH::Controls::NumericalFunctorPtr theCrit,
3170 const double theMaxAngle)
3172 myLastCreatedElems.Clear();
3173 myLastCreatedNodes.Clear();
3175 MESSAGE( "::TriToQuad()" );
3177 if ( !theCrit.get() )
3180 SMESHDS_Mesh * aMesh = GetMeshDS();
3182 // Prepare data for algo: build
3183 // 1. map of elements with their linkIDs
3184 // 2. map of linkIDs with their elements
3186 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3187 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3188 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3189 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3191 TIDSortedElemSet::iterator itElem;
3192 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3194 const SMDS_MeshElement* elem = *itElem;
3195 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3196 bool IsTria = ( elem->NbCornerNodes()==3 );
3197 if (!IsTria) continue;
3199 // retrieve element nodes
3200 const SMDS_MeshNode* aNodes [4];
3201 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3204 aNodes[ i++ ] = itN->next();
3205 aNodes[ 3 ] = aNodes[ 0 ];
3208 for ( i = 0; i < 3; i++ ) {
3209 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3210 // check if elements sharing a link can be fused
3211 itLE = mapLi_listEl.find( link );
3212 if ( itLE != mapLi_listEl.end() ) {
3213 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3215 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3216 //if ( FindShape( elem ) != FindShape( elem2 ))
3217 // continue; // do not fuse triangles laying on different shapes
3218 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3219 continue; // avoid making badly shaped quads
3220 (*itLE).second.push_back( elem );
3223 mapLi_listEl[ link ].push_back( elem );
3225 mapEl_setLi [ elem ].insert( link );
3228 // Clean the maps from the links shared by a sole element, ie
3229 // links to which only one element is bound in mapLi_listEl
3231 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3232 int nbElems = (*itLE).second.size();
3233 if ( nbElems < 2 ) {
3234 const SMDS_MeshElement* elem = (*itLE).second.front();
3235 SMESH_TLink link = (*itLE).first;
3236 mapEl_setLi[ elem ].erase( link );
3237 if ( mapEl_setLi[ elem ].empty() )
3238 mapEl_setLi.erase( elem );
3242 // Algo: fuse triangles into quadrangles
3244 while ( ! mapEl_setLi.empty() ) {
3245 // Look for the start element:
3246 // the element having the least nb of shared links
3247 const SMDS_MeshElement* startElem = 0;
3249 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3250 int nbLinks = (*itEL).second.size();
3251 if ( nbLinks < minNbLinks ) {
3252 startElem = (*itEL).first;
3253 minNbLinks = nbLinks;
3254 if ( minNbLinks == 1 )
3259 // search elements to fuse starting from startElem or links of elements
3260 // fused earlyer - startLinks
3261 list< SMESH_TLink > startLinks;
3262 while ( startElem || !startLinks.empty() ) {
3263 while ( !startElem && !startLinks.empty() ) {
3264 // Get an element to start, by a link
3265 SMESH_TLink linkId = startLinks.front();
3266 startLinks.pop_front();
3267 itLE = mapLi_listEl.find( linkId );
3268 if ( itLE != mapLi_listEl.end() ) {
3269 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3270 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3271 for ( ; itE != listElem.end() ; itE++ )
3272 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3274 mapLi_listEl.erase( itLE );
3279 // Get candidates to be fused
3280 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3281 const SMESH_TLink *link12, *link13;
3283 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3284 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3285 ASSERT( !setLi.empty() );
3286 set< SMESH_TLink >::iterator itLi;
3287 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3289 const SMESH_TLink & link = (*itLi);
3290 itLE = mapLi_listEl.find( link );
3291 if ( itLE == mapLi_listEl.end() )
3294 const SMDS_MeshElement* elem = (*itLE).second.front();
3296 elem = (*itLE).second.back();
3297 mapLi_listEl.erase( itLE );
3298 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3309 // add other links of elem to list of links to re-start from
3310 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3311 set< SMESH_TLink >::iterator it;
3312 for ( it = links.begin(); it != links.end(); it++ ) {
3313 const SMESH_TLink& link2 = (*it);
3314 if ( link2 != link )
3315 startLinks.push_back( link2 );
3319 // Get nodes of possible quadrangles
3320 const SMDS_MeshNode *n12 [4], *n13 [4];
3321 bool Ok12 = false, Ok13 = false;
3322 const SMDS_MeshNode *linkNode1, *linkNode2;
3324 linkNode1 = link12->first;
3325 linkNode2 = link12->second;
3326 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3330 linkNode1 = link13->first;
3331 linkNode2 = link13->second;
3332 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3336 // Choose a pair to fuse
3337 if ( Ok12 && Ok13 ) {
3338 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3339 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3340 double aBadRate12 = getBadRate( &quad12, theCrit );
3341 double aBadRate13 = getBadRate( &quad13, theCrit );
3342 if ( aBadRate13 < aBadRate12 )
3349 // and remove fused elems and remove links from the maps
3350 mapEl_setLi.erase( tr1 );
3353 mapEl_setLi.erase( tr2 );
3354 mapLi_listEl.erase( *link12 );
3355 if ( tr1->NbNodes() == 3 )
3357 const SMDS_MeshElement* newElem = 0;
3358 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3359 myLastCreatedElems.Append(newElem);
3360 AddToSameGroups( newElem, tr1, aMesh );
3361 int aShapeId = tr1->getshapeId();
3363 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3364 aMesh->RemoveElement( tr1 );
3365 aMesh->RemoveElement( tr2 );
3368 vector< const SMDS_MeshNode* > N1;
3369 vector< const SMDS_MeshNode* > N2;
3370 getNodesFromTwoTria(tr1,tr2,N1,N2);
3371 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3372 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3373 // i.e. first nodes from both arrays form a new diagonal
3374 const SMDS_MeshNode* aNodes[8];
3383 const SMDS_MeshElement* newElem = 0;
3384 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3385 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3386 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3388 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3389 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3390 myLastCreatedElems.Append(newElem);
3391 AddToSameGroups( newElem, tr1, aMesh );
3392 int aShapeId = tr1->getshapeId();
3394 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3395 aMesh->RemoveElement( tr1 );
3396 aMesh->RemoveElement( tr2 );
3397 // remove middle node (9)
3398 if ( N1[4]->NbInverseElements() == 0 )
3399 aMesh->RemoveNode( N1[4] );
3400 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3401 aMesh->RemoveNode( N1[6] );
3402 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3403 aMesh->RemoveNode( N2[6] );
3408 mapEl_setLi.erase( tr3 );
3409 mapLi_listEl.erase( *link13 );
3410 if ( tr1->NbNodes() == 3 ) {
3411 const SMDS_MeshElement* newElem = 0;
3412 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3413 myLastCreatedElems.Append(newElem);
3414 AddToSameGroups( newElem, tr1, aMesh );
3415 int aShapeId = tr1->getshapeId();
3417 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3418 aMesh->RemoveElement( tr1 );
3419 aMesh->RemoveElement( tr3 );
3422 vector< const SMDS_MeshNode* > N1;
3423 vector< const SMDS_MeshNode* > N2;
3424 getNodesFromTwoTria(tr1,tr3,N1,N2);
3425 // now we receive following N1 and N2 (using numeration as above image)
3426 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3427 // i.e. first nodes from both arrays form a new diagonal
3428 const SMDS_MeshNode* aNodes[8];
3437 const SMDS_MeshElement* newElem = 0;
3438 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3439 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3440 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3442 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3443 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3444 myLastCreatedElems.Append(newElem);
3445 AddToSameGroups( newElem, tr1, aMesh );
3446 int aShapeId = tr1->getshapeId();
3448 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3449 aMesh->RemoveElement( tr1 );
3450 aMesh->RemoveElement( tr3 );
3451 // remove middle node (9)
3452 if ( N1[4]->NbInverseElements() == 0 )
3453 aMesh->RemoveNode( N1[4] );
3454 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3455 aMesh->RemoveNode( N1[6] );
3456 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3457 aMesh->RemoveNode( N2[6] );
3461 // Next element to fuse: the rejected one
3463 startElem = Ok12 ? tr3 : tr2;
3465 } // if ( startElem )
3466 } // while ( startElem || !startLinks.empty() )
3467 } // while ( ! mapEl_setLi.empty() )
3473 /*#define DUMPSO(txt) \
3474 // cout << txt << endl;
3475 //=============================================================================
3479 //=============================================================================
3480 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3484 int tmp = idNodes[ i1 ];
3485 idNodes[ i1 ] = idNodes[ i2 ];
3486 idNodes[ i2 ] = tmp;
3487 gp_Pnt Ptmp = P[ i1 ];
3490 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3493 //=======================================================================
3494 //function : SortQuadNodes
3495 //purpose : Set 4 nodes of a quadrangle face in a good order.
3496 // Swap 1<->2 or 2<->3 nodes and correspondingly return
3498 //=======================================================================
3500 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3505 for ( i = 0; i < 4; i++ ) {
3506 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3508 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3511 gp_Vec V1(P[0], P[1]);
3512 gp_Vec V2(P[0], P[2]);
3513 gp_Vec V3(P[0], P[3]);
3515 gp_Vec Cross1 = V1 ^ V2;
3516 gp_Vec Cross2 = V2 ^ V3;
3519 if (Cross1.Dot(Cross2) < 0)
3524 if (Cross1.Dot(Cross2) < 0)
3528 swap ( i, i + 1, idNodes, P );
3530 // for ( int ii = 0; ii < 4; ii++ ) {
3531 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3532 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3538 //=======================================================================
3539 //function : SortHexaNodes
3540 //purpose : Set 8 nodes of a hexahedron in a good order.
3541 // Return success status
3542 //=======================================================================
3544 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3549 DUMPSO( "INPUT: ========================================");
3550 for ( i = 0; i < 8; i++ ) {
3551 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3552 if ( !n ) return false;
3553 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3554 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3556 DUMPSO( "========================================");
3559 set<int> faceNodes; // ids of bottom face nodes, to be found
3560 set<int> checkedId1; // ids of tried 2-nd nodes
3561 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3562 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
3563 int iMin, iLoop1 = 0;
3565 // Loop to try the 2-nd nodes
3567 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3569 // Find not checked 2-nd node
3570 for ( i = 1; i < 8; i++ )
3571 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3572 int id1 = idNodes[i];
3573 swap ( 1, i, idNodes, P );
3574 checkedId1.insert ( id1 );
3578 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3579 // ie that all but meybe one (id3 which is on the same face) nodes
3580 // lay on the same side from the triangle plane.
3582 bool manyInPlane = false; // more than 4 nodes lay in plane
3584 while ( ++iLoop2 < 6 ) {
3586 // get 1-2-3 plane coeffs
3587 Standard_Real A, B, C, D;
3588 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3589 if ( N.SquareMagnitude() > gp::Resolution() )
3591 gp_Pln pln ( P[0], N );
3592 pln.Coefficients( A, B, C, D );
3594 // find the node (iMin) closest to pln
3595 Standard_Real dist[ 8 ], minDist = DBL_MAX;
3597 for ( i = 3; i < 8; i++ ) {
3598 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3599 if ( fabs( dist[i] ) < minDist ) {
3600 minDist = fabs( dist[i] );
3603 if ( fabs( dist[i] ) <= tol )
3604 idInPln.insert( idNodes[i] );
3607 // there should not be more than 4 nodes in bottom plane
3608 if ( idInPln.size() > 1 )
3610 DUMPSO( "### idInPln.size() = " << idInPln.size());
3611 // idInPlane does not contain the first 3 nodes
3612 if ( manyInPlane || idInPln.size() == 5)
3613 return false; // all nodes in one plane
3616 // set the 1-st node to be not in plane
3617 for ( i = 3; i < 8; i++ ) {
3618 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3619 DUMPSO( "### Reset 0-th node");
3620 swap( 0, i, idNodes, P );
3625 // reset to re-check second nodes
3626 leastDist = DBL_MAX;
3630 break; // from iLoop2;
3633 // check that the other 4 nodes are on the same side
3634 bool sameSide = true;
3635 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3636 for ( i = 3; sameSide && i < 8; i++ ) {
3638 sameSide = ( isNeg == dist[i] <= 0.);
3641 // keep best solution
3642 if ( sameSide && minDist < leastDist ) {
3643 leastDist = minDist;
3645 faceNodes.insert( idNodes[ 1 ] );
3646 faceNodes.insert( idNodes[ 2 ] );
3647 faceNodes.insert( idNodes[ iMin ] );
3648 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3649 << " leastDist = " << leastDist);
3650 if ( leastDist <= DBL_MIN )
3655 // set next 3-d node to check
3656 int iNext = 2 + iLoop2;
3658 DUMPSO( "Try 2-nd");
3659 swap ( 2, iNext, idNodes, P );
3661 } // while ( iLoop2 < 6 )
3664 if ( faceNodes.empty() ) return false;
3666 // Put the faceNodes in proper places
3667 for ( i = 4; i < 8; i++ ) {
3668 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3669 // find a place to put
3671 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3673 DUMPSO( "Set faceNodes");
3674 swap ( iTo, i, idNodes, P );
3679 // Set nodes of the found bottom face in good order
3680 DUMPSO( " Found bottom face: ");
3681 i = SortQuadNodes( theMesh, idNodes );
3683 gp_Pnt Ptmp = P[ i ];
3688 // for ( int ii = 0; ii < 4; ii++ ) {
3689 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3690 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3693 // Gravity center of the top and bottom faces
3694 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3695 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3697 // Get direction from the bottom to the top face
3698 gp_Vec upDir ( aGCb, aGCt );
3699 Standard_Real upDirSize = upDir.Magnitude();
3700 if ( upDirSize <= gp::Resolution() ) return false;
3703 // Assure that the bottom face normal points up
3704 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3705 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3706 if ( Nb.Dot( upDir ) < 0 ) {
3707 DUMPSO( "Reverse bottom face");
3708 swap( 1, 3, idNodes, P );
3711 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3712 Standard_Real minDist = DBL_MAX;
3713 for ( i = 4; i < 8; i++ ) {
3714 // projection of P[i] to the plane defined by P[0] and upDir
3715 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3716 Standard_Real sqDist = P[0].SquareDistance( Pp );
3717 if ( sqDist < minDist ) {
3722 DUMPSO( "Set 4-th");
3723 swap ( 4, iMin, idNodes, P );
3725 // Set nodes of the top face in good order
3726 DUMPSO( "Sort top face");
3727 i = SortQuadNodes( theMesh, &idNodes[4] );
3730 gp_Pnt Ptmp = P[ i ];
3735 // Assure that direction of the top face normal is from the bottom face
3736 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3737 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3738 if ( Nt.Dot( upDir ) < 0 ) {
3739 DUMPSO( "Reverse top face");
3740 swap( 5, 7, idNodes, P );
3743 // DUMPSO( "OUTPUT: ========================================");
3744 // for ( i = 0; i < 8; i++ ) {
3745 // float *p = ugrid->GetPoint(idNodes[i]);
3746 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3752 //================================================================================
3754 * \brief Return nodes linked to the given one
3755 * \param theNode - the node
3756 * \param linkedNodes - the found nodes
3757 * \param type - the type of elements to check
3759 * Medium nodes are ignored
3761 //================================================================================
3763 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3764 TIDSortedElemSet & linkedNodes,
3765 SMDSAbs_ElementType type )
3767 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3768 while ( elemIt->more() )
3770 const SMDS_MeshElement* elem = elemIt->next();
3771 if(elem->GetType() == SMDSAbs_0DElement)
3774 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3775 if ( elem->GetType() == SMDSAbs_Volume )
3777 SMDS_VolumeTool vol( elem );
3778 while ( nodeIt->more() ) {
3779 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3780 if ( theNode != n && vol.IsLinked( theNode, n ))
3781 linkedNodes.insert( n );
3786 for ( int i = 0; nodeIt->more(); ++i ) {
3787 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3788 if ( n == theNode ) {
3789 int iBefore = i - 1;
3791 if ( elem->IsQuadratic() ) {
3792 int nb = elem->NbNodes() / 2;
3793 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3794 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3796 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3797 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3804 //=======================================================================
3805 //function : laplacianSmooth
3806 //purpose : pulls theNode toward the center of surrounding nodes directly
3807 // connected to that node along an element edge
3808 //=======================================================================
3810 void laplacianSmooth(const SMDS_MeshNode* theNode,
3811 const Handle(Geom_Surface)& theSurface,
3812 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3814 // find surrounding nodes
3816 TIDSortedElemSet nodeSet;
3817 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3819 // compute new coodrs
3821 double coord[] = { 0., 0., 0. };
3822 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3823 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3824 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3825 if ( theSurface.IsNull() ) { // smooth in 3D
3826 coord[0] += node->X();
3827 coord[1] += node->Y();
3828 coord[2] += node->Z();
3830 else { // smooth in 2D
3831 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3832 gp_XY* uv = theUVMap[ node ];
3833 coord[0] += uv->X();
3834 coord[1] += uv->Y();
3837 int nbNodes = nodeSet.size();
3840 coord[0] /= nbNodes;
3841 coord[1] /= nbNodes;
3843 if ( !theSurface.IsNull() ) {
3844 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3845 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3846 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3852 coord[2] /= nbNodes;
3856 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3859 //=======================================================================
3860 //function : centroidalSmooth
3861 //purpose : pulls theNode toward the element-area-weighted centroid of the
3862 // surrounding elements
3863 //=======================================================================
3865 void centroidalSmooth(const SMDS_MeshNode* theNode,
3866 const Handle(Geom_Surface)& theSurface,
3867 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3869 gp_XYZ aNewXYZ(0.,0.,0.);
3870 SMESH::Controls::Area anAreaFunc;
3871 double totalArea = 0.;
3876 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3877 while ( elemIt->more() )
3879 const SMDS_MeshElement* elem = elemIt->next();
3882 gp_XYZ elemCenter(0.,0.,0.);
3883 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3884 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3885 int nn = elem->NbNodes();
3886 if(elem->IsQuadratic()) nn = nn/2;
3888 //while ( itN->more() ) {
3890 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3892 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3893 aNodePoints.push_back( aP );
3894 if ( !theSurface.IsNull() ) { // smooth in 2D
3895 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3896 gp_XY* uv = theUVMap[ aNode ];
3897 aP.SetCoord( uv->X(), uv->Y(), 0. );
3901 double elemArea = anAreaFunc.GetValue( aNodePoints );
3902 totalArea += elemArea;
3904 aNewXYZ += elemCenter * elemArea;
3906 aNewXYZ /= totalArea;
3907 if ( !theSurface.IsNull() ) {
3908 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3909 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3914 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3917 //=======================================================================
3918 //function : getClosestUV
3919 //purpose : return UV of closest projection
3920 //=======================================================================
3922 static bool getClosestUV (Extrema_GenExtPS& projector,
3923 const gp_Pnt& point,
3926 projector.Perform( point );
3927 if ( projector.IsDone() ) {
3928 double u, v, minVal = DBL_MAX;
3929 for ( int i = projector.NbExt(); i > 0; i-- )
3930 if ( projector.SquareDistance( i ) < minVal ) {
3931 minVal = projector.SquareDistance( i );
3932 projector.Point( i ).Parameter( u, v );
3934 result.SetCoord( u, v );
3940 //=======================================================================
3942 //purpose : Smooth theElements during theNbIterations or until a worst
3943 // element has aspect ratio <= theTgtAspectRatio.
3944 // Aspect Ratio varies in range [1.0, inf].
3945 // If theElements is empty, the whole mesh is smoothed.
3946 // theFixedNodes contains additionally fixed nodes. Nodes built
3947 // on edges and boundary nodes are always fixed.
3948 //=======================================================================
3950 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3951 set<const SMDS_MeshNode*> & theFixedNodes,
3952 const SmoothMethod theSmoothMethod,
3953 const int theNbIterations,
3954 double theTgtAspectRatio,
3957 myLastCreatedElems.Clear();
3958 myLastCreatedNodes.Clear();
3960 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3962 if ( theTgtAspectRatio < 1.0 )
3963 theTgtAspectRatio = 1.0;
3965 const double disttol = 1.e-16;
3967 SMESH::Controls::AspectRatio aQualityFunc;
3969 SMESHDS_Mesh* aMesh = GetMeshDS();
3971 if ( theElems.empty() ) {
3972 // add all faces to theElems
3973 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3974 while ( fIt->more() ) {
3975 const SMDS_MeshElement* face = fIt->next();
3976 theElems.insert( theElems.end(), face );
3979 // get all face ids theElems are on
3980 set< int > faceIdSet;
3981 TIDSortedElemSet::iterator itElem;
3983 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3984 int fId = FindShape( *itElem );
3985 // check that corresponding submesh exists and a shape is face
3987 faceIdSet.find( fId ) == faceIdSet.end() &&
3988 aMesh->MeshElements( fId )) {
3989 TopoDS_Shape F = aMesh->IndexToShape( fId );
3990 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3991 faceIdSet.insert( fId );
3994 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3996 // ===============================================
3997 // smooth elements on each TopoDS_Face separately
3998 // ===============================================
4000 SMESH_MesherHelper helper( *GetMesh() );
4002 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
4003 for ( ; fId != faceIdSet.rend(); ++fId )
4005 // get face surface and submesh
4006 Handle(Geom_Surface) surface;
4007 SMESHDS_SubMesh* faceSubMesh = 0;
4009 double fToler2 = 0, f,l;
4010 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
4011 bool isUPeriodic = false, isVPeriodic = false;
4014 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
4015 surface = BRep_Tool::Surface( face );
4016 faceSubMesh = aMesh->MeshElements( *fId );
4017 fToler2 = BRep_Tool::Tolerance( face );
4018 fToler2 *= fToler2 * 10.;
4019 isUPeriodic = surface->IsUPeriodic();
4022 isVPeriodic = surface->IsVPeriodic();
4025 surface->Bounds( u1, u2, v1, v2 );
4026 helper.SetSubShape( face );
4028 // ---------------------------------------------------------
4029 // for elements on a face, find movable and fixed nodes and
4030 // compute UV for them
4031 // ---------------------------------------------------------
4032 bool checkBoundaryNodes = false;
4033 bool isQuadratic = false;
4034 set<const SMDS_MeshNode*> setMovableNodes;
4035 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4036 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4037 list< const SMDS_MeshElement* > elemsOnFace;
4039 Extrema_GenExtPS projector;
4040 GeomAdaptor_Surface surfAdaptor;
4041 if ( !surface.IsNull() ) {
4042 surfAdaptor.Load( surface );
4043 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4045 int nbElemOnFace = 0;
4046 itElem = theElems.begin();
4047 // loop on not yet smoothed elements: look for elems on a face
4048 while ( itElem != theElems.end() )
4050 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4051 break; // all elements found
4053 const SMDS_MeshElement* elem = *itElem;
4054 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4055 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4059 elemsOnFace.push_back( elem );
4060 theElems.erase( itElem++ );
4064 isQuadratic = elem->IsQuadratic();
4066 // get movable nodes of elem
4067 const SMDS_MeshNode* node;
4068 SMDS_TypeOfPosition posType;
4069 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4070 int nn = 0, nbn = elem->NbNodes();
4071 if(elem->IsQuadratic())
4073 while ( nn++ < nbn ) {
4074 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4075 const SMDS_PositionPtr& pos = node->GetPosition();
4076 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4077 if (posType != SMDS_TOP_EDGE &&
4078 posType != SMDS_TOP_VERTEX &&
4079 theFixedNodes.find( node ) == theFixedNodes.end())
4081 // check if all faces around the node are on faceSubMesh
4082 // because a node on edge may be bound to face
4083 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4085 if ( faceSubMesh ) {
4086 while ( eIt->more() && all ) {
4087 const SMDS_MeshElement* e = eIt->next();
4088 all = faceSubMesh->Contains( e );
4092 setMovableNodes.insert( node );
4094 checkBoundaryNodes = true;
4096 if ( posType == SMDS_TOP_3DSPACE )
4097 checkBoundaryNodes = true;
4100 if ( surface.IsNull() )
4103 // get nodes to check UV
4104 list< const SMDS_MeshNode* > uvCheckNodes;
4105 const SMDS_MeshNode* nodeInFace = 0;
4106 itN = elem->nodesIterator();
4107 nn = 0; nbn = elem->NbNodes();
4108 if(elem->IsQuadratic())
4110 while ( nn++ < nbn ) {
4111 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4112 if ( node->GetPosition()->GetDim() == 2 )
4114 if ( uvMap.find( node ) == uvMap.end() )
4115 uvCheckNodes.push_back( node );
4116 // add nodes of elems sharing node
4117 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4118 // while ( eIt->more() ) {
4119 // const SMDS_MeshElement* e = eIt->next();
4120 // if ( e != elem ) {
4121 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4122 // while ( nIt->more() ) {
4123 // const SMDS_MeshNode* n =
4124 // static_cast<const SMDS_MeshNode*>( nIt->next() );
4125 // if ( uvMap.find( n ) == uvMap.end() )
4126 // uvCheckNodes.push_back( n );
4132 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4133 for ( ; n != uvCheckNodes.end(); ++n ) {
4136 const SMDS_PositionPtr& pos = node->GetPosition();
4137 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4141 bool toCheck = true;
4142 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4144 // compute not existing UV
4145 bool project = ( posType == SMDS_TOP_3DSPACE );
4146 // double dist1 = DBL_MAX, dist2 = 0;
4147 // if ( posType != SMDS_TOP_3DSPACE ) {
4148 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4149 // project = dist1 > fToler2;
4151 if ( project ) { // compute new UV
4153 gp_Pnt pNode = SMESH_TNodeXYZ( node );
4154 if ( !getClosestUV( projector, pNode, newUV )) {
4155 MESSAGE("Node Projection Failed " << node);
4159 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4161 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4163 // if ( posType != SMDS_TOP_3DSPACE )
4164 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4165 // if ( dist2 < dist1 )
4169 // store UV in the map
4170 listUV.push_back( uv );
4171 uvMap.insert( make_pair( node, &listUV.back() ));
4173 } // loop on not yet smoothed elements
4175 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4176 checkBoundaryNodes = true;
4178 // fix nodes on mesh boundary
4180 if ( checkBoundaryNodes ) {
4181 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4182 map< SMESH_TLink, int >::iterator link_nb;
4183 // put all elements links to linkNbMap
4184 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4185 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4186 const SMDS_MeshElement* elem = (*elemIt);
4187 int nbn = elem->NbCornerNodes();
4188 // loop on elem links: insert them in linkNbMap
4189 for ( int iN = 0; iN < nbn; ++iN ) {
4190 const SMDS_MeshNode* n1 = elem->GetNode( iN );
4191 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4192 SMESH_TLink link( n1, n2 );
4193 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4197 // remove nodes that are in links encountered only once from setMovableNodes
4198 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4199 if ( link_nb->second == 1 ) {
4200 setMovableNodes.erase( link_nb->first.node1() );
4201 setMovableNodes.erase( link_nb->first.node2() );
4206 // -----------------------------------------------------
4207 // for nodes on seam edge, compute one more UV ( uvMap2 );
4208 // find movable nodes linked to nodes on seam and which
4209 // are to be smoothed using the second UV ( uvMap2 )
4210 // -----------------------------------------------------
4212 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4213 if ( !surface.IsNull() ) {
4214 TopExp_Explorer eExp( face, TopAbs_EDGE );
4215 for ( ; eExp.More(); eExp.Next() ) {
4216 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4217 if ( !BRep_Tool::IsClosed( edge, face ))
4219 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4220 if ( !sm ) continue;
4221 // find out which parameter varies for a node on seam
4224 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4225 if ( pcurve.IsNull() ) continue;
4226 uv1 = pcurve->Value( f );
4228 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4229 if ( pcurve.IsNull() ) continue;
4230 uv2 = pcurve->Value( f );
4231 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4233 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4234 std::swap( uv1, uv2 );
4235 // get nodes on seam and its vertices
4236 list< const SMDS_MeshNode* > seamNodes;
4237 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4238 while ( nSeamIt->more() ) {
4239 const SMDS_MeshNode* node = nSeamIt->next();
4240 if ( !isQuadratic || !IsMedium( node ))
4241 seamNodes.push_back( node );
4243 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4244 for ( ; vExp.More(); vExp.Next() ) {
4245 sm = aMesh->MeshElements( vExp.Current() );
4247 nSeamIt = sm->GetNodes();
4248 while ( nSeamIt->more() )
4249 seamNodes.push_back( nSeamIt->next() );
4252 // loop on nodes on seam
4253 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4254 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4255 const SMDS_MeshNode* nSeam = *noSeIt;
4256 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4257 if ( n_uv == uvMap.end() )
4260 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4261 // set the second UV
4262 listUV.push_back( *n_uv->second );
4263 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4264 if ( uvMap2.empty() )
4265 uvMap2 = uvMap; // copy the uvMap contents
4266 uvMap2[ nSeam ] = &listUV.back();
4268 // collect movable nodes linked to ones on seam in nodesNearSeam
4269 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4270 while ( eIt->more() ) {
4271 const SMDS_MeshElement* e = eIt->next();
4272 int nbUseMap1 = 0, nbUseMap2 = 0;
4273 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4274 int nn = 0, nbn = e->NbNodes();
4275 if(e->IsQuadratic()) nbn = nbn/2;
4276 while ( nn++ < nbn )
4278 const SMDS_MeshNode* n =
4279 static_cast<const SMDS_MeshNode*>( nIt->next() );
4281 setMovableNodes.find( n ) == setMovableNodes.end() )
4283 // add only nodes being closer to uv2 than to uv1
4284 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4285 // 0.5 * ( n->Y() + nSeam->Y() ),
4286 // 0.5 * ( n->Z() + nSeam->Z() ));
4288 // getClosestUV( projector, pMid, uv );
4289 double x = uvMap[ n ]->Coord( iPar );
4290 if ( Abs( uv1.Coord( iPar ) - x ) >
4291 Abs( uv2.Coord( iPar ) - x )) {
4292 nodesNearSeam.insert( n );
4298 // for centroidalSmooth all element nodes must
4299 // be on one side of a seam
4300 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4301 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4303 while ( nn++ < nbn ) {
4304 const SMDS_MeshNode* n =
4305 static_cast<const SMDS_MeshNode*>( nIt->next() );
4306 setMovableNodes.erase( n );
4310 } // loop on nodes on seam
4311 } // loop on edge of a face
4312 } // if ( !face.IsNull() )
4314 if ( setMovableNodes.empty() ) {
4315 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4316 continue; // goto next face
4324 double maxRatio = -1., maxDisplacement = -1.;
4325 set<const SMDS_MeshNode*>::iterator nodeToMove;
4326 for ( it = 0; it < theNbIterations; it++ ) {
4327 maxDisplacement = 0.;
4328 nodeToMove = setMovableNodes.begin();
4329 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4330 const SMDS_MeshNode* node = (*nodeToMove);
4331 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4334 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4335 if ( theSmoothMethod == LAPLACIAN )
4336 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4338 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4340 // node displacement
4341 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4342 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4343 if ( aDispl > maxDisplacement )
4344 maxDisplacement = aDispl;
4346 // no node movement => exit
4347 //if ( maxDisplacement < 1.e-16 ) {
4348 if ( maxDisplacement < disttol ) {
4349 MESSAGE("-- no node movement --");
4353 // check elements quality
4355 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4356 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4357 const SMDS_MeshElement* elem = (*elemIt);
4358 if ( !elem || elem->GetType() != SMDSAbs_Face )
4360 SMESH::Controls::TSequenceOfXYZ aPoints;
4361 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4362 double aValue = aQualityFunc.GetValue( aPoints );
4363 if ( aValue > maxRatio )
4367 if ( maxRatio <= theTgtAspectRatio ) {
4368 MESSAGE("-- quality achived --");
4371 if (it+1 == theNbIterations) {
4372 MESSAGE("-- Iteration limit exceeded --");
4374 } // smoothing iterations
4376 MESSAGE(" Face id: " << *fId <<
4377 " Nb iterstions: " << it <<
4378 " Displacement: " << maxDisplacement <<
4379 " Aspect Ratio " << maxRatio);
4381 // ---------------------------------------
4382 // new nodes positions are computed,
4383 // record movement in DS and set new UV
4384 // ---------------------------------------
4385 nodeToMove = setMovableNodes.begin();
4386 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4387 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4388 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4389 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4390 if ( node_uv != uvMap.end() ) {
4391 gp_XY* uv = node_uv->second;
4393 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4397 // move medium nodes of quadratic elements
4400 vector<const SMDS_MeshNode*> nodes;
4402 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4403 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4405 const SMDS_MeshElement* QF = *elemIt;
4406 if ( QF->IsQuadratic() )
4408 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4409 SMDS_MeshElement::iterator() );
4410 nodes.push_back( nodes[0] );
4412 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4414 if ( !surface.IsNull() )
4416 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4417 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4418 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4419 xyz = surface->Value( uv.X(), uv.Y() );
4422 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4424 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4425 // we have to move a medium node
4426 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4432 } // loop on face ids
4438 //=======================================================================
4439 //function : isReverse
4440 //purpose : Return true if normal of prevNodes is not co-directied with
4441 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4442 // iNotSame is where prevNodes and nextNodes are different.
4443 // If result is true then future volume orientation is OK
4444 //=======================================================================
4446 bool isReverse(const SMDS_MeshElement* face,
4447 const vector<const SMDS_MeshNode*>& prevNodes,
4448 const vector<const SMDS_MeshNode*>& nextNodes,
4452 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4453 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4454 gp_XYZ extrDir( pN - pP ), faceNorm;
4455 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4457 return faceNorm * extrDir < 0.0;
4460 //================================================================================
4462 * \brief Assure that theElemSets[0] holds elements, not nodes
4464 //================================================================================
4466 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4468 if ( !theElemSets[0].empty() &&
4469 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4471 std::swap( theElemSets[0], theElemSets[1] );
4473 else if ( !theElemSets[1].empty() &&
4474 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4476 std::swap( theElemSets[0], theElemSets[1] );
4481 //=======================================================================
4483 * \brief Create elements by sweeping an element
4484 * \param elem - element to sweep
4485 * \param newNodesItVec - nodes generated from each node of the element
4486 * \param newElems - generated elements
4487 * \param nbSteps - number of sweeping steps
4488 * \param srcElements - to append elem for each generated element
4490 //=======================================================================
4492 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4493 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4494 list<const SMDS_MeshElement*>& newElems,
4496 SMESH_SequenceOfElemPtr& srcElements)
4498 //MESSAGE("sweepElement " << nbSteps);
4499 SMESHDS_Mesh* aMesh = GetMeshDS();
4501 const int nbNodes = elem->NbNodes();
4502 const int nbCorners = elem->NbCornerNodes();
4503 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4504 polyhedron creation !!! */
4505 // Loop on elem nodes:
4506 // find new nodes and detect same nodes indices
4507 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4508 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4509 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4510 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4512 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4513 vector<int> sames(nbNodes);
4514 vector<bool> isSingleNode(nbNodes);
4516 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4517 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4518 const SMDS_MeshNode* node = nnIt->first;
4519 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4520 if ( listNewNodes.empty() )
4523 itNN [ iNode ] = listNewNodes.begin();
4524 prevNod[ iNode ] = node;
4525 nextNod[ iNode ] = listNewNodes.front();
4527 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4528 corner node of linear */
4529 if ( prevNod[ iNode ] != nextNod [ iNode ])
4530 nbDouble += !isSingleNode[iNode];
4532 if( iNode < nbCorners ) { // check corners only
4533 if ( prevNod[ iNode ] == nextNod [ iNode ])
4534 sames[nbSame++] = iNode;
4536 iNotSameNode = iNode;
4540 if ( nbSame == nbNodes || nbSame > 2) {
4541 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4545 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4547 // fix nodes order to have bottom normal external
4548 if ( baseType == SMDSEntity_Polygon )
4550 std::reverse( itNN.begin(), itNN.end() );
4551 std::reverse( prevNod.begin(), prevNod.end() );
4552 std::reverse( midlNod.begin(), midlNod.end() );
4553 std::reverse( nextNod.begin(), nextNod.end() );
4554 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4558 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4559 SMDS_MeshCell::applyInterlace( ind, itNN );
4560 SMDS_MeshCell::applyInterlace( ind, prevNod );
4561 SMDS_MeshCell::applyInterlace( ind, nextNod );
4562 SMDS_MeshCell::applyInterlace( ind, midlNod );
4563 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4566 sames[nbSame] = iNotSameNode;
4567 for ( int j = 0; j <= nbSame; ++j )
4568 for ( size_t i = 0; i < ind.size(); ++i )
4569 if ( ind[i] == sames[j] )
4574 iNotSameNode = sames[nbSame];
4578 else if ( elem->GetType() == SMDSAbs_Edge )
4580 // orient a new face same as adjacent one
4582 const SMDS_MeshElement* e;
4583 TIDSortedElemSet dummy;
4584 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4585 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4586 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4588 // there is an adjacent face, check order of nodes in it
4589 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4592 std::swap( itNN[0], itNN[1] );
4593 std::swap( prevNod[0], prevNod[1] );
4594 std::swap( nextNod[0], nextNod[1] );
4595 isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4597 sames[0] = 1 - sames[0];
4598 iNotSameNode = 1 - iNotSameNode;
4603 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4605 iSameNode = sames[ nbSame-1 ];
4606 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4607 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4608 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4611 if ( baseType == SMDSEntity_Polygon )
4613 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4614 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4616 else if ( baseType == SMDSEntity_Quad_Polygon )
4618 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4619 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4622 // make new elements
4623 for (int iStep = 0; iStep < nbSteps; iStep++ )
4626 for ( iNode = 0; iNode < nbNodes; iNode++ )
4628 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4629 nextNod[ iNode ] = *itNN[ iNode ]++;
4632 SMDS_MeshElement* aNewElem = 0;
4633 /*if(!elem->IsPoly())*/ {
4634 switch ( baseType ) {
4636 case SMDSEntity_Node: { // sweep NODE
4637 if ( nbSame == 0 ) {
4638 if ( isSingleNode[0] )
4639 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4641 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4647 case SMDSEntity_Edge: { // sweep EDGE
4648 if ( nbDouble == 0 )
4650 if ( nbSame == 0 ) // ---> quadrangle
4651 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4652 nextNod[ 1 ], nextNod[ 0 ] );
4653 else // ---> triangle
4654 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4655 nextNod[ iNotSameNode ] );
4657 else // ---> polygon
4659 vector<const SMDS_MeshNode*> poly_nodes;
4660 poly_nodes.push_back( prevNod[0] );
4661 poly_nodes.push_back( prevNod[1] );
4662 if ( prevNod[1] != nextNod[1] )
4664 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4665 poly_nodes.push_back( nextNod[1] );
4667 if ( prevNod[0] != nextNod[0] )
4669 poly_nodes.push_back( nextNod[0] );
4670 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4672 switch ( poly_nodes.size() ) {
4674 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4677 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4678 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4681 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4686 case SMDSEntity_Triangle: // TRIANGLE --->
4688 if ( nbDouble > 0 ) break;
4689 if ( nbSame == 0 ) // ---> pentahedron
4690 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4691 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4693 else if ( nbSame == 1 ) // ---> pyramid
4694 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4695 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4696 nextNod[ iSameNode ]);
4698 else // 2 same nodes: ---> tetrahedron
4699 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4700 nextNod[ iNotSameNode ]);
4703 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4707 if ( nbDouble+nbSame == 2 )
4709 if(nbSame==0) { // ---> quadratic quadrangle
4710 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4711 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4713 else { //(nbSame==1) // ---> quadratic triangle
4715 return; // medium node on axis
4717 else if(sames[0]==0)
4718 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4719 prevNod[2], midlNod[1], nextNod[2] );
4721 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4722 prevNod[2], nextNod[2], midlNod[0]);
4725 else if ( nbDouble == 3 )
4727 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4728 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4729 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4736 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4737 if ( nbDouble > 0 ) break;
4739 if ( nbSame == 0 ) // ---> hexahedron
4740 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4741 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4743 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4744 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4745 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4746 nextNod[ iSameNode ]);
4747 newElems.push_back( aNewElem );
4748 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4749 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4750 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4752 else if ( nbSame == 2 ) { // ---> pentahedron
4753 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4754 // iBeforeSame is same too
4755 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4756 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4757 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4759 // iAfterSame is same too
4760 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4761 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4762 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4766 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4767 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4768 if ( nbDouble+nbSame != 3 ) break;
4770 // ---> pentahedron with 15 nodes
4771 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4772 nextNod[0], nextNod[1], nextNod[2],
4773 prevNod[3], prevNod[4], prevNod[5],
4774 nextNod[3], nextNod[4], nextNod[5],
4775 midlNod[0], midlNod[1], midlNod[2]);
4777 else if(nbSame==1) {
4778 // ---> 2d order pyramid of 13 nodes
4779 int apex = iSameNode;
4780 int i0 = ( apex + 1 ) % nbCorners;
4781 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4785 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4786 nextNod[i0], nextNod[i1], prevNod[apex],
4787 prevNod[i01], midlNod[i0],
4788 nextNod[i01], midlNod[i1],
4789 prevNod[i1a], prevNod[i0a],
4790 nextNod[i0a], nextNod[i1a]);
4792 else if(nbSame==2) {
4793 // ---> 2d order tetrahedron of 10 nodes
4794 int n1 = iNotSameNode;
4795 int n2 = ( n1 + 1 ) % nbCorners;
4796 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4800 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4801 prevNod[n12], prevNod[n23], prevNod[n31],
4802 midlNod[n1], nextNod[n12], nextNod[n31]);
4806 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4808 if ( nbDouble != 4 ) break;
4809 // ---> hexahedron with 20 nodes
4810 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4811 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4812 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4813 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4814 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4816 else if(nbSame==1) {
4817 // ---> pyramid + pentahedron - can not be created since it is needed
4818 // additional middle node at the center of face
4819 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4822 else if( nbSame == 2 ) {
4823 if ( nbDouble != 2 ) break;
4824 // ---> 2d order Pentahedron with 15 nodes
4826 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4827 // iBeforeSame is same too
4834 // iAfterSame is same too
4844 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4845 prevNod[n4], prevNod[n5], nextNod[n5],
4846 prevNod[n12], midlNod[n2], nextNod[n12],
4847 prevNod[n45], midlNod[n5], nextNod[n45],
4848 prevNod[n14], prevNod[n25], nextNod[n25]);
4852 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4854 if( nbSame == 0 && nbDouble == 9 ) {
4855 // ---> tri-quadratic hexahedron with 27 nodes
4856 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4857 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4858 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4859 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4860 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4861 prevNod[8], // bottom center
4862 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4863 nextNod[8], // top center
4864 midlNod[8]);// elem center
4872 case SMDSEntity_Polygon: { // sweep POLYGON
4874 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4875 // ---> hexagonal prism
4876 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4877 prevNod[3], prevNod[4], prevNod[5],
4878 nextNod[0], nextNod[1], nextNod[2],
4879 nextNod[3], nextNod[4], nextNod[5]);
4883 case SMDSEntity_Ball:
4888 } // switch ( baseType )
4891 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4893 if ( baseType != SMDSEntity_Polygon )
4895 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4896 SMDS_MeshCell::applyInterlace( ind, prevNod );
4897 SMDS_MeshCell::applyInterlace( ind, nextNod );
4898 SMDS_MeshCell::applyInterlace( ind, midlNod );
4899 SMDS_MeshCell::applyInterlace( ind, itNN );
4900 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4901 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4903 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4904 vector<int> quantities (nbNodes + 2);
4905 polyedre_nodes.clear();
4909 for (int inode = 0; inode < nbNodes; inode++)
4910 polyedre_nodes.push_back( prevNod[inode] );
4911 quantities.push_back( nbNodes );
4914 polyedre_nodes.push_back( nextNod[0] );
4915 for (int inode = nbNodes; inode-1; --inode )
4916 polyedre_nodes.push_back( nextNod[inode-1] );
4917 quantities.push_back( nbNodes );
4925 const int iQuad = elem->IsQuadratic();
4926 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4928 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4929 int inextface = (iface+1+iQuad) % nbNodes;
4930 int imid = (iface+1) % nbNodes;
4931 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4932 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4933 polyedre_nodes.push_back( prevNod[iface] ); // 1
4934 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4936 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4937 polyedre_nodes.push_back( nextNod[iface] ); // 2
4939 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4940 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4942 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4943 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4945 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4946 if ( nbFaceNodes > 2 )
4947 quantities.push_back( nbFaceNodes );
4948 else // degenerated face
4949 polyedre_nodes.resize( prevNbNodes );
4951 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4953 } // try to create a polyherdal prism
4956 newElems.push_back( aNewElem );
4957 myLastCreatedElems.Append(aNewElem);
4958 srcElements.Append( elem );
4961 // set new prev nodes
4962 for ( iNode = 0; iNode < nbNodes; iNode++ )
4963 prevNod[ iNode ] = nextNod[ iNode ];
4968 //=======================================================================
4970 * \brief Create 1D and 2D elements around swept elements
4971 * \param mapNewNodes - source nodes and ones generated from them
4972 * \param newElemsMap - source elements and ones generated from them
4973 * \param elemNewNodesMap - nodes generated from each node of each element
4974 * \param elemSet - all swept elements
4975 * \param nbSteps - number of sweeping steps
4976 * \param srcElements - to append elem for each generated element
4978 //=======================================================================
4980 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4981 TTElemOfElemListMap & newElemsMap,
4982 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4983 TIDSortedElemSet& elemSet,
4985 SMESH_SequenceOfElemPtr& srcElements)
4987 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4988 SMESHDS_Mesh* aMesh = GetMeshDS();
4990 // Find nodes belonging to only one initial element - sweep them into edges.
4992 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4993 for ( ; nList != mapNewNodes.end(); nList++ )
4995 const SMDS_MeshNode* node =
4996 static_cast<const SMDS_MeshNode*>( nList->first );
4997 if ( newElemsMap.count( node ))
4998 continue; // node was extruded into edge
4999 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
5000 int nbInitElems = 0;
5001 const SMDS_MeshElement* el = 0;
5002 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
5003 while ( eIt->more() && nbInitElems < 2 ) {
5004 const SMDS_MeshElement* e = eIt->next();
5005 SMDSAbs_ElementType type = e->GetType();
5006 if ( type == SMDSAbs_Volume ||
5010 if ( type > highType ) {
5017 if ( nbInitElems == 1 ) {
5018 bool NotCreateEdge = el && el->IsMediumNode(node);
5019 if(!NotCreateEdge) {
5020 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5021 list<const SMDS_MeshElement*> newEdges;
5022 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5027 // Make a ceiling for each element ie an equal element of last new nodes.
5028 // Find free links of faces - make edges and sweep them into faces.
5030 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5032 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
5033 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5034 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5036 const SMDS_MeshElement* elem = itElem->first;
5037 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5039 if(itElem->second.size()==0) continue;
5041 const bool isQuadratic = elem->IsQuadratic();
5043 if ( elem->GetType() == SMDSAbs_Edge ) {
5044 // create a ceiling edge
5045 if ( !isQuadratic ) {
5046 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5047 vecNewNodes[ 1 ]->second.back())) {
5048 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5049 vecNewNodes[ 1 ]->second.back()));
5050 srcElements.Append( elem );
5054 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5055 vecNewNodes[ 1 ]->second.back(),
5056 vecNewNodes[ 2 ]->second.back())) {
5057 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5058 vecNewNodes[ 1 ]->second.back(),
5059 vecNewNodes[ 2 ]->second.back()));
5060 srcElements.Append( elem );
5064 if ( elem->GetType() != SMDSAbs_Face )
5067 bool hasFreeLinks = false;
5069 TIDSortedElemSet avoidSet;
5070 avoidSet.insert( elem );
5072 set<const SMDS_MeshNode*> aFaceLastNodes;
5073 int iNode, nbNodes = vecNewNodes.size();
5074 if ( !isQuadratic ) {
5075 // loop on the face nodes
5076 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5077 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5078 // look for free links of the face
5079 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5080 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5081 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5082 // check if a link n1-n2 is free
5083 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5084 hasFreeLinks = true;
5085 // make a new edge and a ceiling for a new edge
5086 const SMDS_MeshElement* edge;
5087 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5088 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5089 srcElements.Append( myLastCreatedElems.Last() );
5091 n1 = vecNewNodes[ iNode ]->second.back();
5092 n2 = vecNewNodes[ iNext ]->second.back();
5093 if ( !aMesh->FindEdge( n1, n2 )) {
5094 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5095 srcElements.Append( edge );
5100 else { // elem is quadratic face
5101 int nbn = nbNodes/2;
5102 for ( iNode = 0; iNode < nbn; iNode++ ) {
5103 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5104 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5105 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5106 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5107 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5108 // check if a link is free
5109 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5110 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5111 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5112 hasFreeLinks = true;
5113 // make an edge and a ceiling for a new edge
5115 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5116 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5117 srcElements.Append( elem );
5119 n1 = vecNewNodes[ iNode ]->second.back();
5120 n2 = vecNewNodes[ iNext ]->second.back();
5121 n3 = vecNewNodes[ iNode+nbn ]->second.back();
5122 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5123 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5124 srcElements.Append( elem );
5128 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5129 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5133 // sweep free links into faces
5135 if ( hasFreeLinks ) {
5136 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5137 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5139 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5140 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5141 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5142 initNodeSet.insert( vecNewNodes[ iNode ]->first );
5143 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5145 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
5146 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5147 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5149 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5150 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5151 std::advance( v, volNb );
5152 // find indices of free faces of a volume and their source edges
5153 list< int > freeInd;
5154 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5155 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5156 int iF, nbF = vTool.NbFaces();
5157 for ( iF = 0; iF < nbF; iF ++ ) {
5158 if (vTool.IsFreeFace( iF ) &&
5159 vTool.GetFaceNodes( iF, faceNodeSet ) &&
5160 initNodeSet != faceNodeSet) // except an initial face
5162 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5164 if ( faceNodeSet == initNodeSetNoCenter )
5166 freeInd.push_back( iF );
5167 // find source edge of a free face iF
5168 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5169 vector<const SMDS_MeshNode*>::iterator lastCommom;
5170 commonNodes.resize( nbNodes, 0 );
5171 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5172 initNodeSet.begin(), initNodeSet.end(),
5173 commonNodes.begin());
5174 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5175 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5177 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5179 if ( !srcEdges.back() )
5181 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5182 << iF << " of volume #" << vTool.ID() << endl;
5187 if ( freeInd.empty() )
5190 // create wall faces for all steps;
5191 // if such a face has been already created by sweep of edge,
5192 // assure that its orientation is OK
5193 for ( int iStep = 0; iStep < nbSteps; iStep++ )
5195 vTool.Set( *v, /*ignoreCentralNodes=*/false );
5196 vTool.SetExternalNormal();
5197 const int nextShift = vTool.IsForward() ? +1 : -1;
5198 list< int >::iterator ind = freeInd.begin();
5199 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5200 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5202 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5203 int nbn = vTool.NbFaceNodes( *ind );
5204 const SMDS_MeshElement * f = 0;
5205 if ( nbn == 3 ) ///// triangle
5207 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5209 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5211 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5213 nodes[ 1 + nextShift ] };
5215 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5217 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5221 else if ( nbn == 4 ) ///// quadrangle
5223 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5225 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5227 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5228 nodes[ 2 ], nodes[ 2+nextShift ] };
5230 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5232 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5233 newOrder[ 2 ], newOrder[ 3 ]));
5236 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5238 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5240 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5242 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5244 nodes[2 + 2*nextShift],
5245 nodes[3 - 2*nextShift],
5247 nodes[3 + 2*nextShift]};
5249 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5251 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5259 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5261 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5262 nodes[1], nodes[3], nodes[5], nodes[7] );
5264 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5266 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5267 nodes[4 - 2*nextShift],
5269 nodes[4 + 2*nextShift],
5271 nodes[5 - 2*nextShift],
5273 nodes[5 + 2*nextShift] };
5275 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5277 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5278 newOrder[ 2 ], newOrder[ 3 ],
5279 newOrder[ 4 ], newOrder[ 5 ],
5280 newOrder[ 6 ], newOrder[ 7 ]));
5283 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5285 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5286 SMDSAbs_Face, /*noMedium=*/false);
5288 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5290 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5291 nodes[4 - 2*nextShift],
5293 nodes[4 + 2*nextShift],
5295 nodes[5 - 2*nextShift],
5297 nodes[5 + 2*nextShift],
5300 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5302 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5303 newOrder[ 2 ], newOrder[ 3 ],
5304 newOrder[ 4 ], newOrder[ 5 ],
5305 newOrder[ 6 ], newOrder[ 7 ],
5309 else //////// polygon
5311 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5312 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5314 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5316 if ( !vTool.IsForward() )
5317 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5319 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5321 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5325 while ( srcElements.Length() < myLastCreatedElems.Length() )
5326 srcElements.Append( *srcEdge );
5328 } // loop on free faces
5330 // go to the next volume
5332 while ( iVol++ < nbVolumesByStep ) v++;
5335 } // loop on volumes of one step
5336 } // sweep free links into faces
5338 // Make a ceiling face with a normal external to a volume
5340 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5341 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5342 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5344 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5345 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5346 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5350 lastVol.SetExternalNormal();
5351 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5352 const int nbn = lastVol.NbFaceNodes( iF );
5353 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5354 if ( !hasFreeLinks ||
5355 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5357 const vector<int>& interlace =
5358 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5359 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5361 AddElement( nodeVec, anyFace.Init( elem ));
5363 while ( srcElements.Length() < myLastCreatedElems.Length() )
5364 srcElements.Append( elem );
5367 } // loop on swept elements
5370 //=======================================================================
5371 //function : RotationSweep
5373 //=======================================================================
5375 SMESH_MeshEditor::PGroupIDs
5376 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5377 const gp_Ax1& theAxis,
5378 const double theAngle,
5379 const int theNbSteps,
5380 const double theTol,
5381 const bool theMakeGroups,
5382 const bool theMakeWalls)
5384 myLastCreatedElems.Clear();
5385 myLastCreatedNodes.Clear();
5387 // source elements for each generated one
5388 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5390 MESSAGE( "RotationSweep()");
5392 aTrsf.SetRotation( theAxis, theAngle );
5394 aTrsf2.SetRotation( theAxis, theAngle/2. );
5396 gp_Lin aLine( theAxis );
5397 double aSqTol = theTol * theTol;
5399 SMESHDS_Mesh* aMesh = GetMeshDS();
5401 TNodeOfNodeListMap mapNewNodes;
5402 TElemOfVecOfNnlmiMap mapElemNewNodes;
5403 TTElemOfElemListMap newElemsMap;
5405 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5406 myMesh->NbFaces(ORDER_QUADRATIC) +
5407 myMesh->NbVolumes(ORDER_QUADRATIC) );
5408 // loop on theElemSets
5409 setElemsFirst( theElemSets );
5410 TIDSortedElemSet::iterator itElem;
5411 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5413 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5414 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5415 const SMDS_MeshElement* elem = *itElem;
5416 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5418 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5419 newNodesItVec.reserve( elem->NbNodes() );
5421 // loop on elem nodes
5422 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5423 while ( itN->more() )
5425 const SMDS_MeshNode* node = cast2Node( itN->next() );
5427 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5429 aXYZ.Coord( coord[0], coord[1], coord[2] );
5430 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5432 // check if a node has been already sweeped
5433 TNodeOfNodeListMapItr nIt =
5434 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5435 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5436 if ( listNewNodes.empty() )
5438 // check if we are to create medium nodes between corner ones
5439 bool needMediumNodes = false;
5440 if ( isQuadraticMesh )
5442 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5443 while (it->more() && !needMediumNodes )
5445 const SMDS_MeshElement* invElem = it->next();
5446 if ( invElem != elem && !theElems.count( invElem )) continue;
5447 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5448 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5449 needMediumNodes = true;
5454 const SMDS_MeshNode * newNode = node;
5455 for ( int i = 0; i < theNbSteps; i++ ) {
5457 if ( needMediumNodes ) // create a medium node
5459 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5460 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5461 myLastCreatedNodes.Append(newNode);
5462 srcNodes.Append( node );
5463 listNewNodes.push_back( newNode );
5464 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5467 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5469 // create a corner node
5470 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5471 myLastCreatedNodes.Append(newNode);
5472 srcNodes.Append( node );
5473 listNewNodes.push_back( newNode );
5476 listNewNodes.push_back( newNode );
5477 // if ( needMediumNodes )
5478 // listNewNodes.push_back( newNode );
5482 newNodesItVec.push_back( nIt );
5484 // make new elements
5485 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5490 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5492 PGroupIDs newGroupIDs;
5493 if ( theMakeGroups )
5494 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5499 //=======================================================================
5500 //function : ExtrusParam
5501 //purpose : standard construction
5502 //=======================================================================
5504 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5505 const int theNbSteps,
5507 const double theTolerance):
5509 myFlags( theFlags ),
5510 myTolerance( theTolerance ),
5511 myElemsToUse( NULL )
5513 mySteps = new TColStd_HSequenceOfReal;
5514 const double stepSize = theStep.Magnitude();
5515 for (int i=1; i<=theNbSteps; i++ )
5516 mySteps->Append( stepSize );
5518 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5519 ( theTolerance > 0 ))
5521 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5525 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5529 //=======================================================================
5530 //function : ExtrusParam
5531 //purpose : steps are given explicitly
5532 //=======================================================================
5534 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5535 Handle(TColStd_HSequenceOfReal) theSteps,
5537 const double theTolerance):
5539 mySteps( theSteps ),
5540 myFlags( theFlags ),
5541 myTolerance( theTolerance ),
5542 myElemsToUse( NULL )
5544 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5545 ( theTolerance > 0 ))
5547 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5551 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5555 //=======================================================================
5556 //function : ExtrusParam
5557 //purpose : for extrusion by normal
5558 //=======================================================================
5560 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5561 const int theNbSteps,
5565 mySteps( new TColStd_HSequenceOfReal ),
5566 myFlags( theFlags ),
5568 myElemsToUse( NULL )
5570 for (int i = 0; i < theNbSteps; i++ )
5571 mySteps->Append( theStepSize );
5575 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5579 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5583 //=======================================================================
5584 //function : ExtrusParam::SetElementsToUse
5585 //purpose : stores elements to use for extrusion by normal, depending on
5586 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5587 //=======================================================================
5589 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5591 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5594 //=======================================================================
5595 //function : ExtrusParam::beginStepIter
5596 //purpose : prepare iteration on steps
5597 //=======================================================================
5599 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5601 myWithMediumNodes = withMediumNodes;
5605 //=======================================================================
5606 //function : ExtrusParam::moreSteps
5607 //purpose : are there more steps?
5608 //=======================================================================
5610 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5612 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5614 //=======================================================================
5615 //function : ExtrusParam::nextStep
5616 //purpose : returns the next step
5617 //=======================================================================
5619 double SMESH_MeshEditor::ExtrusParam::nextStep()
5622 if ( !myCurSteps.empty() )
5624 res = myCurSteps.back();
5625 myCurSteps.pop_back();
5627 else if ( myNextStep <= mySteps->Length() )
5629 myCurSteps.push_back( mySteps->Value( myNextStep ));
5631 if ( myWithMediumNodes )
5633 myCurSteps.back() /= 2.;
5634 myCurSteps.push_back( myCurSteps.back() );
5641 //=======================================================================
5642 //function : ExtrusParam::makeNodesByDir
5643 //purpose : create nodes for standard extrusion
5644 //=======================================================================
5646 int SMESH_MeshEditor::ExtrusParam::
5647 makeNodesByDir( SMESHDS_Mesh* mesh,
5648 const SMDS_MeshNode* srcNode,
5649 std::list<const SMDS_MeshNode*> & newNodes,
5650 const bool makeMediumNodes)
5652 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5655 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5657 p += myDir.XYZ() * nextStep();
5658 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5659 newNodes.push_back( newNode );
5664 //=======================================================================
5665 //function : ExtrusParam::makeNodesByDirAndSew
5666 //purpose : create nodes for standard extrusion with sewing
5667 //=======================================================================
5669 int SMESH_MeshEditor::ExtrusParam::
5670 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5671 const SMDS_MeshNode* srcNode,
5672 std::list<const SMDS_MeshNode*> & newNodes,
5673 const bool makeMediumNodes)
5675 gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5678 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5680 P1 += myDir.XYZ() * nextStep();
5682 // try to search in sequence of existing nodes
5683 // if myNodes.Length()>0 we 'nave to use given sequence
5684 // else - use all nodes of mesh
5685 const SMDS_MeshNode * node = 0;
5686 if ( myNodes.Length() > 0 ) {
5688 for(i=1; i<=myNodes.Length(); i++) {
5689 gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5690 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5692 node = myNodes.Value(i);
5698 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5699 while(itn->more()) {
5700 SMESH_TNodeXYZ P2( itn->next() );
5701 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5710 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5712 newNodes.push_back( node );
5719 //=======================================================================
5720 //function : ExtrusParam::makeNodesByNormal2D
5721 //purpose : create nodes for extrusion using normals of faces
5722 //=======================================================================
5724 int SMESH_MeshEditor::ExtrusParam::
5725 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5726 const SMDS_MeshNode* srcNode,
5727 std::list<const SMDS_MeshNode*> & newNodes,
5728 const bool makeMediumNodes)
5730 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5732 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5734 // get normals to faces sharing srcNode
5735 vector< gp_XYZ > norms, baryCenters;
5736 gp_XYZ norm, avgNorm( 0,0,0 );
5737 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5738 while ( faceIt->more() )
5740 const SMDS_MeshElement* face = faceIt->next();
5741 if ( myElemsToUse && !myElemsToUse->count( face ))
5743 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5745 norms.push_back( norm );
5747 if ( !alongAvgNorm )
5751 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5752 bc += SMESH_TNodeXYZ( nIt->next() );
5753 baryCenters.push_back( bc / nbN );
5758 if ( norms.empty() ) return 0;
5760 double normSize = avgNorm.Modulus();
5761 if ( normSize < std::numeric_limits<double>::min() )
5764 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5767 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5770 avgNorm /= normSize;
5773 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5776 double stepSize = nextStep();
5778 if ( norms.size() > 1 )
5780 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5782 // translate plane of a face
5783 baryCenters[ iF ] += norms[ iF ] * stepSize;
5785 // find point of intersection of the face plane located at baryCenters[ iF ]
5786 // and avgNorm located at pNew
5787 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5788 double dot = ( norms[ iF ] * avgNorm );
5789 if ( dot < std::numeric_limits<double>::min() )
5790 dot = stepSize * 1e-3;
5791 double step = -( norms[ iF ] * pNew + d ) / dot;
5792 pNew += step * avgNorm;
5797 pNew += stepSize * avgNorm;
5801 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5802 newNodes.push_back( newNode );
5807 //=======================================================================
5808 //function : ExtrusParam::makeNodesByNormal1D
5809 //purpose : create nodes for extrusion using normals of edges
5810 //=======================================================================
5812 int SMESH_MeshEditor::ExtrusParam::
5813 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5814 const SMDS_MeshNode* srcNode,
5815 std::list<const SMDS_MeshNode*> & newNodes,
5816 const bool makeMediumNodes)
5818 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5822 //=======================================================================
5823 //function : ExtrusionSweep
5825 //=======================================================================
5827 SMESH_MeshEditor::PGroupIDs
5828 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5829 const gp_Vec& theStep,
5830 const int theNbSteps,
5831 TTElemOfElemListMap& newElemsMap,
5833 const double theTolerance)
5835 ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5836 return ExtrusionSweep( theElems, aParams, newElemsMap );
5840 //=======================================================================
5841 //function : ExtrusionSweep
5843 //=======================================================================
5845 SMESH_MeshEditor::PGroupIDs
5846 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5847 ExtrusParam& theParams,
5848 TTElemOfElemListMap& newElemsMap)
5850 myLastCreatedElems.Clear();
5851 myLastCreatedNodes.Clear();
5853 // source elements for each generated one
5854 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5856 SMESHDS_Mesh* aMesh = GetMeshDS();
5858 setElemsFirst( theElemSets );
5859 const int nbSteps = theParams.NbSteps();
5860 theParams.SetElementsToUse( theElemSets[0] );
5862 TNodeOfNodeListMap mapNewNodes;
5863 //TNodeOfNodeVecMap mapNewNodes;
5864 TElemOfVecOfNnlmiMap mapElemNewNodes;
5865 //TElemOfVecOfMapNodesMap mapElemNewNodes;
5867 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5868 myMesh->NbFaces(ORDER_QUADRATIC) +
5869 myMesh->NbVolumes(ORDER_QUADRATIC) );
5871 TIDSortedElemSet::iterator itElem;
5872 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5874 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5875 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5877 // check element type
5878 const SMDS_MeshElement* elem = *itElem;
5879 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5882 const size_t nbNodes = elem->NbNodes();
5883 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5884 newNodesItVec.reserve( nbNodes );
5886 // loop on elem nodes
5887 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5888 while ( itN->more() )
5890 // check if a node has been already sweeped
5891 const SMDS_MeshNode* node = cast2Node( itN->next() );
5892 TNodeOfNodeListMap::iterator nIt =
5893 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5894 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5895 if ( listNewNodes.empty() )
5899 // check if we are to create medium nodes between corner ones
5900 bool needMediumNodes = false;
5901 if ( isQuadraticMesh )
5903 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5904 while (it->more() && !needMediumNodes )
5906 const SMDS_MeshElement* invElem = it->next();
5907 if ( invElem != elem && !theElems.count( invElem )) continue;
5908 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5909 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5910 needMediumNodes = true;
5913 // create nodes for all steps
5914 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5916 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5917 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5919 myLastCreatedNodes.Append( *newNodesIt );
5920 srcNodes.Append( node );
5925 break; // newNodesItVec will be shorter than nbNodes
5928 newNodesItVec.push_back( nIt );
5930 // make new elements
5931 if ( newNodesItVec.size() == nbNodes )
5932 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5936 if ( theParams.ToMakeBoundary() ) {
5937 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5939 PGroupIDs newGroupIDs;
5940 if ( theParams.ToMakeGroups() )
5941 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5946 //=======================================================================
5947 //function : ExtrusionAlongTrack
5949 //=======================================================================
5950 SMESH_MeshEditor::Extrusion_Error
5951 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5952 SMESH_subMesh* theTrack,
5953 const SMDS_MeshNode* theN1,
5954 const bool theHasAngles,
5955 list<double>& theAngles,
5956 const bool theLinearVariation,
5957 const bool theHasRefPoint,
5958 const gp_Pnt& theRefPoint,
5959 const bool theMakeGroups)
5961 MESSAGE("ExtrusionAlongTrack");
5962 myLastCreatedElems.Clear();
5963 myLastCreatedNodes.Clear();
5966 std::list<double> aPrms;
5967 TIDSortedElemSet::iterator itElem;
5970 TopoDS_Edge aTrackEdge;
5971 TopoDS_Vertex aV1, aV2;
5973 SMDS_ElemIteratorPtr aItE;
5974 SMDS_NodeIteratorPtr aItN;
5975 SMDSAbs_ElementType aTypeE;
5977 TNodeOfNodeListMap mapNewNodes;
5980 aNbE = theElements[0].size() + theElements[1].size();
5983 return EXTR_NO_ELEMENTS;
5985 // 1.1 Track Pattern
5988 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5990 aItE = pSubMeshDS->GetElements();
5991 while ( aItE->more() ) {
5992 const SMDS_MeshElement* pE = aItE->next();
5993 aTypeE = pE->GetType();
5994 // Pattern must contain links only
5995 if ( aTypeE != SMDSAbs_Edge )
5996 return EXTR_PATH_NOT_EDGE;
5999 list<SMESH_MeshEditor_PathPoint> fullList;
6001 const TopoDS_Shape& aS = theTrack->GetSubShape();
6002 // Sub-shape for the Pattern must be an Edge or Wire
6003 if( aS.ShapeType() == TopAbs_EDGE ) {
6004 aTrackEdge = TopoDS::Edge( aS );
6005 // the Edge must not be degenerated
6006 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6007 return EXTR_BAD_PATH_SHAPE;
6008 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6009 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
6010 const SMDS_MeshNode* aN1 = aItN->next();
6011 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
6012 const SMDS_MeshNode* aN2 = aItN->next();
6013 // starting node must be aN1 or aN2
6014 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6015 return EXTR_BAD_STARTING_NODE;
6016 aItN = pSubMeshDS->GetNodes();
6017 while ( aItN->more() ) {
6018 const SMDS_MeshNode* pNode = aItN->next();
6019 const SMDS_EdgePosition* pEPos =
6020 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6021 double aT = pEPos->GetUParameter();
6022 aPrms.push_back( aT );
6024 //Extrusion_Error err =
6025 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6026 } else if( aS.ShapeType() == TopAbs_WIRE ) {
6027 list< SMESH_subMesh* > LSM;
6028 TopTools_SequenceOfShape Edges;
6029 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6030 while(itSM->more()) {
6031 SMESH_subMesh* SM = itSM->next();
6033 const TopoDS_Shape& aS = SM->GetSubShape();
6036 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6037 int startNid = theN1->GetID();
6038 TColStd_MapOfInteger UsedNums;
6040 int NbEdges = Edges.Length();
6042 for(; i<=NbEdges; i++) {
6044 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6045 for(; itLSM!=LSM.end(); itLSM++) {
6047 if(UsedNums.Contains(k)) continue;
6048 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6049 SMESH_subMesh* locTrack = *itLSM;
6050 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6051 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6052 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6053 const SMDS_MeshNode* aN1 = aItN->next();
6054 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6055 const SMDS_MeshNode* aN2 = aItN->next();
6056 // starting node must be aN1 or aN2
6057 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6058 // 2. Collect parameters on the track edge
6060 aItN = locMeshDS->GetNodes();
6061 while ( aItN->more() ) {
6062 const SMDS_MeshNode* pNode = aItN->next();
6063 const SMDS_EdgePosition* pEPos =
6064 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6065 double aT = pEPos->GetUParameter();
6066 aPrms.push_back( aT );
6068 list<SMESH_MeshEditor_PathPoint> LPP;
6069 //Extrusion_Error err =
6070 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6071 LLPPs.push_back(LPP);
6073 // update startN for search following egde
6074 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6075 else startNid = aN1->GetID();
6079 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6080 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6081 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6082 for(; itPP!=firstList.end(); itPP++) {
6083 fullList.push_back( *itPP );
6085 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6086 fullList.pop_back();
6088 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6089 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6090 itPP = currList.begin();
6091 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6092 gp_Dir D1 = PP1.Tangent();
6093 gp_Dir D2 = PP2.Tangent();
6094 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6095 (D1.Z()+D2.Z())/2 ) );
6096 PP1.SetTangent(Dnew);
6097 fullList.push_back(PP1);
6099 for(; itPP!=firstList.end(); itPP++) {
6100 fullList.push_back( *itPP );
6102 PP1 = fullList.back();
6103 fullList.pop_back();
6105 // if wire not closed
6106 fullList.push_back(PP1);
6110 return EXTR_BAD_PATH_SHAPE;
6113 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6114 theHasRefPoint, theRefPoint, theMakeGroups);
6118 //=======================================================================
6119 //function : ExtrusionAlongTrack
6121 //=======================================================================
6122 SMESH_MeshEditor::Extrusion_Error
6123 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6124 SMESH_Mesh* theTrack,
6125 const SMDS_MeshNode* theN1,
6126 const bool theHasAngles,
6127 list<double>& theAngles,
6128 const bool theLinearVariation,
6129 const bool theHasRefPoint,
6130 const gp_Pnt& theRefPoint,
6131 const bool theMakeGroups)
6133 myLastCreatedElems.Clear();
6134 myLastCreatedNodes.Clear();
6137 std::list<double> aPrms;
6138 TIDSortedElemSet::iterator itElem;
6141 TopoDS_Edge aTrackEdge;
6142 TopoDS_Vertex aV1, aV2;
6144 SMDS_ElemIteratorPtr aItE;
6145 SMDS_NodeIteratorPtr aItN;
6146 SMDSAbs_ElementType aTypeE;
6148 TNodeOfNodeListMap mapNewNodes;
6151 aNbE = theElements[0].size() + theElements[1].size();
6154 return EXTR_NO_ELEMENTS;
6156 // 1.1 Track Pattern
6159 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6161 aItE = pMeshDS->elementsIterator();
6162 while ( aItE->more() ) {
6163 const SMDS_MeshElement* pE = aItE->next();
6164 aTypeE = pE->GetType();
6165 // Pattern must contain links only
6166 if ( aTypeE != SMDSAbs_Edge )
6167 return EXTR_PATH_NOT_EDGE;
6170 list<SMESH_MeshEditor_PathPoint> fullList;
6172 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6174 if ( !theTrack->HasShapeToMesh() ) {
6175 //Mesh without shape
6176 const SMDS_MeshNode* currentNode = NULL;
6177 const SMDS_MeshNode* prevNode = theN1;
6178 std::vector<const SMDS_MeshNode*> aNodesList;
6179 aNodesList.push_back(theN1);
6180 int nbEdges = 0, conn=0;
6181 const SMDS_MeshElement* prevElem = NULL;
6182 const SMDS_MeshElement* currentElem = NULL;
6183 int totalNbEdges = theTrack->NbEdges();
6184 SMDS_ElemIteratorPtr nIt;
6187 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6188 return EXTR_BAD_STARTING_NODE;
6191 conn = nbEdgeConnectivity(theN1);
6193 return EXTR_PATH_NOT_EDGE;
6195 aItE = theN1->GetInverseElementIterator();
6196 prevElem = aItE->next();
6197 currentElem = prevElem;
6199 if(totalNbEdges == 1 ) {
6200 nIt = currentElem->nodesIterator();
6201 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6202 if(currentNode == prevNode)
6203 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6204 aNodesList.push_back(currentNode);
6206 nIt = currentElem->nodesIterator();
6207 while( nIt->more() ) {
6208 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6209 if(currentNode == prevNode)
6210 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6211 aNodesList.push_back(currentNode);
6213 //case of the closed mesh
6214 if(currentNode == theN1) {
6219 conn = nbEdgeConnectivity(currentNode);
6221 return EXTR_PATH_NOT_EDGE;
6222 }else if( conn == 1 && nbEdges > 0 ) {
6227 prevNode = currentNode;
6228 aItE = currentNode->GetInverseElementIterator();
6229 currentElem = aItE->next();
6230 if( currentElem == prevElem)
6231 currentElem = aItE->next();
6232 nIt = currentElem->nodesIterator();
6233 prevElem = currentElem;
6239 if(nbEdges != totalNbEdges)
6240 return EXTR_PATH_NOT_EDGE;
6242 TopTools_SequenceOfShape Edges;
6243 double x1,x2,y1,y2,z1,z2;
6244 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6245 int startNid = theN1->GetID();
6246 for(int i = 1; i < aNodesList.size(); i++) {
6247 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
6248 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
6249 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
6250 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
6251 list<SMESH_MeshEditor_PathPoint> LPP;
6253 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6254 LLPPs.push_back(LPP);
6255 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
6256 else startNid = aNodesList[i-1]->GetID();
6260 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6261 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6262 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6263 for(; itPP!=firstList.end(); itPP++) {
6264 fullList.push_back( *itPP );
6267 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6268 SMESH_MeshEditor_PathPoint PP2;
6269 fullList.pop_back();
6271 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6272 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6273 itPP = currList.begin();
6274 PP2 = currList.front();
6275 gp_Dir D1 = PP1.Tangent();
6276 gp_Dir D2 = PP2.Tangent();
6277 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6278 (D1.Z()+D2.Z())/2 ) );
6279 PP1.SetTangent(Dnew);
6280 fullList.push_back(PP1);
6282 for(; itPP!=currList.end(); itPP++) {
6283 fullList.push_back( *itPP );
6285 PP1 = fullList.back();
6286 fullList.pop_back();
6288 fullList.push_back(PP1);
6290 } // Sub-shape for the Pattern must be an Edge or Wire
6291 else if( aS.ShapeType() == TopAbs_EDGE ) {
6292 aTrackEdge = TopoDS::Edge( aS );
6293 // the Edge must not be degenerated
6294 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6295 return EXTR_BAD_PATH_SHAPE;
6296 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6297 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6298 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6299 // starting node must be aN1 or aN2
6300 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6301 return EXTR_BAD_STARTING_NODE;
6302 aItN = pMeshDS->nodesIterator();
6303 while ( aItN->more() ) {
6304 const SMDS_MeshNode* pNode = aItN->next();
6305 if( pNode==aN1 || pNode==aN2 ) continue;
6306 const SMDS_EdgePosition* pEPos =
6307 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6308 double aT = pEPos->GetUParameter();
6309 aPrms.push_back( aT );
6311 //Extrusion_Error err =
6312 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6314 else if( aS.ShapeType() == TopAbs_WIRE ) {
6315 list< SMESH_subMesh* > LSM;
6316 TopTools_SequenceOfShape Edges;
6317 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6318 for(; eExp.More(); eExp.Next()) {
6319 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6320 if( SMESH_Algo::isDegenerated(E) ) continue;
6321 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6327 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6328 TopoDS_Vertex aVprev;
6329 TColStd_MapOfInteger UsedNums;
6330 int NbEdges = Edges.Length();
6332 for(; i<=NbEdges; i++) {
6334 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6335 for(; itLSM!=LSM.end(); itLSM++) {
6337 if(UsedNums.Contains(k)) continue;
6338 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6339 SMESH_subMesh* locTrack = *itLSM;
6340 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6341 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6342 bool aN1isOK = false, aN2isOK = false;
6343 if ( aVprev.IsNull() ) {
6344 // if previous vertex is not yet defined, it means that we in the beginning of wire
6345 // and we have to find initial vertex corresponding to starting node theN1
6346 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6347 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6348 // starting node must be aN1 or aN2
6349 aN1isOK = ( aN1 && aN1 == theN1 );
6350 aN2isOK = ( aN2 && aN2 == theN1 );
6353 // we have specified ending vertex of the previous edge on the previous iteration
6354 // and we have just to check that it corresponds to any vertex in current segment
6355 aN1isOK = aVprev.IsSame( aV1 );
6356 aN2isOK = aVprev.IsSame( aV2 );
6358 if ( !aN1isOK && !aN2isOK ) continue;
6359 // 2. Collect parameters on the track edge
6361 aItN = locMeshDS->GetNodes();
6362 while ( aItN->more() ) {
6363 const SMDS_MeshNode* pNode = aItN->next();
6364 const SMDS_EdgePosition* pEPos =
6365 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6366 double aT = pEPos->GetUParameter();
6367 aPrms.push_back( aT );
6369 list<SMESH_MeshEditor_PathPoint> LPP;
6370 //Extrusion_Error err =
6371 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6372 LLPPs.push_back(LPP);
6374 // update startN for search following egde
6375 if ( aN1isOK ) aVprev = aV2;
6380 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6381 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6382 fullList.splice( fullList.end(), firstList );
6384 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6385 fullList.pop_back();
6387 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6388 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6389 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6390 gp_Dir D1 = PP1.Tangent();
6391 gp_Dir D2 = PP2.Tangent();
6392 gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6393 PP1.SetTangent(Dnew);
6394 fullList.push_back(PP1);
6395 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6396 PP1 = fullList.back();
6397 fullList.pop_back();
6399 // if wire not closed
6400 fullList.push_back(PP1);
6404 return EXTR_BAD_PATH_SHAPE;
6407 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6408 theHasRefPoint, theRefPoint, theMakeGroups);
6412 //=======================================================================
6413 //function : MakeEdgePathPoints
6414 //purpose : auxilary for ExtrusionAlongTrack
6415 //=======================================================================
6416 SMESH_MeshEditor::Extrusion_Error
6417 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
6418 const TopoDS_Edge& aTrackEdge,
6420 list<SMESH_MeshEditor_PathPoint>& LPP)
6422 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6424 aTolVec2=aTolVec*aTolVec;
6426 TopoDS_Vertex aV1, aV2;
6427 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6428 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6429 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6430 // 2. Collect parameters on the track edge
6431 aPrms.push_front( aT1 );
6432 aPrms.push_back( aT2 );
6435 if( FirstIsStart ) {
6446 SMESH_MeshEditor_PathPoint aPP;
6447 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6448 std::list<double>::iterator aItD = aPrms.begin();
6449 for(; aItD != aPrms.end(); ++aItD) {
6453 aC3D->D1( aT, aP3D, aVec );
6454 aL2 = aVec.SquareMagnitude();
6455 if ( aL2 < aTolVec2 )
6456 return EXTR_CANT_GET_TANGENT;
6457 gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6459 aPP.SetTangent( aTgt );
6460 aPP.SetParameter( aT );
6467 //=======================================================================
6468 //function : MakeExtrElements
6469 //purpose : auxilary for ExtrusionAlongTrack
6470 //=======================================================================
6471 SMESH_MeshEditor::Extrusion_Error
6472 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet theElemSets[2],
6473 list<SMESH_MeshEditor_PathPoint>& fullList,
6474 const bool theHasAngles,
6475 list<double>& theAngles,
6476 const bool theLinearVariation,
6477 const bool theHasRefPoint,
6478 const gp_Pnt& theRefPoint,
6479 const bool theMakeGroups)
6481 const int aNbTP = fullList.size();
6484 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6485 LinearAngleVariation(aNbTP-1, theAngles);
6487 // fill vector of path points with angles
6488 vector<SMESH_MeshEditor_PathPoint> aPPs;
6489 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6490 list<double>::iterator itAngles = theAngles.begin();
6491 aPPs.push_back( *itPP++ );
6492 for( ; itPP != fullList.end(); itPP++) {
6493 aPPs.push_back( *itPP );
6494 if ( theHasAngles && itAngles != theAngles.end() )
6495 aPPs.back().SetAngle( *itAngles++ );
6498 TNodeOfNodeListMap mapNewNodes;
6499 TElemOfVecOfNnlmiMap mapElemNewNodes;
6500 TTElemOfElemListMap newElemsMap;
6501 TIDSortedElemSet::iterator itElem;
6502 // source elements for each generated one
6503 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6505 // 3. Center of rotation aV0
6506 gp_Pnt aV0 = theRefPoint;
6507 if ( !theHasRefPoint )
6509 gp_XYZ aGC( 0.,0.,0. );
6510 TIDSortedElemSet newNodes;
6512 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6514 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6515 itElem = theElements.begin();
6516 for ( ; itElem != theElements.end(); itElem++ )
6518 const SMDS_MeshElement* elem = *itElem;
6519 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6520 while ( itN->more() ) {
6521 const SMDS_MeshElement* node = itN->next();
6522 if ( newNodes.insert( node ).second )
6523 aGC += SMESH_TNodeXYZ( node );
6527 aGC /= newNodes.size();
6529 } // if (!theHasRefPoint) {
6531 // 4. Processing the elements
6532 SMESHDS_Mesh* aMesh = GetMeshDS();
6533 list<const SMDS_MeshNode*> emptyList;
6535 setElemsFirst( theElemSets );
6536 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6538 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6539 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6541 const SMDS_MeshElement* elem = *itElem;
6543 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6544 newNodesItVec.reserve( elem->NbNodes() );
6546 // loop on elem nodes
6548 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6549 while ( itN->more() )
6552 // check if a node has been already processed
6553 const SMDS_MeshNode* node = cast2Node( itN->next() );
6554 TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6555 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6556 if ( listNewNodes.empty() )
6559 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6560 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6561 gp_Ax1 anAx1, anAxT1T0;
6562 gp_Dir aDT1x, aDT0x, aDT1T0;
6567 aPN0 = SMESH_TNodeXYZ( node );
6569 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6571 aDT0x= aPP0.Tangent();
6573 for ( int j = 1; j < aNbTP; ++j ) {
6574 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6576 aDT1x = aPP1.Tangent();
6577 aAngle1x = aPP1.Angle();
6579 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6581 gp_Vec aV01x( aP0x, aP1x );
6582 aTrsf.SetTranslation( aV01x );
6585 aV1x = aV0x.Transformed( aTrsf );
6586 aPN1 = aPN0.Transformed( aTrsf );
6588 // rotation 1 [ T1,T0 ]
6589 aAngleT1T0=-aDT1x.Angle( aDT0x );
6590 if (fabs(aAngleT1T0) > aTolAng)
6593 anAxT1T0.SetLocation( aV1x );
6594 anAxT1T0.SetDirection( aDT1T0 );
6595 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6597 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6601 if ( theHasAngles ) {
6602 anAx1.SetLocation( aV1x );
6603 anAx1.SetDirection( aDT1x );
6604 aTrsfRot.SetRotation( anAx1, aAngle1x );
6606 aPN1 = aPN1.Transformed( aTrsfRot );
6610 if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6612 // create additional node
6613 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6614 const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6615 myLastCreatedNodes.Append(newNode);
6616 srcNodes.Append( node );
6617 listNewNodes.push_back( newNode );
6619 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6620 myLastCreatedNodes.Append(newNode);
6621 srcNodes.Append( node );
6622 listNewNodes.push_back( newNode );
6630 else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6632 // if current elem is quadratic and current node is not medium
6633 // we have to check - may be it is needed to insert additional nodes
6634 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6635 if ( listNewNodes.size() == aNbTP-1 )
6637 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6638 gp_XYZ P(node->X(), node->Y(), node->Z());
6639 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6641 for(i=0; i<aNbTP-1; i++) {
6642 const SMDS_MeshNode* N = *it;
6643 double x = ( N->X() + P.X() )/2.;
6644 double y = ( N->Y() + P.Y() )/2.;
6645 double z = ( N->Z() + P.Z() )/2.;
6646 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6647 srcNodes.Append( node );
6648 myLastCreatedNodes.Append(newN);
6651 P = gp_XYZ(N->X(),N->Y(),N->Z());
6653 listNewNodes.clear();
6654 for(i=0; i<2*(aNbTP-1); i++) {
6655 listNewNodes.push_back(aNodes[i]);
6660 newNodesItVec.push_back( nIt );
6663 // make new elements
6664 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6668 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6670 if ( theMakeGroups )
6671 generateGroups( srcNodes, srcElems, "extruded");
6677 //=======================================================================
6678 //function : LinearAngleVariation
6679 //purpose : auxilary for ExtrusionAlongTrack
6680 //=======================================================================
6681 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6682 list<double>& Angles)
6684 int nbAngles = Angles.size();
6685 if( nbSteps > nbAngles ) {
6686 vector<double> theAngles(nbAngles);
6687 list<double>::iterator it = Angles.begin();
6689 for(; it!=Angles.end(); it++) {
6691 theAngles[i] = (*it);
6694 double rAn2St = double( nbAngles ) / double( nbSteps );
6695 double angPrev = 0, angle;
6696 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6697 double angCur = rAn2St * ( iSt+1 );
6698 double angCurFloor = floor( angCur );
6699 double angPrevFloor = floor( angPrev );
6700 if ( angPrevFloor == angCurFloor )
6701 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6703 int iP = int( angPrevFloor );
6704 double angPrevCeil = ceil(angPrev);
6705 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6707 int iC = int( angCurFloor );
6708 if ( iC < nbAngles )
6709 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6711 iP = int( angPrevCeil );
6713 angle += theAngles[ iC ];
6715 res.push_back(angle);
6720 for(; it!=res.end(); it++)
6721 Angles.push_back( *it );
6726 //================================================================================
6728 * \brief Move or copy theElements applying theTrsf to their nodes
6729 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6730 * \param theTrsf - transformation to apply
6731 * \param theCopy - if true, create translated copies of theElems
6732 * \param theMakeGroups - if true and theCopy, create translated groups
6733 * \param theTargetMesh - mesh to copy translated elements into
6734 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6736 //================================================================================
6738 SMESH_MeshEditor::PGroupIDs
6739 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6740 const gp_Trsf& theTrsf,
6742 const bool theMakeGroups,
6743 SMESH_Mesh* theTargetMesh)
6745 myLastCreatedElems.Clear();
6746 myLastCreatedNodes.Clear();
6748 bool needReverse = false;
6749 string groupPostfix;
6750 switch ( theTrsf.Form() ) {
6752 MESSAGE("gp_PntMirror");
6754 groupPostfix = "mirrored";
6757 MESSAGE("gp_Ax1Mirror");
6758 groupPostfix = "mirrored";
6761 MESSAGE("gp_Ax2Mirror");
6763 groupPostfix = "mirrored";
6766 MESSAGE("gp_Rotation");
6767 groupPostfix = "rotated";
6769 case gp_Translation:
6770 MESSAGE("gp_Translation");
6771 groupPostfix = "translated";
6774 MESSAGE("gp_Scale");
6775 groupPostfix = "scaled";
6777 case gp_CompoundTrsf: // different scale by axis
6778 MESSAGE("gp_CompoundTrsf");
6779 groupPostfix = "scaled";
6783 needReverse = false;
6784 groupPostfix = "transformed";
6787 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6788 SMESHDS_Mesh* aMesh = GetMeshDS();
6790 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6791 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6792 SMESH_MeshEditor::ElemFeatures elemType;
6794 // map old node to new one
6795 TNodeNodeMap nodeMap;
6797 // elements sharing moved nodes; those of them which have all
6798 // nodes mirrored but are not in theElems are to be reversed
6799 TIDSortedElemSet inverseElemSet;
6801 // source elements for each generated one
6802 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6804 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6805 TIDSortedElemSet orphanNode;
6807 if ( theElems.empty() ) // transform the whole mesh
6810 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6811 while ( eIt->more() ) theElems.insert( eIt->next() );
6813 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6814 while ( nIt->more() )
6816 const SMDS_MeshNode* node = nIt->next();
6817 if ( node->NbInverseElements() == 0)
6818 orphanNode.insert( node );
6822 // loop on elements to transform nodes : first orphan nodes then elems
6823 TIDSortedElemSet::iterator itElem;
6824 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6825 for (int i=0; i<2; i++)
6826 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6828 const SMDS_MeshElement* elem = *itElem;
6832 // loop on elem nodes
6834 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6835 while ( itN->more() )
6837 const SMDS_MeshNode* node = cast2Node( itN->next() );
6838 // check if a node has been already transformed
6839 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6840 nodeMap.insert( make_pair ( node, node ));
6841 if ( !n2n_isnew.second )
6844 node->GetXYZ( coord );
6845 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6846 if ( theTargetMesh ) {
6847 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6848 n2n_isnew.first->second = newNode;
6849 myLastCreatedNodes.Append(newNode);
6850 srcNodes.Append( node );
6852 else if ( theCopy ) {
6853 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6854 n2n_isnew.first->second = newNode;
6855 myLastCreatedNodes.Append(newNode);
6856 srcNodes.Append( node );
6859 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6860 // node position on shape becomes invalid
6861 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6862 ( SMDS_SpacePosition::originSpacePosition() );
6865 // keep inverse elements
6866 if ( !theCopy && !theTargetMesh && needReverse ) {
6867 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6868 while ( invElemIt->more() ) {
6869 const SMDS_MeshElement* iel = invElemIt->next();
6870 inverseElemSet.insert( iel );
6874 } // loop on elems in { &orphanNode, &theElems };
6876 // either create new elements or reverse mirrored ones
6877 if ( !theCopy && !needReverse && !theTargetMesh )
6880 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6882 // Replicate or reverse elements
6884 std::vector<int> iForw;
6885 vector<const SMDS_MeshNode*> nodes;
6886 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6888 const SMDS_MeshElement* elem = *itElem;
6889 if ( !elem ) continue;
6891 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6892 int nbNodes = elem->NbNodes();
6893 if ( geomType == SMDSGeom_NONE ) continue; // node
6895 nodes.resize( nbNodes );
6897 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6899 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6903 bool allTransformed = true;
6904 int nbFaces = aPolyedre->NbFaces();
6905 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6907 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6908 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6910 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6911 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6912 if ( nodeMapIt == nodeMap.end() )
6913 allTransformed = false; // not all nodes transformed
6915 nodes.push_back((*nodeMapIt).second);
6917 if ( needReverse && allTransformed )
6918 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6920 if ( !allTransformed )
6921 continue; // not all nodes transformed
6923 else // ----------------------- the rest element types
6925 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6926 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6927 const vector<int>& i = needReverse ? iRev : iForw;
6929 // find transformed nodes
6931 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6932 while ( itN->more() ) {
6933 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6934 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6935 if ( nodeMapIt == nodeMap.end() )
6936 break; // not all nodes transformed
6937 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6939 if ( iNode != nbNodes )
6940 continue; // not all nodes transformed
6944 // copy in this or a new mesh
6945 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6946 srcElems.Append( elem );
6949 // reverse element as it was reversed by transformation
6951 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6954 } // loop on elements
6956 if ( editor && editor != this )
6957 myLastCreatedElems = editor->myLastCreatedElems;
6959 PGroupIDs newGroupIDs;
6961 if ( ( theMakeGroups && theCopy ) ||
6962 ( theMakeGroups && theTargetMesh ) )
6963 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6968 //=======================================================================
6970 * \brief Create groups of elements made during transformation
6971 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6972 * \param elemGens - elements making corresponding myLastCreatedElems
6973 * \param postfix - to append to names of new groups
6974 * \param targetMesh - mesh to create groups in
6975 * \param topPresent - is there "top" elements that are created by sweeping
6977 //=======================================================================
6979 SMESH_MeshEditor::PGroupIDs
6980 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6981 const SMESH_SequenceOfElemPtr& elemGens,
6982 const std::string& postfix,
6983 SMESH_Mesh* targetMesh,
6984 const bool topPresent)
6986 PGroupIDs newGroupIDs( new list<int> );
6987 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6989 // Sort existing groups by types and collect their names
6991 // containers to store an old group and generated new ones;
6992 // 1st new group is for result elems of different type than a source one;
6993 // 2nd new group is for same type result elems ("top" group at extrusion)
6995 using boost::make_tuple;
6996 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6997 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6998 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7000 set< string > groupNames;
7002 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7003 if ( !groupIt->more() ) return newGroupIDs;
7005 int newGroupID = mesh->GetGroupIds().back()+1;
7006 while ( groupIt->more() )
7008 SMESH_Group * group = groupIt->next();
7009 if ( !group ) continue;
7010 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7011 if ( !groupDS || groupDS->IsEmpty() ) continue;
7012 groupNames.insert ( group->GetName() );
7013 groupDS->SetStoreName( group->GetName() );
7014 const SMDSAbs_ElementType type = groupDS->GetType();
7015 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7016 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7017 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7018 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7021 // Loop on nodes and elements to add them in new groups
7023 vector< const SMDS_MeshElement* > resultElems;
7024 for ( int isNodes = 0; isNodes < 2; ++isNodes )
7026 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
7027 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7028 if ( gens.Length() != elems.Length() )
7029 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7031 // loop on created elements
7032 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7034 const SMDS_MeshElement* sourceElem = gens( iElem );
7035 if ( !sourceElem ) {
7036 MESSAGE("generateGroups(): NULL source element");
7039 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7040 if ( groupsOldNew.empty() ) { // no groups of this type at all
7041 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7042 ++iElem; // skip all elements made by sourceElem
7045 // collect all elements made by the iElem-th sourceElem
7046 resultElems.clear();
7047 if ( const SMDS_MeshElement* resElem = elems( iElem ))
7048 if ( resElem != sourceElem )
7049 resultElems.push_back( resElem );
7050 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7051 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7052 if ( resElem != sourceElem )
7053 resultElems.push_back( resElem );
7055 const SMDS_MeshElement* topElem = 0;
7056 if ( isNodes ) // there must be a top element
7058 topElem = resultElems.back();
7059 resultElems.pop_back();
7063 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7064 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7065 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7067 topElem = *resElemIt;
7068 *resElemIt = 0; // erase *resElemIt
7072 // add resultElems to groups originted from ones the sourceElem belongs to
7073 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7074 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7076 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7077 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7079 // fill in a new group
7080 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7081 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7082 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7084 newGroup.Add( *resElemIt );
7086 // fill a "top" group
7089 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7090 newTopGroup.Add( topElem );
7094 } // loop on created elements
7095 }// loop on nodes and elements
7097 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7099 list<int> topGrouIds;
7100 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7102 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
7103 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7104 orderedOldNewGroups[i]->get<2>() };
7105 for ( int is2nd = 0; is2nd < 2; ++is2nd )
7107 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7108 if ( newGroupDS->IsEmpty() )
7110 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7115 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7118 const bool isTop = ( topPresent &&
7119 newGroupDS->GetType() == oldGroupDS->GetType() &&
7122 string name = oldGroupDS->GetStoreName();
7123 { // remove trailing whitespaces (issue 22599)
7124 size_t size = name.size();
7125 while ( size > 1 && isspace( name[ size-1 ]))
7127 if ( size != name.size() )
7129 name.resize( size );
7130 oldGroupDS->SetStoreName( name.c_str() );
7133 if ( !targetMesh ) {
7134 string suffix = ( isTop ? "top": postfix.c_str() );
7138 while ( !groupNames.insert( name ).second ) // name exists
7139 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7144 newGroupDS->SetStoreName( name.c_str() );
7146 // make a SMESH_Groups
7147 mesh->AddGroup( newGroupDS );
7149 topGrouIds.push_back( newGroupDS->GetID() );
7151 newGroupIDs->push_back( newGroupDS->GetID() );
7155 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7160 //================================================================================
7162 * * \brief Return list of group of nodes close to each other within theTolerance
7163 * * Search among theNodes or in the whole mesh if theNodes is empty using
7164 * * an Octree algorithm
7165 * \param [in,out] theNodes - the nodes to treat
7166 * \param [in] theTolerance - the tolerance
7167 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7168 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7169 * corner and medium nodes in separate groups
7171 //================================================================================
7173 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7174 const double theTolerance,
7175 TListOfListOfNodes & theGroupsOfNodes,
7176 bool theSeparateCornersAndMedium)
7178 myLastCreatedElems.Clear();
7179 myLastCreatedNodes.Clear();
7181 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7182 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7183 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7184 theSeparateCornersAndMedium = false;
7186 TIDSortedNodeSet& corners = theNodes;
7187 TIDSortedNodeSet medium;
7189 if ( theNodes.empty() ) // get all nodes in the mesh
7191 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7192 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7193 if ( theSeparateCornersAndMedium )
7194 while ( nIt->more() )
7196 const SMDS_MeshNode* n = nIt->next();
7197 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7198 nodeSet->insert( nodeSet->end(), n );
7201 while ( nIt->more() )
7202 theNodes.insert( theNodes.end(),nIt->next() );
7204 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7206 TIDSortedNodeSet::iterator nIt = corners.begin();
7207 while ( nIt != corners.end() )
7208 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7210 medium.insert( medium.end(), *nIt );
7211 corners.erase( nIt++ );
7219 if ( !corners.empty() )
7220 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7221 if ( !medium.empty() )
7222 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7225 //=======================================================================
7226 //function : SimplifyFace
7227 //purpose : split a chain of nodes into several closed chains
7228 //=======================================================================
7230 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7231 vector<const SMDS_MeshNode *>& poly_nodes,
7232 vector<int>& quantities) const
7234 int nbNodes = faceNodes.size();
7239 set<const SMDS_MeshNode*> nodeSet;
7241 // get simple seq of nodes
7242 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7245 simpleNodes[iSimple++] = faceNodes[0];
7246 for (int iCur = 1; iCur < nbNodes; iCur++) {
7247 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7248 simpleNodes[iSimple++] = faceNodes[iCur];
7249 nodeSet.insert( faceNodes[iCur] );
7252 int nbUnique = nodeSet.size();
7253 int nbSimple = iSimple;
7254 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7264 bool foundLoop = (nbSimple > nbUnique);
7267 set<const SMDS_MeshNode*> loopSet;
7268 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7269 const SMDS_MeshNode* n = simpleNodes[iSimple];
7270 if (!loopSet.insert( n ).second) {
7274 int iC = 0, curLast = iSimple;
7275 for (; iC < curLast; iC++) {
7276 if (simpleNodes[iC] == n) break;
7278 int loopLen = curLast - iC;
7280 // create sub-element
7282 quantities.push_back(loopLen);
7283 for (; iC < curLast; iC++) {
7284 poly_nodes.push_back(simpleNodes[iC]);
7287 // shift the rest nodes (place from the first loop position)
7288 for (iC = curLast + 1; iC < nbSimple; iC++) {
7289 simpleNodes[iC - loopLen] = simpleNodes[iC];
7291 nbSimple -= loopLen;
7294 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7295 } // while (foundLoop)
7299 quantities.push_back(iSimple);
7300 for (int i = 0; i < iSimple; i++)
7301 poly_nodes.push_back(simpleNodes[i]);
7307 //=======================================================================
7308 //function : MergeNodes
7309 //purpose : In each group, the cdr of nodes are substituted by the first one
7311 //=======================================================================
7313 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7315 MESSAGE("MergeNodes");
7316 myLastCreatedElems.Clear();
7317 myLastCreatedNodes.Clear();
7319 SMESHDS_Mesh* aMesh = GetMeshDS();
7321 TNodeNodeMap nodeNodeMap; // node to replace - new node
7322 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7323 list< int > rmElemIds, rmNodeIds;
7325 // Fill nodeNodeMap and elems
7327 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7328 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7330 list<const SMDS_MeshNode*>& nodes = *grIt;
7331 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7332 const SMDS_MeshNode* nToKeep = *nIt;
7333 for ( ++nIt; nIt != nodes.end(); nIt++ )
7335 const SMDS_MeshNode* nToRemove = *nIt;
7336 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7337 if ( nToRemove != nToKeep )
7339 rmNodeIds.push_back( nToRemove->GetID() );
7340 AddToSameGroups( nToKeep, nToRemove, aMesh );
7341 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7342 // after MergeNodes() w/o creating node in place of merged ones.
7343 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7344 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7345 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7346 sm->SetIsAlwaysComputed( true );
7348 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7349 while ( invElemIt->more() ) {
7350 const SMDS_MeshElement* elem = invElemIt->next();
7355 // Change element nodes or remove an element
7357 set<const SMDS_MeshNode*> nodeSet;
7358 vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7360 ElemFeatures elemType;
7362 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7363 for ( ; eIt != elems.end(); eIt++ )
7365 const SMDS_MeshElement* elem = *eIt;
7366 const int nbNodes = elem->NbNodes();
7367 const int aShapeId = FindShape( elem );
7370 curNodes.resize( nbNodes );
7371 uniqueNodes.resize( nbNodes );
7372 iRepl.resize( nbNodes );
7373 int iUnique = 0, iCur = 0, nbRepl = 0;
7375 // get new seq of nodes
7376 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7377 while ( itN->more() )
7379 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7381 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7382 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7384 { ////////// BUG 0020185: begin
7385 bool stopRecur = false;
7386 set<const SMDS_MeshNode*> nodesRecur;
7387 nodesRecur.insert(n);
7388 while (!stopRecur) {
7389 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7390 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7391 n = (*nnIt_i).second;
7392 if (!nodesRecur.insert(n).second) {
7393 // error: recursive dependancy
7400 } ////////// BUG 0020185: end
7402 curNodes[ iCur ] = n;
7403 bool isUnique = nodeSet.insert( n ).second;
7405 uniqueNodes[ iUnique++ ] = n;
7407 iRepl[ nbRepl++ ] = iCur;
7411 // Analyse element topology after replacement
7414 int nbUniqueNodes = nodeSet.size();
7415 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7417 if (elem->IsPoly()) // Polygons and Polyhedral volumes
7419 if (elem->GetType() == SMDSAbs_Face) // Polygon
7421 elemType.Init( elem );
7422 const bool isQuad = elemType.myIsQuad;
7424 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7425 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7427 // a polygon can divide into several elements
7428 vector<const SMDS_MeshNode *> polygons_nodes;
7429 vector<int> quantities;
7430 int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7433 vector<const SMDS_MeshNode *> face_nodes;
7435 for (int iface = 0; iface < nbNew; iface++)
7437 int nbNewNodes = quantities[iface];
7438 face_nodes.assign( polygons_nodes.begin() + inode,
7439 polygons_nodes.begin() + inode + nbNewNodes );
7440 inode += nbNewNodes;
7441 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7443 bool isValid = ( nbNewNodes % 2 == 0 );
7444 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7445 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7446 elemType.SetQuad( isValid );
7447 if ( isValid ) // put medium nodes after corners
7448 SMDS_MeshCell::applyInterlaceRev
7449 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7450 nbNewNodes ), face_nodes );
7452 elemType.SetPoly(( nbNewNodes / ( elemType.myIsQuad + 1 ) > 4 ));
7454 SMDS_MeshElement* newElem = AddElement( face_nodes, elemType );
7456 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7459 rmElemIds.push_back(elem->GetID());
7463 else if (elem->GetType() == SMDSAbs_Volume) // Polyhedral volume
7465 if (nbUniqueNodes < 4) {
7466 rmElemIds.push_back(elem->GetID());
7469 // each face has to be analyzed in order to check volume validity
7470 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
7473 int nbFaces = aPolyedre->NbFaces();
7475 vector<const SMDS_MeshNode *> poly_nodes;
7476 vector<int> quantities;
7478 for (int iface = 1; iface <= nbFaces; iface++) {
7479 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7480 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7482 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7483 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7484 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7485 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7486 faceNode = (*nnIt).second;
7488 faceNodes[inode - 1] = faceNode;
7491 SimplifyFace(faceNodes, poly_nodes, quantities);
7494 if (quantities.size() > 3) {
7495 // to be done: remove coincident faces
7498 if (quantities.size() > 3)
7500 const SMDS_MeshElement* newElem =
7501 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7502 myLastCreatedElems.Append(newElem);
7503 if ( aShapeId && newElem )
7504 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7505 rmElemIds.push_back(elem->GetID());
7509 rmElemIds.push_back(elem->GetID());
7520 // TODO not all the possible cases are solved. Find something more generic?
7521 switch ( nbNodes ) {
7522 case 2: ///////////////////////////////////// EDGE
7523 isOk = false; break;
7524 case 3: ///////////////////////////////////// TRIANGLE
7525 isOk = false; break;
7527 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7529 else { //////////////////////////////////// QUADRANGLE
7530 if ( nbUniqueNodes < 3 )
7532 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7533 isOk = false; // opposite nodes stick
7534 //MESSAGE("isOk " << isOk);
7537 case 6: ///////////////////////////////////// PENTAHEDRON
7538 if ( nbUniqueNodes == 4 ) {
7539 // ---------------------------------> tetrahedron
7541 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7542 // all top nodes stick: reverse a bottom
7543 uniqueNodes[ 0 ] = curNodes [ 1 ];
7544 uniqueNodes[ 1 ] = curNodes [ 0 ];
7546 else if (nbRepl == 3 &&
7547 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7548 // all bottom nodes stick: set a top before
7549 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7550 uniqueNodes[ 0 ] = curNodes [ 3 ];
7551 uniqueNodes[ 1 ] = curNodes [ 4 ];
7552 uniqueNodes[ 2 ] = curNodes [ 5 ];
7554 else if (nbRepl == 4 &&
7555 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7556 // a lateral face turns into a line: reverse a bottom
7557 uniqueNodes[ 0 ] = curNodes [ 1 ];
7558 uniqueNodes[ 1 ] = curNodes [ 0 ];
7563 else if ( nbUniqueNodes == 5 ) {
7564 // PENTAHEDRON --------------------> 2 tetrahedrons
7565 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7566 // a bottom node sticks with a linked top one
7568 SMDS_MeshElement* newElem =
7569 aMesh->AddVolume(curNodes[ 3 ],
7572 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7573 myLastCreatedElems.Append(newElem);
7575 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7576 // 2. : reverse a bottom
7577 uniqueNodes[ 0 ] = curNodes [ 1 ];
7578 uniqueNodes[ 1 ] = curNodes [ 0 ];
7588 if(elem->IsQuadratic()) { // Quadratic quadrangle
7600 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7603 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7605 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7606 uniqueNodes[0] = curNodes[0];
7607 uniqueNodes[1] = curNodes[2];
7608 uniqueNodes[2] = curNodes[3];
7609 uniqueNodes[3] = curNodes[5];
7610 uniqueNodes[4] = curNodes[6];
7611 uniqueNodes[5] = curNodes[7];
7614 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7615 uniqueNodes[0] = curNodes[0];
7616 uniqueNodes[1] = curNodes[1];
7617 uniqueNodes[2] = curNodes[2];
7618 uniqueNodes[3] = curNodes[4];
7619 uniqueNodes[4] = curNodes[5];
7620 uniqueNodes[5] = curNodes[6];
7623 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7624 uniqueNodes[0] = curNodes[1];
7625 uniqueNodes[1] = curNodes[2];
7626 uniqueNodes[2] = curNodes[3];
7627 uniqueNodes[3] = curNodes[5];
7628 uniqueNodes[4] = curNodes[6];
7629 uniqueNodes[5] = curNodes[0];
7632 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7633 uniqueNodes[0] = curNodes[0];
7634 uniqueNodes[1] = curNodes[1];
7635 uniqueNodes[2] = curNodes[3];
7636 uniqueNodes[3] = curNodes[4];
7637 uniqueNodes[4] = curNodes[6];
7638 uniqueNodes[5] = curNodes[7];
7641 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7642 uniqueNodes[0] = curNodes[0];
7643 uniqueNodes[1] = curNodes[2];
7644 uniqueNodes[2] = curNodes[3];
7645 uniqueNodes[3] = curNodes[1];
7646 uniqueNodes[4] = curNodes[6];
7647 uniqueNodes[5] = curNodes[7];
7650 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7651 uniqueNodes[0] = curNodes[0];
7652 uniqueNodes[1] = curNodes[1];
7653 uniqueNodes[2] = curNodes[2];
7654 uniqueNodes[3] = curNodes[4];
7655 uniqueNodes[4] = curNodes[5];
7656 uniqueNodes[5] = curNodes[7];
7659 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7660 uniqueNodes[0] = curNodes[0];
7661 uniqueNodes[1] = curNodes[1];
7662 uniqueNodes[2] = curNodes[3];
7663 uniqueNodes[3] = curNodes[4];
7664 uniqueNodes[4] = curNodes[2];
7665 uniqueNodes[5] = curNodes[7];
7668 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7669 uniqueNodes[0] = curNodes[0];
7670 uniqueNodes[1] = curNodes[1];
7671 uniqueNodes[2] = curNodes[2];
7672 uniqueNodes[3] = curNodes[4];
7673 uniqueNodes[4] = curNodes[5];
7674 uniqueNodes[5] = curNodes[3];
7679 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7682 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7686 //////////////////////////////////// HEXAHEDRON
7688 SMDS_VolumeTool hexa (elem);
7689 hexa.SetExternalNormal();
7690 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7691 //////////////////////// HEX ---> 1 tetrahedron
7692 for ( int iFace = 0; iFace < 6; iFace++ ) {
7693 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7694 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7695 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7696 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7697 // one face turns into a point ...
7698 int iOppFace = hexa.GetOppFaceIndex( iFace );
7699 ind = hexa.GetFaceNodesIndices( iOppFace );
7701 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7702 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7705 if ( nbStick == 1 ) {
7706 // ... and the opposite one - into a triangle.
7708 ind = hexa.GetFaceNodesIndices( iFace );
7709 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7716 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7717 //////////////////////// HEX ---> 1 prism
7718 int nbTria = 0, iTria[3];
7719 const int *ind; // indices of face nodes
7720 // look for triangular faces
7721 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7722 ind = hexa.GetFaceNodesIndices( iFace );
7723 TIDSortedNodeSet faceNodes;
7724 for ( iCur = 0; iCur < 4; iCur++ )
7725 faceNodes.insert( curNodes[ind[iCur]] );
7726 if ( faceNodes.size() == 3 )
7727 iTria[ nbTria++ ] = iFace;
7729 // check if triangles are opposite
7730 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7733 // set nodes of the bottom triangle
7734 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7736 for ( iCur = 0; iCur < 4; iCur++ )
7737 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7738 indB.push_back( ind[iCur] );
7739 if ( !hexa.IsForward() )
7740 std::swap( indB[0], indB[2] );
7741 for ( iCur = 0; iCur < 3; iCur++ )
7742 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7743 // set nodes of the top triangle
7744 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7745 for ( iCur = 0; iCur < 3; ++iCur )
7746 for ( int j = 0; j < 4; ++j )
7747 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7749 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7755 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7756 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7757 for ( int iFace = 0; iFace < 6; iFace++ ) {
7758 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7759 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7760 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7761 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7762 // one face turns into a point ...
7763 int iOppFace = hexa.GetOppFaceIndex( iFace );
7764 ind = hexa.GetFaceNodesIndices( iOppFace );
7766 iUnique = 2; // reverse a tetrahedron 1 bottom
7767 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7768 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7770 else if ( iUnique >= 0 )
7771 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7773 if ( nbStick == 0 ) {
7774 // ... and the opposite one is a quadrangle
7776 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7777 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7780 SMDS_MeshElement* newElem =
7781 aMesh->AddVolume(curNodes[ind[ 0 ]],
7784 curNodes[indTop[ 0 ]]);
7785 myLastCreatedElems.Append(newElem);
7787 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7794 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7795 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7796 // find indices of quad and tri faces
7797 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7798 for ( iFace = 0; iFace < 6; iFace++ ) {
7799 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7801 for ( iCur = 0; iCur < 4; iCur++ )
7802 nodeSet.insert( curNodes[ind[ iCur ]] );
7803 nbUniqueNodes = nodeSet.size();
7804 if ( nbUniqueNodes == 3 )
7805 iTriFace[ nbTri++ ] = iFace;
7806 else if ( nbUniqueNodes == 4 )
7807 iQuadFace[ nbQuad++ ] = iFace;
7809 if (nbQuad == 2 && nbTri == 4 &&
7810 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7811 // 2 opposite quadrangles stuck with a diagonal;
7812 // sample groups of merged indices: (0-4)(2-6)
7813 // --------------------------------------------> 2 tetrahedrons
7814 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7815 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7816 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7817 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7818 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7819 // stuck with 0-2 diagonal
7827 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7828 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7829 // stuck with 1-3 diagonal
7841 uniqueNodes[ 0 ] = curNodes [ i0 ];
7842 uniqueNodes[ 1 ] = curNodes [ i1d ];
7843 uniqueNodes[ 2 ] = curNodes [ i3d ];
7844 uniqueNodes[ 3 ] = curNodes [ i0t ];
7847 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7851 myLastCreatedElems.Append(newElem);
7853 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7856 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7857 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7858 // --------------------------------------------> prism
7859 // find 2 opposite triangles
7861 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7862 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7863 // find indices of kept and replaced nodes
7864 // and fill unique nodes of 2 opposite triangles
7865 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7866 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7867 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7868 // fill unique nodes
7871 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7872 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7873 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7875 // iCur of a linked node of the opposite face (make normals co-directed):
7876 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7877 // check that correspondent corners of triangles are linked
7878 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7881 uniqueNodes[ iUnique ] = n;
7882 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7891 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7894 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7901 } // switch ( nbNodes )
7903 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7905 if ( isOk ) // the non-poly elem remains valid after sticking nodes
7907 if ( nbNodes != nbUniqueNodes ||
7908 !aMesh->ChangeElementNodes( elem, & curNodes[0], nbNodes ))
7910 elemType.Init( elem ).SetID( elem->GetID() );
7912 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7913 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7915 uniqueNodes.resize(nbUniqueNodes);
7916 SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7917 if ( sm && newElem )
7918 sm->AddElement( newElem );
7919 if ( elem != newElem )
7920 ReplaceElemInGroups( elem, newElem, aMesh );
7924 // Remove invalid regular element or invalid polygon
7925 rmElemIds.push_back( elem->GetID() );
7928 } // loop on elements
7930 // Remove bad elements, then equal nodes (order important)
7932 Remove( rmElemIds, false );
7933 Remove( rmNodeIds, true );
7939 // ========================================================
7940 // class : SortableElement
7941 // purpose : allow sorting elements basing on their nodes
7942 // ========================================================
7943 class SortableElement : public set <const SMDS_MeshElement*>
7947 SortableElement( const SMDS_MeshElement* theElem )
7950 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7951 while ( nodeIt->more() )
7952 this->insert( nodeIt->next() );
7955 const SMDS_MeshElement* Get() const
7959 mutable const SMDS_MeshElement* myElem;
7962 //=======================================================================
7963 //function : FindEqualElements
7964 //purpose : Return list of group of elements built on the same nodes.
7965 // Search among theElements or in the whole mesh if theElements is empty
7966 //=======================================================================
7968 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7969 TListOfListOfElementsID & theGroupsOfElementsID)
7971 myLastCreatedElems.Clear();
7972 myLastCreatedNodes.Clear();
7974 typedef map< SortableElement, int > TMapOfNodeSet;
7975 typedef list<int> TGroupOfElems;
7977 if ( theElements.empty() )
7978 { // get all elements in the mesh
7979 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7980 while ( eIt->more() )
7981 theElements.insert( theElements.end(), eIt->next() );
7984 vector< TGroupOfElems > arrayOfGroups;
7985 TGroupOfElems groupOfElems;
7986 TMapOfNodeSet mapOfNodeSet;
7988 TIDSortedElemSet::iterator elemIt = theElements.begin();
7989 for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7991 const SMDS_MeshElement* curElem = *elemIt;
7992 SortableElement SE(curElem);
7994 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7995 if ( !pp.second ) { // one more coincident elem
7996 TMapOfNodeSet::iterator& itSE = pp.first;
7997 int ind = (*itSE).second;
7998 arrayOfGroups[ind].push_back( curElem->GetID() );
8001 arrayOfGroups.push_back( groupOfElems );
8002 arrayOfGroups.back().push_back( curElem->GetID() );
8007 groupOfElems.clear();
8008 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8009 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8011 if ( groupIt->size() > 1 ) {
8012 //groupOfElems.sort(); -- theElements is sorted already
8013 theGroupsOfElementsID.push_back( groupOfElems );
8014 theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8019 //=======================================================================
8020 //function : MergeElements
8021 //purpose : In each given group, substitute all elements by the first one.
8022 //=======================================================================
8024 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8026 myLastCreatedElems.Clear();
8027 myLastCreatedNodes.Clear();
8029 typedef list<int> TListOfIDs;
8030 TListOfIDs rmElemIds; // IDs of elems to remove
8032 SMESHDS_Mesh* aMesh = GetMeshDS();
8034 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8035 while ( groupsIt != theGroupsOfElementsID.end() ) {
8036 TListOfIDs& aGroupOfElemID = *groupsIt;
8037 aGroupOfElemID.sort();
8038 int elemIDToKeep = aGroupOfElemID.front();
8039 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8040 aGroupOfElemID.pop_front();
8041 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8042 while ( idIt != aGroupOfElemID.end() ) {
8043 int elemIDToRemove = *idIt;
8044 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8045 // add the kept element in groups of removed one (PAL15188)
8046 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8047 rmElemIds.push_back( elemIDToRemove );
8053 Remove( rmElemIds, false );
8056 //=======================================================================
8057 //function : MergeEqualElements
8058 //purpose : Remove all but one of elements built on the same nodes.
8059 //=======================================================================
8061 void SMESH_MeshEditor::MergeEqualElements()
8063 TIDSortedElemSet aMeshElements; /* empty input ==
8064 to merge equal elements in the whole mesh */
8065 TListOfListOfElementsID aGroupsOfElementsID;
8066 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8067 MergeElements(aGroupsOfElementsID);
8070 //=======================================================================
8071 //function : findAdjacentFace
8073 //=======================================================================
8075 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8076 const SMDS_MeshNode* n2,
8077 const SMDS_MeshElement* elem)
8079 TIDSortedElemSet elemSet, avoidSet;
8081 avoidSet.insert ( elem );
8082 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8085 //=======================================================================
8086 //function : findSegment
8087 //purpose : Return a mesh segment by two nodes one of which can be medium
8088 //=======================================================================
8090 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8091 const SMDS_MeshNode* n2)
8093 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8094 while ( it->more() )
8096 const SMDS_MeshElement* seg = it->next();
8097 if ( seg->GetNodeIndex( n2 ) >= 0 )
8103 //=======================================================================
8104 //function : FindFreeBorder
8106 //=======================================================================
8108 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8110 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8111 const SMDS_MeshNode* theSecondNode,
8112 const SMDS_MeshNode* theLastNode,
8113 list< const SMDS_MeshNode* > & theNodes,
8114 list< const SMDS_MeshElement* >& theFaces)
8116 if ( !theFirstNode || !theSecondNode )
8118 // find border face between theFirstNode and theSecondNode
8119 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8123 theFaces.push_back( curElem );
8124 theNodes.push_back( theFirstNode );
8125 theNodes.push_back( theSecondNode );
8127 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8128 TIDSortedElemSet foundElems;
8129 bool needTheLast = ( theLastNode != 0 );
8131 while ( nStart != theLastNode ) {
8132 if ( nStart == theFirstNode )
8133 return !needTheLast;
8135 // find all free border faces sharing form nStart
8137 list< const SMDS_MeshElement* > curElemList;
8138 list< const SMDS_MeshNode* > nStartList;
8139 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8140 while ( invElemIt->more() ) {
8141 const SMDS_MeshElement* e = invElemIt->next();
8142 if ( e == curElem || foundElems.insert( e ).second ) {
8144 int iNode = 0, nbNodes = e->NbNodes();
8145 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8147 if ( e->IsQuadratic() ) {
8148 const SMDS_VtkFace* F =
8149 dynamic_cast<const SMDS_VtkFace*>(e);
8150 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8151 // use special nodes iterator
8152 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8153 while( anIter->more() ) {
8154 nodes[ iNode++ ] = cast2Node(anIter->next());
8158 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8159 while ( nIt->more() )
8160 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8162 nodes[ iNode ] = nodes[ 0 ];
8164 for ( iNode = 0; iNode < nbNodes; iNode++ )
8165 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8166 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8167 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8169 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8170 curElemList.push_back( e );
8174 // analyse the found
8176 int nbNewBorders = curElemList.size();
8177 if ( nbNewBorders == 0 ) {
8178 // no free border furthermore
8179 return !needTheLast;
8181 else if ( nbNewBorders == 1 ) {
8182 // one more element found
8184 nStart = nStartList.front();
8185 curElem = curElemList.front();
8186 theFaces.push_back( curElem );
8187 theNodes.push_back( nStart );
8190 // several continuations found
8191 list< const SMDS_MeshElement* >::iterator curElemIt;
8192 list< const SMDS_MeshNode* >::iterator nStartIt;
8193 // check if one of them reached the last node
8194 if ( needTheLast ) {
8195 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8196 curElemIt!= curElemList.end();
8197 curElemIt++, nStartIt++ )
8198 if ( *nStartIt == theLastNode ) {
8199 theFaces.push_back( *curElemIt );
8200 theNodes.push_back( *nStartIt );
8204 // find the best free border by the continuations
8205 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8206 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8207 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8208 curElemIt!= curElemList.end();
8209 curElemIt++, nStartIt++ )
8211 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8212 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8213 // find one more free border
8214 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8218 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8219 // choice: clear a worse one
8220 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8221 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8222 contNodes[ iWorse ].clear();
8223 contFaces[ iWorse ].clear();
8226 if ( contNodes[0].empty() && contNodes[1].empty() )
8229 // append the best free border
8230 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8231 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8232 theNodes.pop_back(); // remove nIgnore
8233 theNodes.pop_back(); // remove nStart
8234 theFaces.pop_back(); // remove curElem
8235 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8236 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8237 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8238 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8241 } // several continuations found
8242 } // while ( nStart != theLastNode )
8247 //=======================================================================
8248 //function : CheckFreeBorderNodes
8249 //purpose : Return true if the tree nodes are on a free border
8250 //=======================================================================
8252 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8253 const SMDS_MeshNode* theNode2,
8254 const SMDS_MeshNode* theNode3)
8256 list< const SMDS_MeshNode* > nodes;
8257 list< const SMDS_MeshElement* > faces;
8258 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8261 //=======================================================================
8262 //function : SewFreeBorder
8264 //warning : for border-to-side sewing theSideSecondNode is considered as
8265 // the last side node and theSideThirdNode is not used
8266 //=======================================================================
8268 SMESH_MeshEditor::Sew_Error
8269 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8270 const SMDS_MeshNode* theBordSecondNode,
8271 const SMDS_MeshNode* theBordLastNode,
8272 const SMDS_MeshNode* theSideFirstNode,
8273 const SMDS_MeshNode* theSideSecondNode,
8274 const SMDS_MeshNode* theSideThirdNode,
8275 const bool theSideIsFreeBorder,
8276 const bool toCreatePolygons,
8277 const bool toCreatePolyedrs)
8279 myLastCreatedElems.Clear();
8280 myLastCreatedNodes.Clear();
8282 MESSAGE("::SewFreeBorder()");
8283 Sew_Error aResult = SEW_OK;
8285 // ====================================
8286 // find side nodes and elements
8287 // ====================================
8289 list< const SMDS_MeshNode* > nSide[ 2 ];
8290 list< const SMDS_MeshElement* > eSide[ 2 ];
8291 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8292 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8296 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8297 nSide[0], eSide[0])) {
8298 MESSAGE(" Free Border 1 not found " );
8299 aResult = SEW_BORDER1_NOT_FOUND;
8301 if (theSideIsFreeBorder) {
8304 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8305 nSide[1], eSide[1])) {
8306 MESSAGE(" Free Border 2 not found " );
8307 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8310 if ( aResult != SEW_OK )
8313 if (!theSideIsFreeBorder) {
8317 // -------------------------------------------------------------------------
8319 // 1. If nodes to merge are not coincident, move nodes of the free border
8320 // from the coord sys defined by the direction from the first to last
8321 // nodes of the border to the correspondent sys of the side 2
8322 // 2. On the side 2, find the links most co-directed with the correspondent
8323 // links of the free border
8324 // -------------------------------------------------------------------------
8326 // 1. Since sewing may break if there are volumes to split on the side 2,
8327 // we wont move nodes but just compute new coordinates for them
8328 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8329 TNodeXYZMap nBordXYZ;
8330 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8331 list< const SMDS_MeshNode* >::iterator nBordIt;
8333 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8334 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8335 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8336 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8337 double tol2 = 1.e-8;
8338 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8339 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8340 // Need node movement.
8342 // find X and Z axes to create trsf
8343 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8345 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8347 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8350 gp_Ax3 toBordAx( Pb1, Zb, X );
8351 gp_Ax3 fromSideAx( Ps1, Zs, X );
8352 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8354 gp_Trsf toBordSys, fromSide2Sys;
8355 toBordSys.SetTransformation( toBordAx );
8356 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8357 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8360 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8361 const SMDS_MeshNode* n = *nBordIt;
8362 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8363 toBordSys.Transforms( xyz );
8364 fromSide2Sys.Transforms( xyz );
8365 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8369 // just insert nodes XYZ in the nBordXYZ map
8370 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8371 const SMDS_MeshNode* n = *nBordIt;
8372 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8376 // 2. On the side 2, find the links most co-directed with the correspondent
8377 // links of the free border
8379 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8380 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8381 sideNodes.push_back( theSideFirstNode );
8383 bool hasVolumes = false;
8384 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8385 set<long> foundSideLinkIDs, checkedLinkIDs;
8386 SMDS_VolumeTool volume;
8387 //const SMDS_MeshNode* faceNodes[ 4 ];
8389 const SMDS_MeshNode* sideNode;
8390 const SMDS_MeshElement* sideElem;
8391 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8392 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8393 nBordIt = bordNodes.begin();
8395 // border node position and border link direction to compare with
8396 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8397 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8398 // choose next side node by link direction or by closeness to
8399 // the current border node:
8400 bool searchByDir = ( *nBordIt != theBordLastNode );
8402 // find the next node on the Side 2
8404 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8406 checkedLinkIDs.clear();
8407 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8409 // loop on inverse elements of current node (prevSideNode) on the Side 2
8410 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8411 while ( invElemIt->more() )
8413 const SMDS_MeshElement* elem = invElemIt->next();
8414 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8415 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8416 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8417 bool isVolume = volume.Set( elem );
8418 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8419 if ( isVolume ) // --volume
8421 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8422 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8423 if(elem->IsQuadratic()) {
8424 const SMDS_VtkFace* F =
8425 dynamic_cast<const SMDS_VtkFace*>(elem);
8426 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8427 // use special nodes iterator
8428 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8429 while( anIter->more() ) {
8430 nodes[ iNode ] = cast2Node(anIter->next());
8431 if ( nodes[ iNode++ ] == prevSideNode )
8432 iPrevNode = iNode - 1;
8436 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8437 while ( nIt->more() ) {
8438 nodes[ iNode ] = cast2Node( nIt->next() );
8439 if ( nodes[ iNode++ ] == prevSideNode )
8440 iPrevNode = iNode - 1;
8443 // there are 2 links to check
8448 // loop on links, to be precise, on the second node of links
8449 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8450 const SMDS_MeshNode* n = nodes[ iNode ];
8452 if ( !volume.IsLinked( n, prevSideNode ))
8456 if ( iNode ) // a node before prevSideNode
8457 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8458 else // a node after prevSideNode
8459 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8461 // check if this link was already used
8462 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8463 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8464 if (!isJustChecked &&
8465 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8467 // test a link geometrically
8468 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8469 bool linkIsBetter = false;
8470 double dot = 0.0, dist = 0.0;
8471 if ( searchByDir ) { // choose most co-directed link
8472 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8473 linkIsBetter = ( dot > maxDot );
8475 else { // choose link with the node closest to bordPos
8476 dist = ( nextXYZ - bordPos ).SquareModulus();
8477 linkIsBetter = ( dist < minDist );
8479 if ( linkIsBetter ) {
8488 } // loop on inverse elements of prevSideNode
8491 MESSAGE(" Cant find path by links of the Side 2 ");
8492 return SEW_BAD_SIDE_NODES;
8494 sideNodes.push_back( sideNode );
8495 sideElems.push_back( sideElem );
8496 foundSideLinkIDs.insert ( linkID );
8497 prevSideNode = sideNode;
8499 if ( *nBordIt == theBordLastNode )
8500 searchByDir = false;
8502 // find the next border link to compare with
8503 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8504 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8505 // move to next border node if sideNode is before forward border node (bordPos)
8506 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8507 prevBordNode = *nBordIt;
8509 bordPos = nBordXYZ[ *nBordIt ];
8510 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8511 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8515 while ( sideNode != theSideSecondNode );
8517 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8518 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8519 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8521 } // end nodes search on the side 2
8523 // ============================
8524 // sew the border to the side 2
8525 // ============================
8527 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8528 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8530 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8531 if ( toMergeConformal && toCreatePolygons )
8533 // do not merge quadrangles if polygons are OK (IPAL0052824)
8534 eIt[0] = eSide[0].begin();
8535 eIt[1] = eSide[1].begin();
8536 bool allQuads[2] = { true, true };
8537 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8538 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8539 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8541 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8544 TListOfListOfNodes nodeGroupsToMerge;
8545 if (( toMergeConformal ) ||
8546 ( theSideIsFreeBorder && !theSideThirdNode )) {
8548 // all nodes are to be merged
8550 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8551 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8552 nIt[0]++, nIt[1]++ )
8554 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8555 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8556 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8561 // insert new nodes into the border and the side to get equal nb of segments
8563 // get normalized parameters of nodes on the borders
8564 vector< double > param[ 2 ];
8565 param[0].resize( maxNbNodes );
8566 param[1].resize( maxNbNodes );
8568 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8569 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8570 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8571 const SMDS_MeshNode* nPrev = *nIt;
8572 double bordLength = 0;
8573 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8574 const SMDS_MeshNode* nCur = *nIt;
8575 gp_XYZ segment (nCur->X() - nPrev->X(),
8576 nCur->Y() - nPrev->Y(),
8577 nCur->Z() - nPrev->Z());
8578 double segmentLen = segment.Modulus();
8579 bordLength += segmentLen;
8580 param[ iBord ][ iNode ] = bordLength;
8583 // normalize within [0,1]
8584 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8585 param[ iBord ][ iNode ] /= bordLength;
8589 // loop on border segments
8590 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8591 int i[ 2 ] = { 0, 0 };
8592 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8593 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8595 TElemOfNodeListMap insertMap;
8596 TElemOfNodeListMap::iterator insertMapIt;
8598 // key: elem to insert nodes into
8599 // value: 2 nodes to insert between + nodes to be inserted
8601 bool next[ 2 ] = { false, false };
8603 // find min adjacent segment length after sewing
8604 double nextParam = 10., prevParam = 0;
8605 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8606 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8607 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8608 if ( i[ iBord ] > 0 )
8609 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8611 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8612 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8613 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8615 // choose to insert or to merge nodes
8616 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8617 if ( Abs( du ) <= minSegLen * 0.2 ) {
8620 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8621 const SMDS_MeshNode* n0 = *nIt[0];
8622 const SMDS_MeshNode* n1 = *nIt[1];
8623 nodeGroupsToMerge.back().push_back( n1 );
8624 nodeGroupsToMerge.back().push_back( n0 );
8625 // position of node of the border changes due to merge
8626 param[ 0 ][ i[0] ] += du;
8627 // move n1 for the sake of elem shape evaluation during insertion.
8628 // n1 will be removed by MergeNodes() anyway
8629 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8630 next[0] = next[1] = true;
8635 int intoBord = ( du < 0 ) ? 0 : 1;
8636 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8637 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8638 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8639 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8640 if ( intoBord == 1 ) {
8641 // move node of the border to be on a link of elem of the side
8642 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8643 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8644 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8645 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8646 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8648 insertMapIt = insertMap.find( elem );
8649 bool notFound = ( insertMapIt == insertMap.end() );
8650 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8652 // insert into another link of the same element:
8653 // 1. perform insertion into the other link of the elem
8654 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8655 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8656 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8657 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8658 // 2. perform insertion into the link of adjacent faces
8659 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8660 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8662 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8663 InsertNodesIntoLink( seg, n12, n22, nodeList );
8665 if (toCreatePolyedrs) {
8666 // perform insertion into the links of adjacent volumes
8667 UpdateVolumes(n12, n22, nodeList);
8669 // 3. find an element appeared on n1 and n2 after the insertion
8670 insertMap.erase( elem );
8671 elem = findAdjacentFace( n1, n2, 0 );
8673 if ( notFound || otherLink ) {
8674 // add element and nodes of the side into the insertMap
8675 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8676 (*insertMapIt).second.push_back( n1 );
8677 (*insertMapIt).second.push_back( n2 );
8679 // add node to be inserted into elem
8680 (*insertMapIt).second.push_back( nIns );
8681 next[ 1 - intoBord ] = true;
8684 // go to the next segment
8685 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8686 if ( next[ iBord ] ) {
8687 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8689 nPrev[ iBord ] = *nIt[ iBord ];
8690 nIt[ iBord ]++; i[ iBord ]++;
8694 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8696 // perform insertion of nodes into elements
8698 for (insertMapIt = insertMap.begin();
8699 insertMapIt != insertMap.end();
8702 const SMDS_MeshElement* elem = (*insertMapIt).first;
8703 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8704 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8705 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8707 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8709 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8710 InsertNodesIntoLink( seg, n1, n2, nodeList );
8713 if ( !theSideIsFreeBorder ) {
8714 // look for and insert nodes into the faces adjacent to elem
8715 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8716 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8719 if (toCreatePolyedrs) {
8720 // perform insertion into the links of adjacent volumes
8721 UpdateVolumes(n1, n2, nodeList);
8724 } // end: insert new nodes
8726 MergeNodes ( nodeGroupsToMerge );
8729 // Remove coincident segments
8732 TIDSortedElemSet segments;
8733 SMESH_SequenceOfElemPtr newFaces;
8734 for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8736 if ( !myLastCreatedElems(i) ) continue;
8737 if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8738 segments.insert( segments.end(), myLastCreatedElems(i) );
8740 newFaces.Append( myLastCreatedElems(i) );
8742 // get segments adjacent to merged nodes
8743 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8744 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8746 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8747 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8748 while ( segIt->more() )
8749 segments.insert( segIt->next() );
8753 TListOfListOfElementsID equalGroups;
8754 if ( !segments.empty() )
8755 FindEqualElements( segments, equalGroups );
8756 if ( !equalGroups.empty() )
8758 // remove from segments those that will be removed
8759 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8760 for ( ; itGroups != equalGroups.end(); ++itGroups )
8762 list< int >& group = *itGroups;
8763 list< int >::iterator id = group.begin();
8764 for ( ++id; id != group.end(); ++id )
8765 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8766 segments.erase( seg );
8768 // remove equal segments
8769 MergeElements( equalGroups );
8771 // restore myLastCreatedElems
8772 myLastCreatedElems = newFaces;
8773 TIDSortedElemSet::iterator seg = segments.begin();
8774 for ( ; seg != segments.end(); ++seg )
8775 myLastCreatedElems.Append( *seg );
8781 //=======================================================================
8782 //function : InsertNodesIntoLink
8783 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8784 // and theBetweenNode2 and split theElement
8785 //=======================================================================
8787 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8788 const SMDS_MeshNode* theBetweenNode1,
8789 const SMDS_MeshNode* theBetweenNode2,
8790 list<const SMDS_MeshNode*>& theNodesToInsert,
8791 const bool toCreatePoly)
8793 if ( !theElement ) return;
8795 SMESHDS_Mesh *aMesh = GetMeshDS();
8796 vector<const SMDS_MeshElement*> newElems;
8798 if ( theElement->GetType() == SMDSAbs_Edge )
8800 theNodesToInsert.push_front( theBetweenNode1 );
8801 theNodesToInsert.push_back ( theBetweenNode2 );
8802 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8803 const SMDS_MeshNode* n1 = *n;
8804 for ( ++n; n != theNodesToInsert.end(); ++n )
8806 const SMDS_MeshNode* n2 = *n;
8807 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8808 AddToSameGroups( seg, theElement, aMesh );
8810 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8813 theNodesToInsert.pop_front();
8814 theNodesToInsert.pop_back();
8816 if ( theElement->IsQuadratic() ) // add a not split part
8818 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8819 theElement->end_nodes() );
8820 int iOther = 0, nbN = nodes.size();
8821 for ( ; iOther < nbN; ++iOther )
8822 if ( nodes[iOther] != theBetweenNode1 &&
8823 nodes[iOther] != theBetweenNode2 )
8827 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8828 AddToSameGroups( seg, theElement, aMesh );
8830 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8832 else if ( iOther == 2 )
8834 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8835 AddToSameGroups( seg, theElement, aMesh );
8837 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8840 // treat new elements
8841 for ( size_t i = 0; i < newElems.size(); ++i )
8844 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8845 myLastCreatedElems.Append( newElems[i] );
8847 ReplaceElemInGroups( theElement, newElems, aMesh );
8848 aMesh->RemoveElement( theElement );
8851 } // if ( theElement->GetType() == SMDSAbs_Edge )
8853 const SMDS_MeshElement* theFace = theElement;
8854 if ( theFace->GetType() != SMDSAbs_Face ) return;
8856 // find indices of 2 link nodes and of the rest nodes
8857 int iNode = 0, il1, il2, i3, i4;
8858 il1 = il2 = i3 = i4 = -1;
8859 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8861 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8862 while ( nodeIt->more() ) {
8863 const SMDS_MeshNode* n = nodeIt->next();
8864 if ( n == theBetweenNode1 )
8866 else if ( n == theBetweenNode2 )
8872 nodes[ iNode++ ] = n;
8874 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8877 // arrange link nodes to go one after another regarding the face orientation
8878 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8879 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8884 aNodesToInsert.reverse();
8886 // check that not link nodes of a quadrangles are in good order
8887 int nbFaceNodes = theFace->NbNodes();
8888 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8894 if (toCreatePoly || theFace->IsPoly()) {
8897 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8899 // add nodes of face up to first node of link
8902 if ( theFace->IsQuadratic() ) {
8903 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8904 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8905 // use special nodes iterator
8906 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8907 while( anIter->more() && !isFLN ) {
8908 const SMDS_MeshNode* n = cast2Node(anIter->next());
8909 poly_nodes[iNode++] = n;
8910 if (n == nodes[il1]) {
8914 // add nodes to insert
8915 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8916 for (; nIt != aNodesToInsert.end(); nIt++) {
8917 poly_nodes[iNode++] = *nIt;
8919 // add nodes of face starting from last node of link
8920 while ( anIter->more() ) {
8921 poly_nodes[iNode++] = cast2Node(anIter->next());
8925 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8926 while ( nodeIt->more() && !isFLN ) {
8927 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8928 poly_nodes[iNode++] = n;
8929 if (n == nodes[il1]) {
8933 // add nodes to insert
8934 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8935 for (; nIt != aNodesToInsert.end(); nIt++) {
8936 poly_nodes[iNode++] = *nIt;
8938 // add nodes of face starting from last node of link
8939 while ( nodeIt->more() ) {
8940 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8941 poly_nodes[iNode++] = n;
8946 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8949 else if ( !theFace->IsQuadratic() )
8951 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8952 int nbLinkNodes = 2 + aNodesToInsert.size();
8953 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8954 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8955 linkNodes[ 0 ] = nodes[ il1 ];
8956 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8957 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8958 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8959 linkNodes[ iNode++ ] = *nIt;
8961 // decide how to split a quadrangle: compare possible variants
8962 // and choose which of splits to be a quadrangle
8963 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8964 if ( nbFaceNodes == 3 ) {
8965 iBestQuad = nbSplits;
8968 else if ( nbFaceNodes == 4 ) {
8969 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8970 double aBestRate = DBL_MAX;
8971 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8973 double aBadRate = 0;
8974 // evaluate elements quality
8975 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8976 if ( iSplit == iQuad ) {
8977 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8981 aBadRate += getBadRate( &quad, aCrit );
8984 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8986 nodes[ iSplit < iQuad ? i4 : i3 ]);
8987 aBadRate += getBadRate( &tria, aCrit );
8991 if ( aBadRate < aBestRate ) {
8993 aBestRate = aBadRate;
8998 // create new elements
9000 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9001 SMDS_MeshElement* newElem = 0;
9002 if ( iSplit == iBestQuad )
9003 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9008 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9010 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
9013 const SMDS_MeshNode* newNodes[ 4 ];
9014 newNodes[ 0 ] = linkNodes[ i1 ];
9015 newNodes[ 1 ] = linkNodes[ i2 ];
9016 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9017 newNodes[ 3 ] = nodes[ i4 ];
9018 if (iSplit == iBestQuad)
9019 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
9021 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
9023 } // end if(!theFace->IsQuadratic())
9025 else { // theFace is quadratic
9026 // we have to split theFace on simple triangles and one simple quadrangle
9028 int nbshift = tmp*2;
9029 // shift nodes in nodes[] by nbshift
9031 for(i=0; i<nbshift; i++) {
9032 const SMDS_MeshNode* n = nodes[0];
9033 for(j=0; j<nbFaceNodes-1; j++) {
9034 nodes[j] = nodes[j+1];
9036 nodes[nbFaceNodes-1] = n;
9038 il1 = il1 - nbshift;
9039 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9040 // n0 n1 n2 n0 n1 n2
9041 // +-----+-----+ +-----+-----+
9050 // create new elements
9052 if ( nbFaceNodes == 6 ) { // quadratic triangle
9053 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9054 if ( theFace->IsMediumNode(nodes[il1]) ) {
9055 // create quadrangle
9056 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9062 // create quadrangle
9063 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9069 else { // nbFaceNodes==8 - quadratic quadrangle
9070 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9071 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9072 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9073 if ( theFace->IsMediumNode( nodes[ il1 ])) {
9074 // create quadrangle
9075 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9081 // create quadrangle
9082 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9088 // create needed triangles using n1,n2,n3 and inserted nodes
9089 int nbn = 2 + aNodesToInsert.size();
9090 vector<const SMDS_MeshNode*> aNodes(nbn);
9091 aNodes[0 ] = nodes[n1];
9092 aNodes[nbn-1] = nodes[n2];
9093 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9094 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9095 aNodes[iNode++] = *nIt;
9097 for ( i = 1; i < nbn; i++ )
9098 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9101 // remove the old face
9102 for ( size_t i = 0; i < newElems.size(); ++i )
9105 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9106 myLastCreatedElems.Append( newElems[i] );
9108 ReplaceElemInGroups( theFace, newElems, aMesh );
9109 aMesh->RemoveElement(theFace);
9111 } // InsertNodesIntoLink()
9113 //=======================================================================
9114 //function : UpdateVolumes
9116 //=======================================================================
9118 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9119 const SMDS_MeshNode* theBetweenNode2,
9120 list<const SMDS_MeshNode*>& theNodesToInsert)
9122 myLastCreatedElems.Clear();
9123 myLastCreatedNodes.Clear();
9125 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9126 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9127 const SMDS_MeshElement* elem = invElemIt->next();
9129 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9130 SMDS_VolumeTool aVolume (elem);
9131 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9134 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9135 int iface, nbFaces = aVolume.NbFaces();
9136 vector<const SMDS_MeshNode *> poly_nodes;
9137 vector<int> quantities (nbFaces);
9139 for (iface = 0; iface < nbFaces; iface++) {
9140 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9141 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9142 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9144 for (int inode = 0; inode < nbFaceNodes; inode++) {
9145 poly_nodes.push_back(faceNodes[inode]);
9147 if (nbInserted == 0) {
9148 if (faceNodes[inode] == theBetweenNode1) {
9149 if (faceNodes[inode + 1] == theBetweenNode2) {
9150 nbInserted = theNodesToInsert.size();
9152 // add nodes to insert
9153 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9154 for (; nIt != theNodesToInsert.end(); nIt++) {
9155 poly_nodes.push_back(*nIt);
9159 else if (faceNodes[inode] == theBetweenNode2) {
9160 if (faceNodes[inode + 1] == theBetweenNode1) {
9161 nbInserted = theNodesToInsert.size();
9163 // add nodes to insert in reversed order
9164 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9166 for (; nIt != theNodesToInsert.begin(); nIt--) {
9167 poly_nodes.push_back(*nIt);
9169 poly_nodes.push_back(*nIt);
9176 quantities[iface] = nbFaceNodes + nbInserted;
9179 // Replace the volume
9180 SMESHDS_Mesh *aMesh = GetMeshDS();
9182 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9184 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9185 myLastCreatedElems.Append( newElem );
9186 ReplaceElemInGroups( elem, newElem, aMesh );
9188 aMesh->RemoveElement( elem );
9194 //================================================================================
9196 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9198 //================================================================================
9200 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9201 vector<const SMDS_MeshNode *> & nodes,
9202 vector<int> & nbNodeInFaces )
9205 nbNodeInFaces.clear();
9206 SMDS_VolumeTool vTool ( elem );
9207 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9209 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9210 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9211 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9216 //=======================================================================
9218 * \brief Convert elements contained in a sub-mesh to quadratic
9219 * \return int - nb of checked elements
9221 //=======================================================================
9223 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9224 SMESH_MesherHelper& theHelper,
9225 const bool theForce3d)
9228 if( !theSm ) return nbElem;
9230 vector<int> nbNodeInFaces;
9231 vector<const SMDS_MeshNode *> nodes;
9232 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9233 while(ElemItr->more())
9236 const SMDS_MeshElement* elem = ElemItr->next();
9237 if( !elem ) continue;
9239 // analyse a necessity of conversion
9240 const SMDSAbs_ElementType aType = elem->GetType();
9241 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9243 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9244 bool hasCentralNodes = false;
9245 if ( elem->IsQuadratic() )
9248 switch ( aGeomType ) {
9249 case SMDSEntity_Quad_Triangle:
9250 case SMDSEntity_Quad_Quadrangle:
9251 case SMDSEntity_Quad_Hexa:
9252 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9254 case SMDSEntity_BiQuad_Triangle:
9255 case SMDSEntity_BiQuad_Quadrangle:
9256 case SMDSEntity_TriQuad_Hexa:
9257 alreadyOK = theHelper.GetIsBiQuadratic();
9258 hasCentralNodes = true;
9263 // take into account already present modium nodes
9265 case SMDSAbs_Volume:
9266 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9268 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9270 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9276 // get elem data needed to re-create it
9278 const int id = elem->GetID();
9279 const int nbNodes = elem->NbCornerNodes();
9280 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9281 if ( aGeomType == SMDSEntity_Polyhedra )
9282 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9283 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9284 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9286 // remove a linear element
9287 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9289 // remove central nodes of biquadratic elements (biquad->quad convertion)
9290 if ( hasCentralNodes )
9291 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9292 if ( nodes[i]->NbInverseElements() == 0 )
9293 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9295 const SMDS_MeshElement* NewElem = 0;
9301 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9309 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9312 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9315 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9319 case SMDSAbs_Volume :
9323 case SMDSEntity_Tetra:
9324 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9326 case SMDSEntity_Pyramid:
9327 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9329 case SMDSEntity_Penta:
9330 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9332 case SMDSEntity_Hexa:
9333 case SMDSEntity_Quad_Hexa:
9334 case SMDSEntity_TriQuad_Hexa:
9335 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9336 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9338 case SMDSEntity_Hexagonal_Prism:
9340 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9347 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9348 if( NewElem && NewElem->getshapeId() < 1 )
9349 theSm->AddElement( NewElem );
9353 //=======================================================================
9354 //function : ConvertToQuadratic
9356 //=======================================================================
9358 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9360 SMESHDS_Mesh* meshDS = GetMeshDS();
9362 SMESH_MesherHelper aHelper(*myMesh);
9364 aHelper.SetIsQuadratic( true );
9365 aHelper.SetIsBiQuadratic( theToBiQuad );
9366 aHelper.SetElementsOnShape(true);
9367 aHelper.ToFixNodeParameters( true );
9369 // convert elements assigned to sub-meshes
9370 int nbCheckedElems = 0;
9371 if ( myMesh->HasShapeToMesh() )
9373 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9375 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9376 while ( smIt->more() ) {
9377 SMESH_subMesh* sm = smIt->next();
9378 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9379 aHelper.SetSubShape( sm->GetSubShape() );
9380 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9386 // convert elements NOT assigned to sub-meshes
9387 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9388 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9390 aHelper.SetElementsOnShape(false);
9391 SMESHDS_SubMesh *smDS = 0;
9394 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9395 while( aEdgeItr->more() )
9397 const SMDS_MeshEdge* edge = aEdgeItr->next();
9398 if ( !edge->IsQuadratic() )
9400 int id = edge->GetID();
9401 const SMDS_MeshNode* n1 = edge->GetNode(0);
9402 const SMDS_MeshNode* n2 = edge->GetNode(1);
9404 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9406 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9407 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9411 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9416 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9417 while( aFaceItr->more() )
9419 const SMDS_MeshFace* face = aFaceItr->next();
9420 if ( !face ) continue;
9422 const SMDSAbs_EntityType type = face->GetEntityType();
9426 case SMDSEntity_Quad_Triangle:
9427 case SMDSEntity_Quad_Quadrangle:
9428 alreadyOK = !theToBiQuad;
9429 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9431 case SMDSEntity_BiQuad_Triangle:
9432 case SMDSEntity_BiQuad_Quadrangle:
9433 alreadyOK = theToBiQuad;
9434 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9436 default: alreadyOK = false;
9441 const int id = face->GetID();
9442 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9444 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9446 SMDS_MeshFace * NewFace = 0;
9449 case SMDSEntity_Triangle:
9450 case SMDSEntity_Quad_Triangle:
9451 case SMDSEntity_BiQuad_Triangle:
9452 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9453 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9454 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9457 case SMDSEntity_Quadrangle:
9458 case SMDSEntity_Quad_Quadrangle:
9459 case SMDSEntity_BiQuad_Quadrangle:
9460 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9461 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9462 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9466 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9468 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9472 vector<int> nbNodeInFaces;
9473 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9474 while(aVolumeItr->more())
9476 const SMDS_MeshVolume* volume = aVolumeItr->next();
9477 if ( !volume ) continue;
9479 const SMDSAbs_EntityType type = volume->GetEntityType();
9480 if ( volume->IsQuadratic() )
9485 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9486 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9487 default: alreadyOK = true;
9491 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9495 const int id = volume->GetID();
9496 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9497 if ( type == SMDSEntity_Polyhedra )
9498 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9499 else if ( type == SMDSEntity_Hexagonal_Prism )
9500 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9502 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9504 SMDS_MeshVolume * NewVolume = 0;
9507 case SMDSEntity_Tetra:
9508 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9510 case SMDSEntity_Hexa:
9511 case SMDSEntity_Quad_Hexa:
9512 case SMDSEntity_TriQuad_Hexa:
9513 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9514 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9515 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9516 if ( nodes[i]->NbInverseElements() == 0 )
9517 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9519 case SMDSEntity_Pyramid:
9520 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9521 nodes[3], nodes[4], id, theForce3d);
9523 case SMDSEntity_Penta:
9524 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9525 nodes[3], nodes[4], nodes[5], id, theForce3d);
9527 case SMDSEntity_Hexagonal_Prism:
9529 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9531 ReplaceElemInGroups(volume, NewVolume, meshDS);
9536 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9537 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9538 // aHelper.FixQuadraticElements(myError);
9539 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9543 //================================================================================
9545 * \brief Makes given elements quadratic
9546 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9547 * \param theElements - elements to make quadratic
9549 //================================================================================
9551 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9552 TIDSortedElemSet& theElements,
9553 const bool theToBiQuad)
9555 if ( theElements.empty() ) return;
9557 // we believe that all theElements are of the same type
9558 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9560 // get all nodes shared by theElements
9561 TIDSortedNodeSet allNodes;
9562 TIDSortedElemSet::iterator eIt = theElements.begin();
9563 for ( ; eIt != theElements.end(); ++eIt )
9564 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9566 // complete theElements with elements of lower dim whose all nodes are in allNodes
9568 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9569 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9570 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9571 for ( ; nIt != allNodes.end(); ++nIt )
9573 const SMDS_MeshNode* n = *nIt;
9574 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9575 while ( invIt->more() )
9577 const SMDS_MeshElement* e = invIt->next();
9578 const SMDSAbs_ElementType type = e->GetType();
9579 if ( e->IsQuadratic() )
9581 quadAdjacentElems[ type ].insert( e );
9584 switch ( e->GetEntityType() ) {
9585 case SMDSEntity_Quad_Triangle:
9586 case SMDSEntity_Quad_Quadrangle:
9587 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9588 case SMDSEntity_BiQuad_Triangle:
9589 case SMDSEntity_BiQuad_Quadrangle:
9590 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9591 default: alreadyOK = true;
9596 if ( type >= elemType )
9597 continue; // same type or more complex linear element
9599 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9600 continue; // e is already checked
9604 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9605 while ( nodeIt->more() && allIn )
9606 allIn = allNodes.count( nodeIt->next() );
9608 theElements.insert(e );
9612 SMESH_MesherHelper helper(*myMesh);
9613 helper.SetIsQuadratic( true );
9614 helper.SetIsBiQuadratic( theToBiQuad );
9616 // add links of quadratic adjacent elements to the helper
9618 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9619 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9620 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9622 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9624 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9625 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9626 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9628 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9630 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9631 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9632 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9634 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9637 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9639 SMESHDS_Mesh* meshDS = GetMeshDS();
9640 SMESHDS_SubMesh* smDS = 0;
9641 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9643 const SMDS_MeshElement* elem = *eIt;
9646 int nbCentralNodes = 0;
9647 switch ( elem->GetEntityType() ) {
9648 // linear convertible
9649 case SMDSEntity_Edge:
9650 case SMDSEntity_Triangle:
9651 case SMDSEntity_Quadrangle:
9652 case SMDSEntity_Tetra:
9653 case SMDSEntity_Pyramid:
9654 case SMDSEntity_Hexa:
9655 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9656 // quadratic that can become bi-quadratic
9657 case SMDSEntity_Quad_Triangle:
9658 case SMDSEntity_Quad_Quadrangle:
9659 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9661 case SMDSEntity_BiQuad_Triangle:
9662 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9663 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9665 default: alreadyOK = true;
9667 if ( alreadyOK ) continue;
9669 const SMDSAbs_ElementType type = elem->GetType();
9670 const int id = elem->GetID();
9671 const int nbNodes = elem->NbCornerNodes();
9672 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9674 helper.SetSubShape( elem->getshapeId() );
9676 if ( !smDS || !smDS->Contains( elem ))
9677 smDS = meshDS->MeshElements( elem->getshapeId() );
9678 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9680 SMDS_MeshElement * newElem = 0;
9683 case 4: // cases for most frequently used element types go first (for optimization)
9684 if ( type == SMDSAbs_Volume )
9685 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9687 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9690 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9691 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9694 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9697 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9700 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9701 nodes[4], id, theForce3d);
9704 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9705 nodes[4], nodes[5], id, theForce3d);
9709 ReplaceElemInGroups( elem, newElem, meshDS);
9710 if( newElem && smDS )
9711 smDS->AddElement( newElem );
9713 // remove central nodes
9714 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9715 if ( nodes[i]->NbInverseElements() == 0 )
9716 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9718 } // loop on theElements
9721 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9722 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9723 // helper.FixQuadraticElements( myError );
9724 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9728 //=======================================================================
9730 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9731 * \return int - nb of checked elements
9733 //=======================================================================
9735 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9736 SMDS_ElemIteratorPtr theItr,
9737 const int theShapeID)
9740 SMESHDS_Mesh* meshDS = GetMeshDS();
9741 ElemFeatures elemType;
9742 vector<const SMDS_MeshNode *> nodes;
9744 while( theItr->more() )
9746 const SMDS_MeshElement* elem = theItr->next();
9748 if( elem && elem->IsQuadratic())
9751 int nbCornerNodes = elem->NbCornerNodes();
9752 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9754 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9756 //remove a quadratic element
9757 if ( !theSm || !theSm->Contains( elem ))
9758 theSm = meshDS->MeshElements( elem->getshapeId() );
9759 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9761 // remove medium nodes
9762 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9763 if ( nodes[i]->NbInverseElements() == 0 )
9764 meshDS->RemoveFreeNode( nodes[i], theSm );
9766 // add a linear element
9767 nodes.resize( nbCornerNodes );
9768 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9769 ReplaceElemInGroups(elem, newElem, meshDS);
9770 if( theSm && newElem )
9771 theSm->AddElement( newElem );
9777 //=======================================================================
9778 //function : ConvertFromQuadratic
9780 //=======================================================================
9782 bool SMESH_MeshEditor::ConvertFromQuadratic()
9784 int nbCheckedElems = 0;
9785 if ( myMesh->HasShapeToMesh() )
9787 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9789 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9790 while ( smIt->more() ) {
9791 SMESH_subMesh* sm = smIt->next();
9792 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9793 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9799 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9800 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9802 SMESHDS_SubMesh *aSM = 0;
9803 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9811 //================================================================================
9813 * \brief Return true if all medium nodes of the element are in the node set
9815 //================================================================================
9817 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9819 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9820 if ( !nodeSet.count( elem->GetNode(i) ))
9826 //================================================================================
9828 * \brief Makes given elements linear
9830 //================================================================================
9832 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9834 if ( theElements.empty() ) return;
9836 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9837 set<int> mediumNodeIDs;
9838 TIDSortedElemSet::iterator eIt = theElements.begin();
9839 for ( ; eIt != theElements.end(); ++eIt )
9841 const SMDS_MeshElement* e = *eIt;
9842 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9843 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9846 // replace given elements by linear ones
9847 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9848 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9850 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9851 // except those elements sharing medium nodes of quadratic element whose medium nodes
9852 // are not all in mediumNodeIDs
9854 // get remaining medium nodes
9855 TIDSortedNodeSet mediumNodes;
9856 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9857 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9858 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9859 mediumNodes.insert( mediumNodes.end(), n );
9861 // find more quadratic elements to convert
9862 TIDSortedElemSet moreElemsToConvert;
9863 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9864 for ( ; nIt != mediumNodes.end(); ++nIt )
9866 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9867 while ( invIt->more() )
9869 const SMDS_MeshElement* e = invIt->next();
9870 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9872 // find a more complex element including e and
9873 // whose medium nodes are not in mediumNodes
9874 bool complexFound = false;
9875 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9877 SMDS_ElemIteratorPtr invIt2 =
9878 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9879 while ( invIt2->more() )
9881 const SMDS_MeshElement* eComplex = invIt2->next();
9882 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9884 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9885 if ( nbCommonNodes == e->NbNodes())
9887 complexFound = true;
9888 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9894 if ( !complexFound )
9895 moreElemsToConvert.insert( e );
9899 elemIt = elemSetIterator( moreElemsToConvert );
9900 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9903 //=======================================================================
9904 //function : SewSideElements
9906 //=======================================================================
9908 SMESH_MeshEditor::Sew_Error
9909 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9910 TIDSortedElemSet& theSide2,
9911 const SMDS_MeshNode* theFirstNode1,
9912 const SMDS_MeshNode* theFirstNode2,
9913 const SMDS_MeshNode* theSecondNode1,
9914 const SMDS_MeshNode* theSecondNode2)
9916 myLastCreatedElems.Clear();
9917 myLastCreatedNodes.Clear();
9919 MESSAGE ("::::SewSideElements()");
9920 if ( theSide1.size() != theSide2.size() )
9921 return SEW_DIFF_NB_OF_ELEMENTS;
9923 Sew_Error aResult = SEW_OK;
9925 // 1. Build set of faces representing each side
9926 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9927 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9929 // =======================================================================
9930 // 1. Build set of faces representing each side:
9931 // =======================================================================
9932 // a. build set of nodes belonging to faces
9933 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9934 // c. create temporary faces representing side of volumes if correspondent
9935 // face does not exist
9937 SMESHDS_Mesh* aMesh = GetMeshDS();
9938 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9939 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9940 TIDSortedElemSet faceSet1, faceSet2;
9941 set<const SMDS_MeshElement*> volSet1, volSet2;
9942 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9943 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9944 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9945 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9946 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9947 int iSide, iFace, iNode;
9949 list<const SMDS_MeshElement* > tempFaceList;
9950 for ( iSide = 0; iSide < 2; iSide++ ) {
9951 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9952 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9953 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9954 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9955 set<const SMDS_MeshElement*>::iterator vIt;
9956 TIDSortedElemSet::iterator eIt;
9957 set<const SMDS_MeshNode*>::iterator nIt;
9959 // check that given nodes belong to given elements
9960 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9961 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9962 int firstIndex = -1, secondIndex = -1;
9963 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9964 const SMDS_MeshElement* elem = *eIt;
9965 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9966 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9967 if ( firstIndex > -1 && secondIndex > -1 ) break;
9969 if ( firstIndex < 0 || secondIndex < 0 ) {
9970 // we can simply return until temporary faces created
9971 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9974 // -----------------------------------------------------------
9975 // 1a. Collect nodes of existing faces
9976 // and build set of face nodes in order to detect missing
9977 // faces corresponding to sides of volumes
9978 // -----------------------------------------------------------
9980 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9982 // loop on the given element of a side
9983 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9984 //const SMDS_MeshElement* elem = *eIt;
9985 const SMDS_MeshElement* elem = *eIt;
9986 if ( elem->GetType() == SMDSAbs_Face ) {
9987 faceSet->insert( elem );
9988 set <const SMDS_MeshNode*> faceNodeSet;
9989 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9990 while ( nodeIt->more() ) {
9991 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9992 nodeSet->insert( n );
9993 faceNodeSet.insert( n );
9995 setOfFaceNodeSet.insert( faceNodeSet );
9997 else if ( elem->GetType() == SMDSAbs_Volume )
9998 volSet->insert( elem );
10000 // ------------------------------------------------------------------------------
10001 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10002 // ------------------------------------------------------------------------------
10004 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10005 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10006 while ( fIt->more() ) { // loop on faces sharing a node
10007 const SMDS_MeshElement* f = fIt->next();
10008 if ( faceSet->find( f ) == faceSet->end() ) {
10009 // check if all nodes are in nodeSet and
10010 // complete setOfFaceNodeSet if they are
10011 set <const SMDS_MeshNode*> faceNodeSet;
10012 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10013 bool allInSet = true;
10014 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10015 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10016 if ( nodeSet->find( n ) == nodeSet->end() )
10019 faceNodeSet.insert( n );
10022 faceSet->insert( f );
10023 setOfFaceNodeSet.insert( faceNodeSet );
10029 // -------------------------------------------------------------------------
10030 // 1c. Create temporary faces representing sides of volumes if correspondent
10031 // face does not exist
10032 // -------------------------------------------------------------------------
10034 if ( !volSet->empty() ) {
10035 //int nodeSetSize = nodeSet->size();
10037 // loop on given volumes
10038 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10039 SMDS_VolumeTool vol (*vIt);
10040 // loop on volume faces: find free faces
10041 // --------------------------------------
10042 list<const SMDS_MeshElement* > freeFaceList;
10043 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10044 if ( !vol.IsFreeFace( iFace ))
10046 // check if there is already a face with same nodes in a face set
10047 const SMDS_MeshElement* aFreeFace = 0;
10048 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10049 int nbNodes = vol.NbFaceNodes( iFace );
10050 set <const SMDS_MeshNode*> faceNodeSet;
10051 vol.GetFaceNodes( iFace, faceNodeSet );
10052 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10054 // no such a face is given but it still can exist, check it
10055 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10056 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10058 if ( !aFreeFace ) {
10059 // create a temporary face
10060 if ( nbNodes == 3 ) {
10061 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10062 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10064 else if ( nbNodes == 4 ) {
10065 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10066 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10069 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10070 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10071 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10074 tempFaceList.push_back( aFreeFace );
10078 freeFaceList.push_back( aFreeFace );
10080 } // loop on faces of a volume
10082 // choose one of several free faces of a volume
10083 // --------------------------------------------
10084 if ( freeFaceList.size() > 1 ) {
10085 // choose a face having max nb of nodes shared by other elems of a side
10086 int maxNbNodes = -1;
10087 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10088 while ( fIt != freeFaceList.end() ) { // loop on free faces
10089 int nbSharedNodes = 0;
10090 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10091 while ( nodeIt->more() ) { // loop on free face nodes
10092 const SMDS_MeshNode* n =
10093 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10094 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10095 while ( invElemIt->more() ) {
10096 const SMDS_MeshElement* e = invElemIt->next();
10097 nbSharedNodes += faceSet->count( e );
10098 nbSharedNodes += elemSet->count( e );
10101 if ( nbSharedNodes > maxNbNodes ) {
10102 maxNbNodes = nbSharedNodes;
10103 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10105 else if ( nbSharedNodes == maxNbNodes ) {
10109 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10112 if ( freeFaceList.size() > 1 )
10114 // could not choose one face, use another way
10115 // choose a face most close to the bary center of the opposite side
10116 gp_XYZ aBC( 0., 0., 0. );
10117 set <const SMDS_MeshNode*> addedNodes;
10118 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10119 eIt = elemSet2->begin();
10120 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10121 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10122 while ( nodeIt->more() ) { // loop on free face nodes
10123 const SMDS_MeshNode* n =
10124 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10125 if ( addedNodes.insert( n ).second )
10126 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10129 aBC /= addedNodes.size();
10130 double minDist = DBL_MAX;
10131 fIt = freeFaceList.begin();
10132 while ( fIt != freeFaceList.end() ) { // loop on free faces
10134 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10135 while ( nodeIt->more() ) { // loop on free face nodes
10136 const SMDS_MeshNode* n =
10137 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10138 gp_XYZ p( n->X(),n->Y(),n->Z() );
10139 dist += ( aBC - p ).SquareModulus();
10141 if ( dist < minDist ) {
10143 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10146 fIt = freeFaceList.erase( fIt++ );
10149 } // choose one of several free faces of a volume
10151 if ( freeFaceList.size() == 1 ) {
10152 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10153 faceSet->insert( aFreeFace );
10154 // complete a node set with nodes of a found free face
10155 // for ( iNode = 0; iNode < ; iNode++ )
10156 // nodeSet->insert( fNodes[ iNode ] );
10159 } // loop on volumes of a side
10161 // // complete a set of faces if new nodes in a nodeSet appeared
10162 // // ----------------------------------------------------------
10163 // if ( nodeSetSize != nodeSet->size() ) {
10164 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10165 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10166 // while ( fIt->more() ) { // loop on faces sharing a node
10167 // const SMDS_MeshElement* f = fIt->next();
10168 // if ( faceSet->find( f ) == faceSet->end() ) {
10169 // // check if all nodes are in nodeSet and
10170 // // complete setOfFaceNodeSet if they are
10171 // set <const SMDS_MeshNode*> faceNodeSet;
10172 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10173 // bool allInSet = true;
10174 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10175 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10176 // if ( nodeSet->find( n ) == nodeSet->end() )
10177 // allInSet = false;
10179 // faceNodeSet.insert( n );
10181 // if ( allInSet ) {
10182 // faceSet->insert( f );
10183 // setOfFaceNodeSet.insert( faceNodeSet );
10189 } // Create temporary faces, if there are volumes given
10192 if ( faceSet1.size() != faceSet2.size() ) {
10193 // delete temporary faces: they are in reverseElements of actual nodes
10194 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10195 // while ( tmpFaceIt->more() )
10196 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10197 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10198 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10199 // aMesh->RemoveElement(*tmpFaceIt);
10200 MESSAGE("Diff nb of faces");
10201 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10204 // ============================================================
10205 // 2. Find nodes to merge:
10206 // bind a node to remove to a node to put instead
10207 // ============================================================
10209 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10210 if ( theFirstNode1 != theFirstNode2 )
10211 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10212 if ( theSecondNode1 != theSecondNode2 )
10213 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10215 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10216 set< long > linkIdSet; // links to process
10217 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10219 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10220 list< NLink > linkList[2];
10221 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10222 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10223 // loop on links in linkList; find faces by links and append links
10224 // of the found faces to linkList
10225 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10226 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10228 NLink link[] = { *linkIt[0], *linkIt[1] };
10229 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10230 if ( !linkIdSet.count( linkID ) )
10233 // by links, find faces in the face sets,
10234 // and find indices of link nodes in the found faces;
10235 // in a face set, there is only one or no face sharing a link
10236 // ---------------------------------------------------------------
10238 const SMDS_MeshElement* face[] = { 0, 0 };
10239 vector<const SMDS_MeshNode*> fnodes[2];
10240 int iLinkNode[2][2];
10241 TIDSortedElemSet avoidSet;
10242 for ( 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 //cout << "Side " << iSide << " ";
10246 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10247 // find a face by two link nodes
10248 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10249 *faceSetPtr[ iSide ], avoidSet,
10250 &iLinkNode[iSide][0],
10251 &iLinkNode[iSide][1] );
10252 if ( face[ iSide ])
10254 //cout << " F " << face[ iSide]->GetID() <<endl;
10255 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10256 // put face nodes to fnodes
10257 if ( face[ iSide ]->IsQuadratic() )
10259 // use interlaced nodes iterator
10260 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10261 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10262 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10263 while ( nIter->more() )
10264 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10268 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10269 face[ iSide ]->end_nodes() );
10271 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10275 // check similarity of elements of the sides
10276 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10277 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10278 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10279 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10282 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10284 break; // do not return because it's necessary to remove tmp faces
10287 // set nodes to merge
10288 // -------------------
10290 if ( face[0] && face[1] ) {
10291 const int nbNodes = face[0]->NbNodes();
10292 if ( nbNodes != face[1]->NbNodes() ) {
10293 MESSAGE("Diff nb of face nodes");
10294 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10295 break; // do not return because it s necessary to remove tmp faces
10297 bool reverse[] = { false, false }; // order of nodes in the link
10298 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10299 // analyse link orientation in faces
10300 int i1 = iLinkNode[ iSide ][ 0 ];
10301 int i2 = iLinkNode[ iSide ][ 1 ];
10302 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10304 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10305 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10306 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10308 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10309 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10312 // add other links of the faces to linkList
10313 // -----------------------------------------
10315 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10316 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10317 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10318 if ( !iter_isnew.second ) { // already in a set: no need to process
10319 linkIdSet.erase( iter_isnew.first );
10321 else // new in set == encountered for the first time: add
10323 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10324 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10325 linkList[0].push_back ( NLink( n1, n2 ));
10326 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10331 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10334 } // loop on link lists
10336 if ( aResult == SEW_OK &&
10337 ( //linkIt[0] != linkList[0].end() ||
10338 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10339 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10340 " " << (faceSetPtr[1]->empty()));
10341 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10344 // ====================================================================
10345 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10346 // ====================================================================
10348 // delete temporary faces
10349 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10350 // while ( tmpFaceIt->more() )
10351 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10352 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10353 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10354 aMesh->RemoveElement(*tmpFaceIt);
10356 if ( aResult != SEW_OK)
10359 list< int > nodeIDsToRemove;
10360 vector< const SMDS_MeshNode*> nodes;
10361 ElemFeatures elemType;
10363 // loop on nodes replacement map
10364 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10365 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10366 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10368 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10369 nodeIDsToRemove.push_back( nToRemove->GetID() );
10370 // loop on elements sharing nToRemove
10371 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10372 while ( invElemIt->more() ) {
10373 const SMDS_MeshElement* e = invElemIt->next();
10374 // get a new suite of nodes: make replacement
10375 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10376 nodes.resize( nbNodes );
10377 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10378 while ( nIt->more() ) {
10379 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10380 nnIt = nReplaceMap.find( n );
10381 if ( nnIt != nReplaceMap.end() ) {
10383 n = (*nnIt).second;
10387 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10388 // elemIDsToRemove.push_back( e->GetID() );
10392 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10393 aMesh->RemoveElement( e );
10395 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10397 AddToSameGroups( newElem, e, aMesh );
10398 if ( int aShapeId = e->getshapeId() )
10399 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10405 Remove( nodeIDsToRemove, true );
10410 //================================================================================
10412 * \brief Find corresponding nodes in two sets of faces
10413 * \param theSide1 - first face set
10414 * \param theSide2 - second first face
10415 * \param theFirstNode1 - a boundary node of set 1
10416 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10417 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10418 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10419 * \param nReplaceMap - output map of corresponding nodes
10420 * \return bool - is a success or not
10422 //================================================================================
10425 //#define DEBUG_MATCHING_NODES
10428 SMESH_MeshEditor::Sew_Error
10429 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10430 set<const SMDS_MeshElement*>& theSide2,
10431 const SMDS_MeshNode* theFirstNode1,
10432 const SMDS_MeshNode* theFirstNode2,
10433 const SMDS_MeshNode* theSecondNode1,
10434 const SMDS_MeshNode* theSecondNode2,
10435 TNodeNodeMap & nReplaceMap)
10437 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10439 nReplaceMap.clear();
10440 if ( theFirstNode1 != theFirstNode2 )
10441 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10442 if ( theSecondNode1 != theSecondNode2 )
10443 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10445 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10446 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10448 list< NLink > linkList[2];
10449 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10450 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10452 // loop on links in linkList; find faces by links and append links
10453 // of the found faces to linkList
10454 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10455 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10456 NLink link[] = { *linkIt[0], *linkIt[1] };
10457 if ( linkSet.find( link[0] ) == linkSet.end() )
10460 // by links, find faces in the face sets,
10461 // and find indices of link nodes in the found faces;
10462 // in a face set, there is only one or no face sharing a link
10463 // ---------------------------------------------------------------
10465 const SMDS_MeshElement* face[] = { 0, 0 };
10466 list<const SMDS_MeshNode*> notLinkNodes[2];
10467 //bool reverse[] = { false, false }; // order of notLinkNodes
10469 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10471 const SMDS_MeshNode* n1 = link[iSide].first;
10472 const SMDS_MeshNode* n2 = link[iSide].second;
10473 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10474 set< const SMDS_MeshElement* > facesOfNode1;
10475 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10477 // during a loop of the first node, we find all faces around n1,
10478 // during a loop of the second node, we find one face sharing both n1 and n2
10479 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10480 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10481 while ( fIt->more() ) { // loop on faces sharing a node
10482 const SMDS_MeshElement* f = fIt->next();
10483 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10484 ! facesOfNode1.insert( f ).second ) // f encounters twice
10486 if ( face[ iSide ] ) {
10487 MESSAGE( "2 faces per link " );
10488 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10491 faceSet->erase( f );
10493 // get not link nodes
10494 int nbN = f->NbNodes();
10495 if ( f->IsQuadratic() )
10497 nbNodes[ iSide ] = nbN;
10498 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10499 int i1 = f->GetNodeIndex( n1 );
10500 int i2 = f->GetNodeIndex( n2 );
10501 int iEnd = nbN, iBeg = -1, iDelta = 1;
10502 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10504 std::swap( iEnd, iBeg ); iDelta = -1;
10509 if ( i == iEnd ) i = iBeg + iDelta;
10510 if ( i == i1 ) break;
10511 nodes.push_back ( f->GetNode( i ) );
10517 // check similarity of elements of the sides
10518 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10519 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10520 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10521 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10524 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10528 // set nodes to merge
10529 // -------------------
10531 if ( face[0] && face[1] ) {
10532 if ( nbNodes[0] != nbNodes[1] ) {
10533 MESSAGE("Diff nb of face nodes");
10534 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10536 #ifdef DEBUG_MATCHING_NODES
10537 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10538 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10539 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10541 int nbN = nbNodes[0];
10543 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10544 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10545 for ( int i = 0 ; i < nbN - 2; ++i ) {
10546 #ifdef DEBUG_MATCHING_NODES
10547 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10549 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10553 // add other links of the face 1 to linkList
10554 // -----------------------------------------
10556 const SMDS_MeshElement* f0 = face[0];
10557 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10558 for ( int i = 0; i < nbN; i++ )
10560 const SMDS_MeshNode* n2 = f0->GetNode( i );
10561 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10562 linkSet.insert( SMESH_TLink( n1, n2 ));
10563 if ( !iter_isnew.second ) { // already in a set: no need to process
10564 linkSet.erase( iter_isnew.first );
10566 else // new in set == encountered for the first time: add
10568 #ifdef DEBUG_MATCHING_NODES
10569 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10570 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10572 linkList[0].push_back ( NLink( n1, n2 ));
10573 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10578 } // loop on link lists
10583 //================================================================================
10585 * \brief Create elements equal (on same nodes) to given ones
10586 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10587 * elements of the uppest dimension are duplicated.
10589 //================================================================================
10591 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10593 ClearLastCreated();
10594 SMESHDS_Mesh* mesh = GetMeshDS();
10596 // get an element type and an iterator over elements
10598 SMDSAbs_ElementType type;
10599 SMDS_ElemIteratorPtr elemIt;
10600 vector< const SMDS_MeshElement* > allElems;
10601 if ( theElements.empty() )
10603 if ( mesh->NbNodes() == 0 )
10605 // get most complex type
10606 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10607 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10608 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10610 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10611 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10616 // put all elements in the vector <allElems>
10617 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10618 elemIt = mesh->elementsIterator( type );
10619 while ( elemIt->more() )
10620 allElems.push_back( elemIt->next());
10621 elemIt = elemSetIterator( allElems );
10625 type = (*theElements.begin())->GetType();
10626 elemIt = elemSetIterator( theElements );
10629 // duplicate elements
10631 ElemFeatures elemType;
10633 vector< const SMDS_MeshNode* > nodes;
10634 while ( elemIt->more() )
10636 const SMDS_MeshElement* elem = elemIt->next();
10637 if ( elem->GetType() != type )
10640 elemType.Init( elem, /*basicOnly=*/false );
10641 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10643 AddElement( nodes, elemType );
10647 //================================================================================
10649 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10650 \param theElems - the list of elements (edges or faces) to be replicated
10651 The nodes for duplication could be found from these elements
10652 \param theNodesNot - list of nodes to NOT replicate
10653 \param theAffectedElems - the list of elements (cells and edges) to which the
10654 replicated nodes should be associated to.
10655 \return TRUE if operation has been completed successfully, FALSE otherwise
10657 //================================================================================
10659 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10660 const TIDSortedElemSet& theNodesNot,
10661 const TIDSortedElemSet& theAffectedElems )
10663 myLastCreatedElems.Clear();
10664 myLastCreatedNodes.Clear();
10666 if ( theElems.size() == 0 )
10669 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10674 TNodeNodeMap anOldNodeToNewNode;
10675 // duplicate elements and nodes
10676 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10677 // replce nodes by duplications
10678 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10682 //================================================================================
10684 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10685 \param theMeshDS - mesh instance
10686 \param theElems - the elements replicated or modified (nodes should be changed)
10687 \param theNodesNot - nodes to NOT replicate
10688 \param theNodeNodeMap - relation of old node to new created node
10689 \param theIsDoubleElem - flag os to replicate element or modify
10690 \return TRUE if operation has been completed successfully, FALSE otherwise
10692 //================================================================================
10694 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10695 const TIDSortedElemSet& theElems,
10696 const TIDSortedElemSet& theNodesNot,
10697 TNodeNodeMap& theNodeNodeMap,
10698 const bool theIsDoubleElem )
10700 MESSAGE("doubleNodes");
10701 // iterate through element and duplicate them (by nodes duplication)
10703 std::vector<const SMDS_MeshNode*> newNodes;
10704 ElemFeatures elemType;
10706 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10707 for ( ; elemItr != theElems.end(); ++elemItr )
10709 const SMDS_MeshElement* anElem = *elemItr;
10713 // duplicate nodes to duplicate element
10714 bool isDuplicate = false;
10715 newNodes.resize( anElem->NbNodes() );
10716 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10718 while ( anIter->more() )
10720 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10721 const SMDS_MeshNode* aNewNode = aCurrNode;
10722 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10723 if ( n2n != theNodeNodeMap.end() )
10725 aNewNode = n2n->second;
10727 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10730 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10731 copyPosition( aCurrNode, aNewNode );
10732 theNodeNodeMap[ aCurrNode ] = aNewNode;
10733 myLastCreatedNodes.Append( aNewNode );
10735 isDuplicate |= (aCurrNode != aNewNode);
10736 newNodes[ ind++ ] = aNewNode;
10738 if ( !isDuplicate )
10741 if ( theIsDoubleElem )
10742 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10744 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10751 //================================================================================
10753 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10754 \param theNodes - identifiers of nodes to be doubled
10755 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10756 nodes. If list of element identifiers is empty then nodes are doubled but
10757 they not assigned to elements
10758 \return TRUE if operation has been completed successfully, FALSE otherwise
10760 //================================================================================
10762 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10763 const std::list< int >& theListOfModifiedElems )
10765 MESSAGE("DoubleNodes");
10766 myLastCreatedElems.Clear();
10767 myLastCreatedNodes.Clear();
10769 if ( theListOfNodes.size() == 0 )
10772 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10776 // iterate through nodes and duplicate them
10778 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10780 std::list< int >::const_iterator aNodeIter;
10781 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10783 int aCurr = *aNodeIter;
10784 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10790 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10793 copyPosition( aNode, aNewNode );
10794 anOldNodeToNewNode[ aNode ] = aNewNode;
10795 myLastCreatedNodes.Append( aNewNode );
10799 // Create map of new nodes for modified elements
10801 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10803 std::list< int >::const_iterator anElemIter;
10804 for ( anElemIter = theListOfModifiedElems.begin();
10805 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10807 int aCurr = *anElemIter;
10808 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10812 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10814 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10816 while ( anIter->more() )
10818 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10819 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10821 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10822 aNodeArr[ ind++ ] = aNewNode;
10825 aNodeArr[ ind++ ] = aCurrNode;
10827 anElemToNodes[ anElem ] = aNodeArr;
10830 // Change nodes of elements
10832 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10833 anElemToNodesIter = anElemToNodes.begin();
10834 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10836 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10837 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10840 MESSAGE("ChangeElementNodes");
10841 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10850 //================================================================================
10852 \brief Check if element located inside shape
10853 \return TRUE if IN or ON shape, FALSE otherwise
10855 //================================================================================
10857 template<class Classifier>
10858 bool isInside(const SMDS_MeshElement* theElem,
10859 Classifier& theClassifier,
10860 const double theTol)
10862 gp_XYZ centerXYZ (0, 0, 0);
10863 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10864 while (aNodeItr->more())
10865 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10867 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10868 theClassifier.Perform(aPnt, theTol);
10869 TopAbs_State aState = theClassifier.State();
10870 return (aState == TopAbs_IN || aState == TopAbs_ON );
10873 //================================================================================
10875 * \brief Classifier of the 3D point on the TopoDS_Face
10876 * with interaface suitable for isInside()
10878 //================================================================================
10880 struct _FaceClassifier
10882 Extrema_ExtPS _extremum;
10883 BRepAdaptor_Surface _surface;
10884 TopAbs_State _state;
10886 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10888 _extremum.Initialize( _surface,
10889 _surface.FirstUParameter(), _surface.LastUParameter(),
10890 _surface.FirstVParameter(), _surface.LastVParameter(),
10891 _surface.Tolerance(), _surface.Tolerance() );
10893 void Perform(const gp_Pnt& aPnt, double theTol)
10896 _state = TopAbs_OUT;
10897 _extremum.Perform(aPnt);
10898 if ( _extremum.IsDone() )
10899 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10900 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10902 TopAbs_State State() const
10909 //================================================================================
10911 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10912 This method is the first step of DoubleNodeElemGroupsInRegion.
10913 \param theElems - list of groups of elements (edges or faces) to be replicated
10914 \param theNodesNot - list of groups of nodes not to replicated
10915 \param theShape - shape to detect affected elements (element which geometric center
10916 located on or inside shape). If the shape is null, detection is done on faces orientations
10917 (select elements with a gravity center on the side given by faces normals).
10918 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10919 The replicated nodes should be associated to affected elements.
10920 \return groups of affected elements
10921 \sa DoubleNodeElemGroupsInRegion()
10923 //================================================================================
10925 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10926 const TIDSortedElemSet& theNodesNot,
10927 const TopoDS_Shape& theShape,
10928 TIDSortedElemSet& theAffectedElems)
10930 if ( theShape.IsNull() )
10932 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10933 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10934 std::set<const SMDS_MeshElement*> edgesToCheck;
10935 alreadyCheckedNodes.clear();
10936 alreadyCheckedElems.clear();
10937 edgesToCheck.clear();
10939 // --- iterates on elements to be replicated and get elements by back references from their nodes
10941 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10943 for ( ielem=1; elemItr != theElems.end(); ++elemItr )
10945 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10946 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10949 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10950 MESSAGE("element " << ielem++ << " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10951 std::set<const SMDS_MeshNode*> nodesElem;
10953 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10954 while ( nodeItr->more() )
10956 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10957 nodesElem.insert(aNode);
10959 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10960 for (; nodit != nodesElem.end(); nodit++)
10962 MESSAGE(" noeud ");
10963 const SMDS_MeshNode* aNode = *nodit;
10964 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10966 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10968 alreadyCheckedNodes.insert(aNode);
10969 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10970 while ( backElemItr->more() )
10972 MESSAGE(" backelem ");
10973 const SMDS_MeshElement* curElem = backElemItr->next();
10974 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10976 if (theElems.find(curElem) != theElems.end())
10978 alreadyCheckedElems.insert(curElem);
10979 double x=0, y=0, z=0;
10981 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10982 while ( nodeItr2->more() )
10984 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10985 x += anotherNode->X();
10986 y += anotherNode->Y();
10987 z += anotherNode->Z();
10991 p.SetCoord( x/nb -aNode->X(),
10993 z/nb -aNode->Z() );
10994 MESSAGE(" check " << p.X() << " " << p.Y() << " " << p.Z());
10997 MESSAGE(" --- inserted")
10998 theAffectedElems.insert( curElem );
11000 else if (curElem->GetType() == SMDSAbs_Edge)
11001 edgesToCheck.insert(curElem);
11005 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
11006 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
11007 for( ; eit != edgesToCheck.end(); eit++)
11009 bool onside = true;
11010 const SMDS_MeshElement* anEdge = *eit;
11011 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
11012 while ( nodeItr->more() )
11014 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11015 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
11023 MESSAGE(" --- edge onside inserted")
11024 theAffectedElems.insert(anEdge);
11030 const double aTol = Precision::Confusion();
11031 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11032 auto_ptr<_FaceClassifier> aFaceClassifier;
11033 if ( theShape.ShapeType() == TopAbs_SOLID )
11035 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11036 bsc3d->PerformInfinitePoint(aTol);
11038 else if (theShape.ShapeType() == TopAbs_FACE )
11040 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11043 // iterates on indicated elements and get elements by back references from their nodes
11044 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11046 for ( ielem = 1; elemItr != theElems.end(); ++elemItr )
11048 MESSAGE("element " << ielem++);
11049 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11052 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11053 while ( nodeItr->more() )
11055 MESSAGE(" noeud ");
11056 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11057 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11059 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11060 while ( backElemItr->more() )
11062 MESSAGE(" backelem ");
11063 const SMDS_MeshElement* curElem = backElemItr->next();
11064 if ( curElem && theElems.find(curElem) == theElems.end() &&
11066 isInside( curElem, *bsc3d, aTol ) :
11067 isInside( curElem, *aFaceClassifier, aTol )))
11068 theAffectedElems.insert( curElem );
11076 //================================================================================
11078 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11079 \param theElems - group of of elements (edges or faces) to be replicated
11080 \param theNodesNot - group of nodes not to replicate
11081 \param theShape - shape to detect affected elements (element which geometric center
11082 located on or inside shape).
11083 The replicated nodes should be associated to affected elements.
11084 \return TRUE if operation has been completed successfully, FALSE otherwise
11086 //================================================================================
11088 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11089 const TIDSortedElemSet& theNodesNot,
11090 const TopoDS_Shape& theShape )
11092 if ( theShape.IsNull() )
11095 const double aTol = Precision::Confusion();
11096 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11097 auto_ptr<_FaceClassifier> aFaceClassifier;
11098 if ( theShape.ShapeType() == TopAbs_SOLID )
11100 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11101 bsc3d->PerformInfinitePoint(aTol);
11103 else if (theShape.ShapeType() == TopAbs_FACE )
11105 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11108 // iterates on indicated elements and get elements by back references from their nodes
11109 TIDSortedElemSet anAffected;
11110 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11111 for ( ; elemItr != theElems.end(); ++elemItr )
11113 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11117 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11118 while ( nodeItr->more() )
11120 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11121 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11123 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11124 while ( backElemItr->more() )
11126 const SMDS_MeshElement* curElem = backElemItr->next();
11127 if ( curElem && theElems.find(curElem) == theElems.end() &&
11129 isInside( curElem, *bsc3d, aTol ) :
11130 isInside( curElem, *aFaceClassifier, aTol )))
11131 anAffected.insert( curElem );
11135 return DoubleNodes( theElems, theNodesNot, anAffected );
11139 * \brief compute an oriented angle between two planes defined by four points.
11140 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11141 * @param p0 base of the rotation axe
11142 * @param p1 extremity of the rotation axe
11143 * @param g1 belongs to the first plane
11144 * @param g2 belongs to the second plane
11146 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11148 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11149 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11150 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11151 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11152 gp_Vec vref(p0, p1);
11155 gp_Vec n1 = vref.Crossed(v1);
11156 gp_Vec n2 = vref.Crossed(v2);
11158 return n2.AngleWithRef(n1, vref);
11160 catch ( Standard_Failure ) {
11162 return Max( v1.Magnitude(), v2.Magnitude() );
11166 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11167 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11168 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11169 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11170 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11171 * 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.
11172 * 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.
11173 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11174 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11175 * \param theElems - list of groups of volumes, where a group of volume is a set of
11176 * SMDS_MeshElements sorted by Id.
11177 * \param createJointElems - if TRUE, create the elements
11178 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11179 * the boundary between \a theDomains and the rest mesh
11180 * \return TRUE if operation has been completed successfully, FALSE otherwise
11182 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11183 bool createJointElems,
11184 bool onAllBoundaries)
11186 MESSAGE("----------------------------------------------");
11187 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11188 MESSAGE("----------------------------------------------");
11190 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11191 meshDS->BuildDownWardConnectivity(true);
11193 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11195 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11196 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11197 // build the list of nodes shared by 2 or more domains, with their domain indexes
11199 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11200 std::map<int,int>celldom; // cell vtkId --> domain
11201 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11202 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11203 faceDomains.clear();
11205 cellDomains.clear();
11206 nodeDomains.clear();
11207 std::map<int,int> emptyMap;
11208 std::set<int> emptySet;
11211 MESSAGE(".. Number of domains :"<<theElems.size());
11213 TIDSortedElemSet theRestDomElems;
11214 const int iRestDom = -1;
11215 const int idom0 = onAllBoundaries ? iRestDom : 0;
11216 const int nbDomains = theElems.size();
11218 // Check if the domains do not share an element
11219 for (int idom = 0; idom < nbDomains-1; idom++)
11221 // MESSAGE("... Check of domain #" << idom);
11222 const TIDSortedElemSet& domain = theElems[idom];
11223 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11224 for (; elemItr != domain.end(); ++elemItr)
11226 const SMDS_MeshElement* anElem = *elemItr;
11227 int idombisdeb = idom + 1 ;
11228 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
11230 const TIDSortedElemSet& domainbis = theElems[idombis];
11231 if ( domainbis.count(anElem) )
11233 MESSAGE(".... Domain #" << idom);
11234 MESSAGE(".... Domain #" << idombis);
11235 throw SALOME_Exception("The domains are not disjoint.");
11242 for (int idom = 0; idom < nbDomains; idom++)
11245 // --- build a map (face to duplicate --> volume to modify)
11246 // with all the faces shared by 2 domains (group of elements)
11247 // and corresponding volume of this domain, for each shared face.
11248 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11250 MESSAGE("... Neighbors of domain #" << idom);
11251 const TIDSortedElemSet& domain = theElems[idom];
11252 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11253 for (; elemItr != domain.end(); ++elemItr)
11255 const SMDS_MeshElement* anElem = *elemItr;
11258 int vtkId = anElem->getVtkId();
11259 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11260 int neighborsVtkIds[NBMAXNEIGHBORS];
11261 int downIds[NBMAXNEIGHBORS];
11262 unsigned char downTypes[NBMAXNEIGHBORS];
11263 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11264 for (int n = 0; n < nbNeighbors; n++)
11266 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11267 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11268 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11271 for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11273 // MESSAGE("Domain " << idombis);
11274 const TIDSortedElemSet& domainbis = theElems[idombis];
11275 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11277 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11279 DownIdType face(downIds[n], downTypes[n]);
11280 if (!faceDomains[face].count(idom))
11282 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11283 celldom[vtkId] = idom;
11284 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11288 theRestDomElems.insert( elem );
11289 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11290 celldom[neighborsVtkIds[n]] = iRestDom;
11298 //MESSAGE("Number of shared faces " << faceDomains.size());
11299 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11301 // --- explore the shared faces domain by domain,
11302 // explore the nodes of the face and see if they belong to a cell in the domain,
11303 // which has only a node or an edge on the border (not a shared face)
11305 for (int idomain = idom0; idomain < nbDomains; idomain++)
11307 //MESSAGE("Domain " << idomain);
11308 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11309 itface = faceDomains.begin();
11310 for (; itface != faceDomains.end(); ++itface)
11312 const std::map<int, int>& domvol = itface->second;
11313 if (!domvol.count(idomain))
11315 DownIdType face = itface->first;
11316 //MESSAGE(" --- face " << face.cellId);
11317 std::set<int> oldNodes;
11319 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11320 std::set<int>::iterator itn = oldNodes.begin();
11321 for (; itn != oldNodes.end(); ++itn)
11324 //MESSAGE(" node " << oldId);
11325 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11326 for (int i=0; i<l.ncells; i++)
11328 int vtkId = l.cells[i];
11329 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11330 if (!domain.count(anElem))
11332 int vtkType = grid->GetCellType(vtkId);
11333 int downId = grid->CellIdToDownId(vtkId);
11336 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11337 continue; // not OK at this stage of the algorithm:
11338 //no cells created after BuildDownWardConnectivity
11340 DownIdType aCell(downId, vtkType);
11341 cellDomains[aCell][idomain] = vtkId;
11342 celldom[vtkId] = idomain;
11343 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11349 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11350 // for each shared face, get the nodes
11351 // for each node, for each domain of the face, create a clone of the node
11353 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11354 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11355 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11357 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11358 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11359 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11361 MESSAGE(".. Duplication of the nodes");
11362 for (int idomain = idom0; idomain < nbDomains; idomain++)
11364 itface = faceDomains.begin();
11365 for (; itface != faceDomains.end(); ++itface)
11367 const std::map<int, int>& domvol = itface->second;
11368 if (!domvol.count(idomain))
11370 DownIdType face = itface->first;
11371 //MESSAGE(" --- face " << face.cellId);
11372 std::set<int> oldNodes;
11374 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11375 std::set<int>::iterator itn = oldNodes.begin();
11376 for (; itn != oldNodes.end(); ++itn)
11379 if (nodeDomains[oldId].empty())
11381 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11382 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11384 std::map<int, int>::const_iterator itdom = domvol.begin();
11385 for (; itdom != domvol.end(); ++itdom)
11387 int idom = itdom->first;
11388 //MESSAGE(" domain " << idom);
11389 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11391 if (nodeDomains[oldId].size() >= 2) // a multiple node
11393 vector<int> orderedDoms;
11394 //MESSAGE("multiple node " << oldId);
11395 if (mutipleNodes.count(oldId))
11396 orderedDoms = mutipleNodes[oldId];
11399 map<int,int>::iterator it = nodeDomains[oldId].begin();
11400 for (; it != nodeDomains[oldId].end(); ++it)
11401 orderedDoms.push_back(it->first);
11403 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11404 //stringstream txt;
11405 //for (int i=0; i<orderedDoms.size(); i++)
11406 // txt << orderedDoms[i] << " ";
11407 //MESSAGE("orderedDoms " << txt.str());
11408 mutipleNodes[oldId] = orderedDoms;
11410 double *coords = grid->GetPoint(oldId);
11411 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11412 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11413 int newId = newNode->getVtkId();
11414 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11415 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11422 MESSAGE(".. Creation of elements");
11423 for (int idomain = idom0; idomain < nbDomains; idomain++)
11425 itface = faceDomains.begin();
11426 for (; itface != faceDomains.end(); ++itface)
11428 std::map<int, int> domvol = itface->second;
11429 if (!domvol.count(idomain))
11431 DownIdType face = itface->first;
11432 //MESSAGE(" --- face " << face.cellId);
11433 std::set<int> oldNodes;
11435 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11436 int nbMultipleNodes = 0;
11437 std::set<int>::iterator itn = oldNodes.begin();
11438 for (; itn != oldNodes.end(); ++itn)
11441 if (mutipleNodes.count(oldId))
11444 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11446 //MESSAGE("multiple Nodes detected on a shared face");
11447 int downId = itface->first.cellId;
11448 unsigned char cellType = itface->first.cellType;
11449 // --- shared edge or shared face ?
11450 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11453 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11454 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11455 if (mutipleNodes.count(nodes[i]))
11456 if (!mutipleNodesToFace.count(nodes[i]))
11457 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11459 else // shared face (between two volumes)
11461 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11462 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11463 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11464 for (int ie =0; ie < nbEdges; ie++)
11467 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11468 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11470 vector<int> vn0 = mutipleNodes[nodes[0]];
11471 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11473 for (int i0 = 0; i0 < vn0.size(); i0++)
11474 for (int i1 = 0; i1 < vn1.size(); i1++)
11475 if (vn0[i0] == vn1[i1])
11476 doms.push_back(vn0[i0]);
11477 if (doms.size() >2)
11479 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11480 double *coords = grid->GetPoint(nodes[0]);
11481 gp_Pnt p0(coords[0], coords[1], coords[2]);
11482 coords = grid->GetPoint(nodes[nbNodes - 1]);
11483 gp_Pnt p1(coords[0], coords[1], coords[2]);
11485 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11486 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11487 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11488 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11489 for (int id=0; id < doms.size(); id++)
11491 int idom = doms[id];
11492 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11493 for (int ivol=0; ivol<nbvol; ivol++)
11495 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11496 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11497 if (domain.count(elem))
11499 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11500 domvol[idom] = svol;
11501 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11503 vtkIdType npts = 0;
11504 vtkIdType* pts = 0;
11505 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11506 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11509 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11510 angleDom[idom] = 0;
11514 gp_Pnt g(values[0], values[1], values[2]);
11515 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11516 //MESSAGE(" angle=" << angleDom[idom]);
11522 map<double, int> sortedDom; // sort domains by angle
11523 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11524 sortedDom[ia->second] = ia->first;
11525 vector<int> vnodes;
11527 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11529 vdom.push_back(ib->second);
11530 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11532 for (int ino = 0; ino < nbNodes; ino++)
11533 vnodes.push_back(nodes[ino]);
11534 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11543 // --- iterate on shared faces (volumes to modify, face to extrude)
11544 // get node id's of the face (id SMDS = id VTK)
11545 // create flat element with old and new nodes if requested
11547 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11548 // (domain1 X domain2) = domain1 + MAXINT*domain2
11550 std::map<int, std::map<long,int> > nodeQuadDomains;
11551 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11553 MESSAGE(".. Creation of elements: simple junction");
11554 if (createJointElems)
11557 string joints2DName = "joints2D";
11558 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11559 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11560 string joints3DName = "joints3D";
11561 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11562 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11564 itface = faceDomains.begin();
11565 for (; itface != faceDomains.end(); ++itface)
11567 DownIdType face = itface->first;
11568 std::set<int> oldNodes;
11569 std::set<int>::iterator itn;
11571 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11573 std::map<int, int> domvol = itface->second;
11574 std::map<int, int>::iterator itdom = domvol.begin();
11575 int dom1 = itdom->first;
11576 int vtkVolId = itdom->second;
11578 int dom2 = itdom->first;
11579 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11581 stringstream grpname;
11584 grpname << dom1 << "_" << dom2;
11586 grpname << dom2 << "_" << dom1;
11587 string namegrp = grpname.str();
11588 if (!mapOfJunctionGroups.count(namegrp))
11589 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11590 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11592 sgrp->Add(vol->GetID());
11593 if (vol->GetType() == SMDSAbs_Volume)
11594 joints3DGrp->Add(vol->GetID());
11595 else if (vol->GetType() == SMDSAbs_Face)
11596 joints2DGrp->Add(vol->GetID());
11600 // --- create volumes on multiple domain intersection if requested
11601 // iterate on mutipleNodesToFace
11602 // iterate on edgesMultiDomains
11604 MESSAGE(".. Creation of elements: multiple junction");
11605 if (createJointElems)
11607 // --- iterate on mutipleNodesToFace
11609 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11610 for (; itn != mutipleNodesToFace.end(); ++itn)
11612 int node = itn->first;
11613 vector<int> orderDom = itn->second;
11614 vector<vtkIdType> orderedNodes;
11615 for (int idom = 0; idom <orderDom.size(); idom++)
11616 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11617 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11619 stringstream grpname;
11621 grpname << 0 << "_" << 0;
11623 string namegrp = grpname.str();
11624 if (!mapOfJunctionGroups.count(namegrp))
11625 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11626 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11628 sgrp->Add(face->GetID());
11631 // --- iterate on edgesMultiDomains
11633 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11634 for (; ite != edgesMultiDomains.end(); ++ite)
11636 vector<int> nodes = ite->first;
11637 vector<int> orderDom = ite->second;
11638 vector<vtkIdType> orderedNodes;
11639 if (nodes.size() == 2)
11641 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11642 for (int ino=0; ino < nodes.size(); ino++)
11643 if (orderDom.size() == 3)
11644 for (int idom = 0; idom <orderDom.size(); idom++)
11645 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11647 for (int idom = orderDom.size()-1; idom >=0; idom--)
11648 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11649 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11652 string namegrp = "jointsMultiples";
11653 if (!mapOfJunctionGroups.count(namegrp))
11654 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11655 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11657 sgrp->Add(vol->GetID());
11661 //INFOS("Quadratic multiple joints not implemented");
11662 // TODO quadratic nodes
11667 // --- list the explicit faces and edges of the mesh that need to be modified,
11668 // i.e. faces and edges built with one or more duplicated nodes.
11669 // associate these faces or edges to their corresponding domain.
11670 // only the first domain found is kept when a face or edge is shared
11672 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11673 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11674 faceOrEdgeDom.clear();
11677 MESSAGE(".. Modification of elements");
11678 for (int idomain = idom0; idomain < nbDomains; idomain++)
11680 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11681 for (; itnod != nodeDomains.end(); ++itnod)
11683 int oldId = itnod->first;
11684 //MESSAGE(" node " << oldId);
11685 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11686 for (int i = 0; i < l.ncells; i++)
11688 int vtkId = l.cells[i];
11689 int vtkType = grid->GetCellType(vtkId);
11690 int downId = grid->CellIdToDownId(vtkId);
11692 continue; // new cells: not to be modified
11693 DownIdType aCell(downId, vtkType);
11694 int volParents[1000];
11695 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11696 for (int j = 0; j < nbvol; j++)
11697 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11698 if (!feDom.count(vtkId))
11700 feDom[vtkId] = idomain;
11701 faceOrEdgeDom[aCell] = emptyMap;
11702 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11703 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11704 // << " type " << vtkType << " downId " << downId);
11710 // --- iterate on shared faces (volumes to modify, face to extrude)
11711 // get node id's of the face
11712 // replace old nodes by new nodes in volumes, and update inverse connectivity
11714 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11715 for (int m=0; m<3; m++)
11717 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11718 itface = (*amap).begin();
11719 for (; itface != (*amap).end(); ++itface)
11721 DownIdType face = itface->first;
11722 std::set<int> oldNodes;
11723 std::set<int>::iterator itn;
11725 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11726 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11727 std::map<int, int> localClonedNodeIds;
11729 std::map<int, int> domvol = itface->second;
11730 std::map<int, int>::iterator itdom = domvol.begin();
11731 for (; itdom != domvol.end(); ++itdom)
11733 int idom = itdom->first;
11734 int vtkVolId = itdom->second;
11735 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11736 localClonedNodeIds.clear();
11737 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11740 if (nodeDomains[oldId].count(idom))
11742 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11743 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11746 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11751 // Remove empty groups (issue 0022812)
11752 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11753 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11755 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11756 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11759 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11760 grid->BuildLinks();
11768 * \brief Double nodes on some external faces and create flat elements.
11769 * Flat elements are mainly used by some types of mechanic calculations.
11771 * Each group of the list must be constituted of faces.
11772 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11773 * @param theElems - list of groups of faces, where a group of faces is a set of
11774 * SMDS_MeshElements sorted by Id.
11775 * @return TRUE if operation has been completed successfully, FALSE otherwise
11777 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11779 MESSAGE("-------------------------------------------------");
11780 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11781 MESSAGE("-------------------------------------------------");
11783 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11785 // --- For each group of faces
11786 // duplicate the nodes, create a flat element based on the face
11787 // replace the nodes of the faces by their clones
11789 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11790 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11791 clonedNodes.clear();
11792 intermediateNodes.clear();
11793 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11794 mapOfJunctionGroups.clear();
11796 for (int idom = 0; idom < theElems.size(); idom++)
11798 const TIDSortedElemSet& domain = theElems[idom];
11799 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11800 for (; elemItr != domain.end(); ++elemItr)
11802 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11803 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11806 // MESSAGE("aFace=" << aFace->GetID());
11807 bool isQuad = aFace->IsQuadratic();
11808 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11810 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11812 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11813 while (nodeIt->more())
11815 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11816 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11818 ln2.push_back(node);
11820 ln0.push_back(node);
11822 const SMDS_MeshNode* clone = 0;
11823 if (!clonedNodes.count(node))
11825 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11826 copyPosition( node, clone );
11827 clonedNodes[node] = clone;
11830 clone = clonedNodes[node];
11833 ln3.push_back(clone);
11835 ln1.push_back(clone);
11837 const SMDS_MeshNode* inter = 0;
11838 if (isQuad && (!isMedium))
11840 if (!intermediateNodes.count(node))
11842 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11843 copyPosition( node, inter );
11844 intermediateNodes[node] = inter;
11847 inter = intermediateNodes[node];
11848 ln4.push_back(inter);
11852 // --- extrude the face
11854 vector<const SMDS_MeshNode*> ln;
11855 SMDS_MeshVolume* vol = 0;
11856 vtkIdType aType = aFace->GetVtkType();
11860 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11861 // MESSAGE("vol prism " << vol->GetID());
11862 ln.push_back(ln1[0]);
11863 ln.push_back(ln1[1]);
11864 ln.push_back(ln1[2]);
11867 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11868 // MESSAGE("vol hexa " << vol->GetID());
11869 ln.push_back(ln1[0]);
11870 ln.push_back(ln1[1]);
11871 ln.push_back(ln1[2]);
11872 ln.push_back(ln1[3]);
11874 case VTK_QUADRATIC_TRIANGLE:
11875 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11876 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11877 // MESSAGE("vol quad prism " << vol->GetID());
11878 ln.push_back(ln1[0]);
11879 ln.push_back(ln1[1]);
11880 ln.push_back(ln1[2]);
11881 ln.push_back(ln3[0]);
11882 ln.push_back(ln3[1]);
11883 ln.push_back(ln3[2]);
11885 case VTK_QUADRATIC_QUAD:
11886 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11887 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11888 // ln4[0], ln4[1], ln4[2], ln4[3]);
11889 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11890 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11891 ln4[0], ln4[1], ln4[2], ln4[3]);
11892 // MESSAGE("vol quad hexa " << vol->GetID());
11893 ln.push_back(ln1[0]);
11894 ln.push_back(ln1[1]);
11895 ln.push_back(ln1[2]);
11896 ln.push_back(ln1[3]);
11897 ln.push_back(ln3[0]);
11898 ln.push_back(ln3[1]);
11899 ln.push_back(ln3[2]);
11900 ln.push_back(ln3[3]);
11910 stringstream grpname;
11914 string namegrp = grpname.str();
11915 if (!mapOfJunctionGroups.count(namegrp))
11916 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11917 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11919 sgrp->Add(vol->GetID());
11922 // --- modify the face
11924 aFace->ChangeNodes(&ln[0], ln.size());
11931 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11932 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11933 * groups of faces to remove inside the object, (idem edges).
11934 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11936 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11937 const TopoDS_Shape& theShape,
11938 SMESH_NodeSearcher* theNodeSearcher,
11939 const char* groupName,
11940 std::vector<double>& nodesCoords,
11941 std::vector<std::vector<int> >& listOfListOfNodes)
11943 MESSAGE("--------------------------------");
11944 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11945 MESSAGE("--------------------------------");
11947 // --- zone of volumes to remove is given :
11948 // 1 either by a geom shape (one or more vertices) and a radius,
11949 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11950 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11951 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11952 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11953 // defined by it's name.
11955 SMESHDS_GroupBase* groupDS = 0;
11956 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11957 while ( groupIt->more() )
11960 SMESH_Group * group = groupIt->next();
11961 if ( !group ) continue;
11962 groupDS = group->GetGroupDS();
11963 if ( !groupDS || groupDS->IsEmpty() ) continue;
11964 std::string grpName = group->GetName();
11965 //MESSAGE("grpName=" << grpName);
11966 if (grpName == groupName)
11972 bool isNodeGroup = false;
11973 bool isNodeCoords = false;
11976 if (groupDS->GetType() != SMDSAbs_Node)
11978 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11981 if (nodesCoords.size() > 0)
11982 isNodeCoords = true; // a list o nodes given by their coordinates
11983 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11985 // --- define groups to build
11987 int idg; // --- group of SMDS volumes
11988 string grpvName = groupName;
11989 grpvName += "_vol";
11990 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11993 MESSAGE("group not created " << grpvName);
11996 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11998 int idgs; // --- group of SMDS faces on the skin
11999 string grpsName = groupName;
12000 grpsName += "_skin";
12001 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12004 MESSAGE("group not created " << grpsName);
12007 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12009 int idgi; // --- group of SMDS faces internal (several shapes)
12010 string grpiName = groupName;
12011 grpiName += "_internalFaces";
12012 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12015 MESSAGE("group not created " << grpiName);
12018 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12020 int idgei; // --- group of SMDS faces internal (several shapes)
12021 string grpeiName = groupName;
12022 grpeiName += "_internalEdges";
12023 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12026 MESSAGE("group not created " << grpeiName);
12029 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12031 // --- build downward connectivity
12033 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12034 meshDS->BuildDownWardConnectivity(true);
12035 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12037 // --- set of volumes detected inside
12039 std::set<int> setOfInsideVol;
12040 std::set<int> setOfVolToCheck;
12042 std::vector<gp_Pnt> gpnts;
12045 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12047 MESSAGE("group of nodes provided");
12048 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12049 while ( elemIt->more() )
12051 const SMDS_MeshElement* elem = elemIt->next();
12054 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12057 SMDS_MeshElement* vol = 0;
12058 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12059 while (volItr->more())
12061 vol = (SMDS_MeshElement*)volItr->next();
12062 setOfInsideVol.insert(vol->getVtkId());
12063 sgrp->Add(vol->GetID());
12067 else if (isNodeCoords)
12069 MESSAGE("list of nodes coordinates provided");
12072 while (i < nodesCoords.size()-2)
12074 double x = nodesCoords[i++];
12075 double y = nodesCoords[i++];
12076 double z = nodesCoords[i++];
12077 gp_Pnt p = gp_Pnt(x, y ,z);
12078 gpnts.push_back(p);
12079 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12083 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12085 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12086 TopTools_IndexedMapOfShape vertexMap;
12087 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12088 gp_Pnt p = gp_Pnt(0,0,0);
12089 if (vertexMap.Extent() < 1)
12092 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12094 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12095 p = BRep_Tool::Pnt(vertex);
12096 gpnts.push_back(p);
12097 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12101 if (gpnts.size() > 0)
12104 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12106 nodeId = startNode->GetID();
12107 MESSAGE("nodeId " << nodeId);
12109 double radius2 = radius*radius;
12110 MESSAGE("radius2 " << radius2);
12112 // --- volumes on start node
12114 setOfVolToCheck.clear();
12115 SMDS_MeshElement* startVol = 0;
12116 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12117 while (volItr->more())
12119 startVol = (SMDS_MeshElement*)volItr->next();
12120 setOfVolToCheck.insert(startVol->getVtkId());
12122 if (setOfVolToCheck.empty())
12124 MESSAGE("No volumes found");
12128 // --- starting with central volumes then their neighbors, check if they are inside
12129 // or outside the domain, until no more new neighbor volume is inside.
12130 // Fill the group of inside volumes
12132 std::map<int, double> mapOfNodeDistance2;
12133 mapOfNodeDistance2.clear();
12134 std::set<int> setOfOutsideVol;
12135 while (!setOfVolToCheck.empty())
12137 std::set<int>::iterator it = setOfVolToCheck.begin();
12139 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12140 bool volInside = false;
12141 vtkIdType npts = 0;
12142 vtkIdType* pts = 0;
12143 grid->GetCellPoints(vtkId, npts, pts);
12144 for (int i=0; i<npts; i++)
12146 double distance2 = 0;
12147 if (mapOfNodeDistance2.count(pts[i]))
12149 distance2 = mapOfNodeDistance2[pts[i]];
12150 MESSAGE("point " << pts[i] << " distance2 " << distance2);
12154 double *coords = grid->GetPoint(pts[i]);
12155 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12157 for (int j=0; j<gpnts.size(); j++)
12159 double d2 = aPoint.SquareDistance(gpnts[j]);
12160 if (d2 < distance2)
12163 if (distance2 < radius2)
12167 mapOfNodeDistance2[pts[i]] = distance2;
12168 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12170 if (distance2 < radius2)
12172 volInside = true; // one or more nodes inside the domain
12173 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12179 setOfInsideVol.insert(vtkId);
12180 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12181 int neighborsVtkIds[NBMAXNEIGHBORS];
12182 int downIds[NBMAXNEIGHBORS];
12183 unsigned char downTypes[NBMAXNEIGHBORS];
12184 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12185 for (int n = 0; n < nbNeighbors; n++)
12186 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12187 setOfVolToCheck.insert(neighborsVtkIds[n]);
12191 setOfOutsideVol.insert(vtkId);
12192 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12194 setOfVolToCheck.erase(vtkId);
12198 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12199 // If yes, add the volume to the inside set
12201 bool addedInside = true;
12202 std::set<int> setOfVolToReCheck;
12203 while (addedInside)
12205 MESSAGE(" --------------------------- re check");
12206 addedInside = false;
12207 std::set<int>::iterator itv = setOfInsideVol.begin();
12208 for (; itv != setOfInsideVol.end(); ++itv)
12211 int neighborsVtkIds[NBMAXNEIGHBORS];
12212 int downIds[NBMAXNEIGHBORS];
12213 unsigned char downTypes[NBMAXNEIGHBORS];
12214 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12215 for (int n = 0; n < nbNeighbors; n++)
12216 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12217 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12219 setOfVolToCheck = setOfVolToReCheck;
12220 setOfVolToReCheck.clear();
12221 while (!setOfVolToCheck.empty())
12223 std::set<int>::iterator it = setOfVolToCheck.begin();
12225 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12227 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12228 int countInside = 0;
12229 int neighborsVtkIds[NBMAXNEIGHBORS];
12230 int downIds[NBMAXNEIGHBORS];
12231 unsigned char downTypes[NBMAXNEIGHBORS];
12232 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12233 for (int n = 0; n < nbNeighbors; n++)
12234 if (setOfInsideVol.count(neighborsVtkIds[n]))
12236 MESSAGE("countInside " << countInside);
12237 if (countInside > 1)
12239 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12240 setOfInsideVol.insert(vtkId);
12241 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12242 addedInside = true;
12245 setOfVolToReCheck.insert(vtkId);
12247 setOfVolToCheck.erase(vtkId);
12251 // --- map of Downward faces at the boundary, inside the global volume
12252 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12253 // fill group of SMDS faces inside the volume (when several volume shapes)
12254 // fill group of SMDS faces on the skin of the global volume (if skin)
12256 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12257 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12258 std::set<int>::iterator it = setOfInsideVol.begin();
12259 for (; it != setOfInsideVol.end(); ++it)
12262 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12263 int neighborsVtkIds[NBMAXNEIGHBORS];
12264 int downIds[NBMAXNEIGHBORS];
12265 unsigned char downTypes[NBMAXNEIGHBORS];
12266 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12267 for (int n = 0; n < nbNeighbors; n++)
12269 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12270 if (neighborDim == 3)
12272 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12274 DownIdType face(downIds[n], downTypes[n]);
12275 boundaryFaces[face] = vtkId;
12277 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12278 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12279 if (vtkFaceId >= 0)
12281 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12282 // find also the smds edges on this face
12283 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12284 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12285 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12286 for (int i = 0; i < nbEdges; i++)
12288 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12289 if (vtkEdgeId >= 0)
12290 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12294 else if (neighborDim == 2) // skin of the volume
12296 DownIdType face(downIds[n], downTypes[n]);
12297 skinFaces[face] = vtkId;
12298 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12299 if (vtkFaceId >= 0)
12300 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12305 // --- identify the edges constituting the wire of each subshape on the skin
12306 // define polylines with the nodes of edges, equivalent to wires
12307 // project polylines on subshapes, and partition, to get geom faces
12309 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12310 std::set<int> emptySet;
12312 std::set<int> shapeIds;
12314 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12315 while (itelem->more())
12317 const SMDS_MeshElement *elem = itelem->next();
12318 int shapeId = elem->getshapeId();
12319 int vtkId = elem->getVtkId();
12320 if (!shapeIdToVtkIdSet.count(shapeId))
12322 shapeIdToVtkIdSet[shapeId] = emptySet;
12323 shapeIds.insert(shapeId);
12325 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12328 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12329 std::set<DownIdType, DownIdCompare> emptyEdges;
12330 emptyEdges.clear();
12332 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12333 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12335 int shapeId = itShape->first;
12336 MESSAGE(" --- Shape ID --- "<< shapeId);
12337 shapeIdToEdges[shapeId] = emptyEdges;
12339 std::vector<int> nodesEdges;
12341 std::set<int>::iterator its = itShape->second.begin();
12342 for (; its != itShape->second.end(); ++its)
12345 MESSAGE(" " << vtkId);
12346 int neighborsVtkIds[NBMAXNEIGHBORS];
12347 int downIds[NBMAXNEIGHBORS];
12348 unsigned char downTypes[NBMAXNEIGHBORS];
12349 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12350 for (int n = 0; n < nbNeighbors; n++)
12352 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12354 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12355 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12356 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12358 DownIdType edge(downIds[n], downTypes[n]);
12359 if (!shapeIdToEdges[shapeId].count(edge))
12361 shapeIdToEdges[shapeId].insert(edge);
12363 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12364 nodesEdges.push_back(vtkNodeId[0]);
12365 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12366 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12372 std::list<int> order;
12374 if (nodesEdges.size() > 0)
12376 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12377 nodesEdges[0] = -1;
12378 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12379 nodesEdges[1] = -1; // do not reuse this edge
12383 int nodeTofind = order.back(); // try first to push back
12385 for (i = 0; i<nodesEdges.size(); i++)
12386 if (nodesEdges[i] == nodeTofind)
12388 if (i == nodesEdges.size())
12389 found = false; // no follower found on back
12392 if (i%2) // odd ==> use the previous one
12393 if (nodesEdges[i-1] < 0)
12397 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12398 nodesEdges[i-1] = -1;
12400 else // even ==> use the next one
12401 if (nodesEdges[i+1] < 0)
12405 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12406 nodesEdges[i+1] = -1;
12411 // try to push front
12413 nodeTofind = order.front(); // try to push front
12414 for (i = 0; i<nodesEdges.size(); i++)
12415 if (nodesEdges[i] == nodeTofind)
12417 if (i == nodesEdges.size())
12419 found = false; // no predecessor found on front
12422 if (i%2) // odd ==> use the previous one
12423 if (nodesEdges[i-1] < 0)
12427 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12428 nodesEdges[i-1] = -1;
12430 else // even ==> use the next one
12431 if (nodesEdges[i+1] < 0)
12435 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12436 nodesEdges[i+1] = -1;
12442 std::vector<int> nodes;
12443 nodes.push_back(shapeId);
12444 std::list<int>::iterator itl = order.begin();
12445 for (; itl != order.end(); itl++)
12447 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12448 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12450 listOfListOfNodes.push_back(nodes);
12453 // partition geom faces with blocFissure
12454 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12455 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12461 //================================================================================
12463 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12464 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12465 * \return TRUE if operation has been completed successfully, FALSE otherwise
12467 //================================================================================
12469 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12471 // iterates on volume elements and detect all free faces on them
12472 SMESHDS_Mesh* aMesh = GetMeshDS();
12476 ElemFeatures faceType( SMDSAbs_Face );
12477 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12478 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12481 const SMDS_MeshVolume* volume = vIt->next();
12482 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12483 vTool.SetExternalNormal();
12484 const int iQuad = volume->IsQuadratic();
12485 faceType.SetQuad( iQuad );
12486 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12488 if (!vTool.IsFreeFace(iface))
12491 vector<const SMDS_MeshNode *> nodes;
12492 int nbFaceNodes = vTool.NbFaceNodes(iface);
12493 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12495 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12496 nodes.push_back(faceNodes[inode]);
12498 if (iQuad) // add medium nodes
12500 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12501 nodes.push_back(faceNodes[inode]);
12502 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12503 nodes.push_back(faceNodes[8]);
12505 // add new face based on volume nodes
12506 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12508 nbExisted++; // face already exsist
12512 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12517 return ( nbFree == ( nbExisted + nbCreated ));
12522 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12524 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12526 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12529 //================================================================================
12531 * \brief Creates missing boundary elements
12532 * \param elements - elements whose boundary is to be checked
12533 * \param dimension - defines type of boundary elements to create
12534 * \param group - a group to store created boundary elements in
12535 * \param targetMesh - a mesh to store created boundary elements in
12536 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12537 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12538 * boundary elements will be copied into the targetMesh
12539 * \param toAddExistingBondary - if true, not only new but also pre-existing
12540 * boundary elements will be added into the new group
12541 * \param aroundElements - if true, elements will be created on boundary of given
12542 * elements else, on boundary of the whole mesh.
12543 * \return nb of added boundary elements
12545 //================================================================================
12547 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12548 Bnd_Dimension dimension,
12549 SMESH_Group* group/*=0*/,
12550 SMESH_Mesh* targetMesh/*=0*/,
12551 bool toCopyElements/*=false*/,
12552 bool toCopyExistingBoundary/*=false*/,
12553 bool toAddExistingBondary/*= false*/,
12554 bool aroundElements/*= false*/)
12556 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12557 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12558 // hope that all elements are of the same type, do not check them all
12559 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12560 throw SALOME_Exception(LOCALIZED("wrong element type"));
12563 toCopyElements = toCopyExistingBoundary = false;
12565 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12566 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12567 int nbAddedBnd = 0;
12569 // editor adding present bnd elements and optionally holding elements to add to the group
12570 SMESH_MeshEditor* presentEditor;
12571 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12572 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12574 SMESH_MesherHelper helper( *myMesh );
12575 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12576 SMDS_VolumeTool vTool;
12577 TIDSortedElemSet avoidSet;
12578 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12581 typedef vector<const SMDS_MeshNode*> TConnectivity;
12582 TConnectivity tgtNodes;
12583 ElemFeatures elemKind( missType ), elemToCopy;
12585 vector<const SMDS_MeshElement*> presentBndElems;
12586 vector<TConnectivity> missingBndElems;
12587 vector<int> freeFacets;
12588 TConnectivity nodes, elemNodes;
12590 SMDS_ElemIteratorPtr eIt;
12591 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12592 else eIt = elemSetIterator( elements );
12594 while (eIt->more())
12596 const SMDS_MeshElement* elem = eIt->next();
12597 const int iQuad = elem->IsQuadratic();
12598 elemKind.SetQuad( iQuad );
12600 // ------------------------------------------------------------------------------------
12601 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12602 // ------------------------------------------------------------------------------------
12603 presentBndElems.clear();
12604 missingBndElems.clear();
12605 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12606 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12608 const SMDS_MeshElement* otherVol = 0;
12609 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12611 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12612 ( !aroundElements || elements.count( otherVol )))
12614 freeFacets.push_back( iface );
12616 if ( missType == SMDSAbs_Face )
12617 vTool.SetExternalNormal();
12618 for ( size_t i = 0; i < freeFacets.size(); ++i )
12620 int iface = freeFacets[i];
12621 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12622 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12623 if ( missType == SMDSAbs_Edge ) // boundary edges
12625 nodes.resize( 2+iQuad );
12626 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12628 for ( int j = 0; j < nodes.size(); ++j )
12629 nodes[j] = nn[ i+j ];
12630 if ( const SMDS_MeshElement* edge =
12631 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12632 presentBndElems.push_back( edge );
12634 missingBndElems.push_back( nodes );
12637 else // boundary face
12640 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12641 nodes.push_back( nn[inode] ); // add corner nodes
12643 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12644 nodes.push_back( nn[inode] ); // add medium nodes
12645 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12647 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12649 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12650 SMDSAbs_Face, /*noMedium=*/false ))
12651 presentBndElems.push_back( f );
12653 missingBndElems.push_back( nodes );
12655 if ( targetMesh != myMesh )
12657 // add 1D elements on face boundary to be added to a new mesh
12658 const SMDS_MeshElement* edge;
12659 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12662 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12664 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12665 if ( edge && avoidSet.insert( edge ).second )
12666 presentBndElems.push_back( edge );
12672 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12674 avoidSet.clear(), avoidSet.insert( elem );
12675 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12676 SMDS_MeshElement::iterator() );
12677 elemNodes.push_back( elemNodes[0] );
12678 nodes.resize( 2 + iQuad );
12679 const int nbLinks = elem->NbCornerNodes();
12680 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12682 nodes[0] = elemNodes[iN];
12683 nodes[1] = elemNodes[iN+1+iQuad];
12684 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12685 continue; // not free link
12687 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12688 if ( const SMDS_MeshElement* edge =
12689 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12690 presentBndElems.push_back( edge );
12692 missingBndElems.push_back( nodes );
12696 // ---------------------------------
12697 // 2. Add missing boundary elements
12698 // ---------------------------------
12699 if ( targetMesh != myMesh )
12700 // instead of making a map of nodes in this mesh and targetMesh,
12701 // we create nodes with same IDs.
12702 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12704 TConnectivity& srcNodes = missingBndElems[i];
12705 tgtNodes.resize( srcNodes.size() );
12706 for ( inode = 0; inode < srcNodes.size(); ++inode )
12707 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12708 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12710 /*noMedium=*/false))
12712 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12716 for ( int i = 0; i < missingBndElems.size(); ++i )
12718 TConnectivity& nodes = missingBndElems[i];
12719 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12721 /*noMedium=*/false))
12723 SMDS_MeshElement* newElem =
12724 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12725 nbAddedBnd += bool( newElem );
12727 // try to set a new element to a shape
12728 if ( myMesh->HasShapeToMesh() )
12731 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12732 const size_t nbN = nodes.size() / (iQuad+1 );
12733 for ( inode = 0; inode < nbN && ok; ++inode )
12735 pair<int, TopAbs_ShapeEnum> i_stype =
12736 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12737 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12738 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12740 if ( ok && mediumShapes.size() > 1 )
12742 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12743 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12744 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12746 if (( ok = ( stype_i->first != stype_i_0.first )))
12747 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12748 aMesh->IndexToShape( stype_i_0.second ));
12751 if ( ok && mediumShapes.begin()->first == missShapeType )
12752 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12756 // ----------------------------------
12757 // 3. Copy present boundary elements
12758 // ----------------------------------
12759 if ( toCopyExistingBoundary )
12760 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12762 const SMDS_MeshElement* e = presentBndElems[i];
12763 tgtNodes.resize( e->NbNodes() );
12764 for ( inode = 0; inode < nodes.size(); ++inode )
12765 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12766 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12768 else // store present elements to add them to a group
12769 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12771 presentEditor->myLastCreatedElems.Append( presentBndElems[i] );
12774 } // loop on given elements
12776 // ---------------------------------------------
12777 // 4. Fill group with boundary elements
12778 // ---------------------------------------------
12781 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12782 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12783 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12785 tgtEditor.myLastCreatedElems.Clear();
12786 tgtEditor2.myLastCreatedElems.Clear();
12788 // -----------------------
12789 // 5. Copy given elements
12790 // -----------------------
12791 if ( toCopyElements && targetMesh != myMesh )
12793 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12794 else eIt = elemSetIterator( elements );
12795 while (eIt->more())
12797 const SMDS_MeshElement* elem = eIt->next();
12798 tgtNodes.resize( elem->NbNodes() );
12799 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12800 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12801 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12803 tgtEditor.myLastCreatedElems.Clear();
12809 //================================================================================
12811 * \brief Copy node position and set \a to node on the same geometry
12813 //================================================================================
12815 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12816 const SMDS_MeshNode* to )
12818 if ( !from || !to ) return;
12820 SMDS_PositionPtr pos = from->GetPosition();
12821 if ( !pos || from->getshapeId() < 1 ) return;
12823 switch ( pos->GetTypeOfPosition() )
12825 case SMDS_TOP_3DSPACE: break;
12827 case SMDS_TOP_FACE:
12829 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12830 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12831 fPos->GetUParameter(), fPos->GetVParameter() );
12834 case SMDS_TOP_EDGE:
12836 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12837 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12838 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12841 case SMDS_TOP_VERTEX:
12843 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12846 case SMDS_TOP_UNSPEC: