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() ) / 2 );
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( 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();
6483 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6484 LinearAngleVariation(aNbTP-1, theAngles);
6485 // fill vector of path points with angles
6486 vector<SMESH_MeshEditor_PathPoint> aPPs;
6487 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6488 list<double>::iterator itAngles = theAngles.begin();
6489 aPPs.push_back( *itPP++ );
6490 for( ; itPP != fullList.end(); itPP++) {
6491 aPPs.push_back( *itPP );
6492 if ( theHasAngles && itAngles != theAngles.end() )
6493 aPPs.back().SetAngle( *itAngles++ );
6496 TNodeOfNodeListMap mapNewNodes;
6497 TElemOfVecOfNnlmiMap mapElemNewNodes;
6498 TTElemOfElemListMap newElemsMap;
6499 TIDSortedElemSet::iterator itElem;
6500 // source elements for each generated one
6501 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6503 // 3. Center of rotation aV0
6504 gp_Pnt aV0 = theRefPoint;
6505 if ( !theHasRefPoint )
6507 gp_XYZ aGC( 0.,0.,0. );
6508 TIDSortedElemSet newNodes;
6510 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6512 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6513 itElem = theElements.begin();
6514 for ( ; itElem != theElements.end(); itElem++ ) {
6515 const SMDS_MeshElement* elem = *itElem;
6517 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6518 while ( itN->more() ) {
6519 const SMDS_MeshElement* node = itN->next();
6520 if ( newNodes.insert( node ).second )
6521 aGC += SMESH_TNodeXYZ( node );
6525 aGC /= newNodes.size();
6527 } // if (!theHasRefPoint) {
6529 // 4. Processing the elements
6530 SMESHDS_Mesh* aMesh = GetMeshDS();
6532 setElemsFirst( theElemSets );
6533 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6535 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6536 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
6537 // check element type
6538 const SMDS_MeshElement* elem = *itElem;
6541 // SMDSAbs_ElementType aTypeE = elem->GetType();
6542 // if ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge )
6545 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6546 newNodesItVec.reserve( elem->NbNodes() );
6548 // loop on elem nodes
6550 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6551 while ( itN->more() )
6554 // check if a node has been already processed
6555 const SMDS_MeshNode* node =
6556 static_cast<const SMDS_MeshNode*>( itN->next() );
6557 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
6558 if ( nIt == mapNewNodes.end() ) {
6559 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6560 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6563 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6564 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6565 gp_Ax1 anAx1, anAxT1T0;
6566 gp_Dir aDT1x, aDT0x, aDT1T0;
6571 aPN0 = SMESH_TNodeXYZ( node );
6573 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6575 aDT0x= aPP0.Tangent();
6576 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
6578 for ( int j = 1; j < aNbTP; ++j ) {
6579 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6581 aDT1x = aPP1.Tangent();
6582 aAngle1x = aPP1.Angle();
6584 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6586 gp_Vec aV01x( aP0x, aP1x );
6587 aTrsf.SetTranslation( aV01x );
6590 aV1x = aV0x.Transformed( aTrsf );
6591 aPN1 = aPN0.Transformed( aTrsf );
6593 // rotation 1 [ T1,T0 ]
6594 aAngleT1T0=-aDT1x.Angle( aDT0x );
6595 if (fabs(aAngleT1T0) > aTolAng) {
6597 anAxT1T0.SetLocation( aV1x );
6598 anAxT1T0.SetDirection( aDT1T0 );
6599 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6601 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6605 if ( theHasAngles ) {
6606 anAx1.SetLocation( aV1x );
6607 anAx1.SetDirection( aDT1x );
6608 aTrsfRot.SetRotation( anAx1, aAngle1x );
6610 aPN1 = aPN1.Transformed( aTrsfRot );
6614 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
6615 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6616 // create additional node
6617 double x = ( aPN1.X() + aPN0.X() )/2.;
6618 double y = ( aPN1.Y() + aPN0.Y() )/2.;
6619 double z = ( aPN1.Z() + aPN0.Z() )/2.;
6620 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6621 myLastCreatedNodes.Append(newNode);
6622 srcNodes.Append( node );
6623 listNewNodes.push_back( newNode );
6625 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6626 myLastCreatedNodes.Append(newNode);
6627 srcNodes.Append( node );
6628 listNewNodes.push_back( newNode );
6638 // if current elem is quadratic and current node is not medium
6639 // we have to check - may be it is needed to insert additional nodes
6640 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6641 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6642 if(listNewNodes.size()==aNbTP-1) {
6643 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6644 gp_XYZ P(node->X(), node->Y(), node->Z());
6645 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6647 for(i=0; i<aNbTP-1; i++) {
6648 const SMDS_MeshNode* N = *it;
6649 double x = ( N->X() + P.X() )/2.;
6650 double y = ( N->Y() + P.Y() )/2.;
6651 double z = ( N->Z() + P.Z() )/2.;
6652 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6653 srcNodes.Append( node );
6654 myLastCreatedNodes.Append(newN);
6657 P = gp_XYZ(N->X(),N->Y(),N->Z());
6659 listNewNodes.clear();
6660 for(i=0; i<2*(aNbTP-1); i++) {
6661 listNewNodes.push_back(aNodes[i]);
6667 newNodesItVec.push_back( nIt );
6669 // make new elements
6670 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6671 // newNodesItVec[0]->second.size(), myLastCreatedElems );
6672 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6676 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6678 if ( theMakeGroups )
6679 generateGroups( srcNodes, srcElems, "extruded");
6685 //=======================================================================
6686 //function : LinearAngleVariation
6687 //purpose : auxilary for ExtrusionAlongTrack
6688 //=======================================================================
6689 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6690 list<double>& Angles)
6692 int nbAngles = Angles.size();
6693 if( nbSteps > nbAngles ) {
6694 vector<double> theAngles(nbAngles);
6695 list<double>::iterator it = Angles.begin();
6697 for(; it!=Angles.end(); it++) {
6699 theAngles[i] = (*it);
6702 double rAn2St = double( nbAngles ) / double( nbSteps );
6703 double angPrev = 0, angle;
6704 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6705 double angCur = rAn2St * ( iSt+1 );
6706 double angCurFloor = floor( angCur );
6707 double angPrevFloor = floor( angPrev );
6708 if ( angPrevFloor == angCurFloor )
6709 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6711 int iP = int( angPrevFloor );
6712 double angPrevCeil = ceil(angPrev);
6713 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6715 int iC = int( angCurFloor );
6716 if ( iC < nbAngles )
6717 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6719 iP = int( angPrevCeil );
6721 angle += theAngles[ iC ];
6723 res.push_back(angle);
6728 for(; it!=res.end(); it++)
6729 Angles.push_back( *it );
6734 //================================================================================
6736 * \brief Move or copy theElements applying theTrsf to their nodes
6737 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6738 * \param theTrsf - transformation to apply
6739 * \param theCopy - if true, create translated copies of theElems
6740 * \param theMakeGroups - if true and theCopy, create translated groups
6741 * \param theTargetMesh - mesh to copy translated elements into
6742 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6744 //================================================================================
6746 SMESH_MeshEditor::PGroupIDs
6747 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6748 const gp_Trsf& theTrsf,
6750 const bool theMakeGroups,
6751 SMESH_Mesh* theTargetMesh)
6753 myLastCreatedElems.Clear();
6754 myLastCreatedNodes.Clear();
6756 bool needReverse = false;
6757 string groupPostfix;
6758 switch ( theTrsf.Form() ) {
6760 MESSAGE("gp_PntMirror");
6762 groupPostfix = "mirrored";
6765 MESSAGE("gp_Ax1Mirror");
6766 groupPostfix = "mirrored";
6769 MESSAGE("gp_Ax2Mirror");
6771 groupPostfix = "mirrored";
6774 MESSAGE("gp_Rotation");
6775 groupPostfix = "rotated";
6777 case gp_Translation:
6778 MESSAGE("gp_Translation");
6779 groupPostfix = "translated";
6782 MESSAGE("gp_Scale");
6783 groupPostfix = "scaled";
6785 case gp_CompoundTrsf: // different scale by axis
6786 MESSAGE("gp_CompoundTrsf");
6787 groupPostfix = "scaled";
6791 needReverse = false;
6792 groupPostfix = "transformed";
6795 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6796 SMESHDS_Mesh* aMesh = GetMeshDS();
6798 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6799 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6800 SMESH_MeshEditor::ElemFeatures elemType;
6802 // map old node to new one
6803 TNodeNodeMap nodeMap;
6805 // elements sharing moved nodes; those of them which have all
6806 // nodes mirrored but are not in theElems are to be reversed
6807 TIDSortedElemSet inverseElemSet;
6809 // source elements for each generated one
6810 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6812 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6813 TIDSortedElemSet orphanNode;
6815 if ( theElems.empty() ) // transform the whole mesh
6818 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6819 while ( eIt->more() ) theElems.insert( eIt->next() );
6821 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6822 while ( nIt->more() )
6824 const SMDS_MeshNode* node = nIt->next();
6825 if ( node->NbInverseElements() == 0)
6826 orphanNode.insert( node );
6830 // loop on elements to transform nodes : first orphan nodes then elems
6831 TIDSortedElemSet::iterator itElem;
6832 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6833 for (int i=0; i<2; i++)
6834 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6836 const SMDS_MeshElement* elem = *itElem;
6840 // loop on elem nodes
6842 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6843 while ( itN->more() )
6845 const SMDS_MeshNode* node = cast2Node( itN->next() );
6846 // check if a node has been already transformed
6847 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6848 nodeMap.insert( make_pair ( node, node ));
6849 if ( !n2n_isnew.second )
6852 node->GetXYZ( coord );
6853 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6854 if ( theTargetMesh ) {
6855 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6856 n2n_isnew.first->second = newNode;
6857 myLastCreatedNodes.Append(newNode);
6858 srcNodes.Append( node );
6860 else if ( theCopy ) {
6861 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6862 n2n_isnew.first->second = newNode;
6863 myLastCreatedNodes.Append(newNode);
6864 srcNodes.Append( node );
6867 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6868 // node position on shape becomes invalid
6869 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6870 ( SMDS_SpacePosition::originSpacePosition() );
6873 // keep inverse elements
6874 if ( !theCopy && !theTargetMesh && needReverse ) {
6875 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6876 while ( invElemIt->more() ) {
6877 const SMDS_MeshElement* iel = invElemIt->next();
6878 inverseElemSet.insert( iel );
6882 } // loop on elems in { &orphanNode, &theElems };
6884 // either create new elements or reverse mirrored ones
6885 if ( !theCopy && !needReverse && !theTargetMesh )
6888 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6890 // Replicate or reverse elements
6892 std::vector<int> iForw;
6893 vector<const SMDS_MeshNode*> nodes;
6894 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6896 const SMDS_MeshElement* elem = *itElem;
6897 if ( !elem ) continue;
6899 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6900 int nbNodes = elem->NbNodes();
6901 if ( geomType == SMDSGeom_NONE ) continue; // node
6903 nodes.resize( nbNodes );
6905 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6907 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6911 bool allTransformed = true;
6912 int nbFaces = aPolyedre->NbFaces();
6913 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6915 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6916 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6918 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6919 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6920 if ( nodeMapIt == nodeMap.end() )
6921 allTransformed = false; // not all nodes transformed
6923 nodes.push_back((*nodeMapIt).second);
6925 if ( needReverse && allTransformed )
6926 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6928 if ( !allTransformed )
6929 continue; // not all nodes transformed
6931 else // ----------------------- the rest element types
6933 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6934 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6935 const vector<int>& i = needReverse ? iRev : iForw;
6937 // find transformed nodes
6939 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6940 while ( itN->more() ) {
6941 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6942 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6943 if ( nodeMapIt == nodeMap.end() )
6944 break; // not all nodes transformed
6945 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6947 if ( iNode != nbNodes )
6948 continue; // not all nodes transformed
6952 // copy in this or a new mesh
6953 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6954 srcElems.Append( elem );
6957 // reverse element as it was reversed by transformation
6959 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6962 } // loop on elements
6964 if ( editor && editor != this )
6965 myLastCreatedElems = editor->myLastCreatedElems;
6967 PGroupIDs newGroupIDs;
6969 if ( ( theMakeGroups && theCopy ) ||
6970 ( theMakeGroups && theTargetMesh ) )
6971 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6976 //=======================================================================
6978 * \brief Create groups of elements made during transformation
6979 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6980 * \param elemGens - elements making corresponding myLastCreatedElems
6981 * \param postfix - to append to names of new groups
6982 * \param targetMesh - mesh to create groups in
6983 * \param topPresent - is there "top" elements that are created by sweeping
6985 //=======================================================================
6987 SMESH_MeshEditor::PGroupIDs
6988 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6989 const SMESH_SequenceOfElemPtr& elemGens,
6990 const std::string& postfix,
6991 SMESH_Mesh* targetMesh,
6992 const bool topPresent)
6994 PGroupIDs newGroupIDs( new list<int> );
6995 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6997 // Sort existing groups by types and collect their names
6999 // containers to store an old group and generated new ones;
7000 // 1st new group is for result elems of different type than a source one;
7001 // 2nd new group is for same type result elems ("top" group at extrusion)
7003 using boost::make_tuple;
7004 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
7005 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
7006 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7008 set< string > groupNames;
7010 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7011 if ( !groupIt->more() ) return newGroupIDs;
7013 int newGroupID = mesh->GetGroupIds().back()+1;
7014 while ( groupIt->more() )
7016 SMESH_Group * group = groupIt->next();
7017 if ( !group ) continue;
7018 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7019 if ( !groupDS || groupDS->IsEmpty() ) continue;
7020 groupNames.insert ( group->GetName() );
7021 groupDS->SetStoreName( group->GetName() );
7022 const SMDSAbs_ElementType type = groupDS->GetType();
7023 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7024 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7025 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7026 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7029 // Loop on nodes and elements to add them in new groups
7031 vector< const SMDS_MeshElement* > resultElems;
7032 for ( int isNodes = 0; isNodes < 2; ++isNodes )
7034 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
7035 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7036 if ( gens.Length() != elems.Length() )
7037 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7039 // loop on created elements
7040 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7042 const SMDS_MeshElement* sourceElem = gens( iElem );
7043 if ( !sourceElem ) {
7044 MESSAGE("generateGroups(): NULL source element");
7047 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7048 if ( groupsOldNew.empty() ) { // no groups of this type at all
7049 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7050 ++iElem; // skip all elements made by sourceElem
7053 // collect all elements made by the iElem-th sourceElem
7054 resultElems.clear();
7055 if ( const SMDS_MeshElement* resElem = elems( iElem ))
7056 if ( resElem != sourceElem )
7057 resultElems.push_back( resElem );
7058 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7059 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7060 if ( resElem != sourceElem )
7061 resultElems.push_back( resElem );
7063 const SMDS_MeshElement* topElem = 0;
7064 if ( isNodes ) // there must be a top element
7066 topElem = resultElems.back();
7067 resultElems.pop_back();
7071 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7072 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7073 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7075 topElem = *resElemIt;
7076 *resElemIt = 0; // erase *resElemIt
7080 // add resultElems to groups originted from ones the sourceElem belongs to
7081 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7082 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7084 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7085 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7087 // fill in a new group
7088 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7089 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7090 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7092 newGroup.Add( *resElemIt );
7094 // fill a "top" group
7097 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7098 newTopGroup.Add( topElem );
7102 } // loop on created elements
7103 }// loop on nodes and elements
7105 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7107 list<int> topGrouIds;
7108 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7110 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
7111 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7112 orderedOldNewGroups[i]->get<2>() };
7113 for ( int is2nd = 0; is2nd < 2; ++is2nd )
7115 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7116 if ( newGroupDS->IsEmpty() )
7118 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7123 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7126 const bool isTop = ( topPresent &&
7127 newGroupDS->GetType() == oldGroupDS->GetType() &&
7130 string name = oldGroupDS->GetStoreName();
7131 { // remove trailing whitespaces (issue 22599)
7132 size_t size = name.size();
7133 while ( size > 1 && isspace( name[ size-1 ]))
7135 if ( size != name.size() )
7137 name.resize( size );
7138 oldGroupDS->SetStoreName( name.c_str() );
7141 if ( !targetMesh ) {
7142 string suffix = ( isTop ? "top": postfix.c_str() );
7146 while ( !groupNames.insert( name ).second ) // name exists
7147 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7152 newGroupDS->SetStoreName( name.c_str() );
7154 // make a SMESH_Groups
7155 mesh->AddGroup( newGroupDS );
7157 topGrouIds.push_back( newGroupDS->GetID() );
7159 newGroupIDs->push_back( newGroupDS->GetID() );
7163 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7168 //================================================================================
7170 * * \brief Return list of group of nodes close to each other within theTolerance
7171 * * Search among theNodes or in the whole mesh if theNodes is empty using
7172 * * an Octree algorithm
7173 * \param [in,out] theNodes - the nodes to treat
7174 * \param [in] theTolerance - the tolerance
7175 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7176 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7177 * corner and medium nodes in separate groups
7179 //================================================================================
7181 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7182 const double theTolerance,
7183 TListOfListOfNodes & theGroupsOfNodes,
7184 bool theSeparateCornersAndMedium)
7186 myLastCreatedElems.Clear();
7187 myLastCreatedNodes.Clear();
7189 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7190 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7191 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7192 theSeparateCornersAndMedium = false;
7194 TIDSortedNodeSet& corners = theNodes;
7195 TIDSortedNodeSet medium;
7197 if ( theNodes.empty() ) // get all nodes in the mesh
7199 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7200 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7201 if ( theSeparateCornersAndMedium )
7202 while ( nIt->more() )
7204 const SMDS_MeshNode* n = nIt->next();
7205 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7206 nodeSet->insert( nodeSet->end(), n );
7209 while ( nIt->more() )
7210 theNodes.insert( theNodes.end(),nIt->next() );
7212 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7214 TIDSortedNodeSet::iterator nIt = corners.begin();
7215 while ( nIt != corners.end() )
7216 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7218 medium.insert( medium.end(), *nIt );
7219 corners.erase( nIt++ );
7227 if ( !corners.empty() )
7228 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7229 if ( !medium.empty() )
7230 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7233 //=======================================================================
7234 //function : SimplifyFace
7235 //purpose : split a chain of nodes into several closed chains
7236 //=======================================================================
7238 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7239 vector<const SMDS_MeshNode *>& poly_nodes,
7240 vector<int>& quantities) const
7242 int nbNodes = faceNodes.size();
7247 set<const SMDS_MeshNode*> nodeSet;
7249 // get simple seq of nodes
7250 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7253 simpleNodes[iSimple++] = faceNodes[0];
7254 for (int iCur = 1; iCur < nbNodes; iCur++) {
7255 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7256 simpleNodes[iSimple++] = faceNodes[iCur];
7257 nodeSet.insert( faceNodes[iCur] );
7260 int nbUnique = nodeSet.size();
7261 int nbSimple = iSimple;
7262 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7272 bool foundLoop = (nbSimple > nbUnique);
7275 set<const SMDS_MeshNode*> loopSet;
7276 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7277 const SMDS_MeshNode* n = simpleNodes[iSimple];
7278 if (!loopSet.insert( n ).second) {
7282 int iC = 0, curLast = iSimple;
7283 for (; iC < curLast; iC++) {
7284 if (simpleNodes[iC] == n) break;
7286 int loopLen = curLast - iC;
7288 // create sub-element
7290 quantities.push_back(loopLen);
7291 for (; iC < curLast; iC++) {
7292 poly_nodes.push_back(simpleNodes[iC]);
7295 // shift the rest nodes (place from the first loop position)
7296 for (iC = curLast + 1; iC < nbSimple; iC++) {
7297 simpleNodes[iC - loopLen] = simpleNodes[iC];
7299 nbSimple -= loopLen;
7302 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7303 } // while (foundLoop)
7307 quantities.push_back(iSimple);
7308 for (int i = 0; i < iSimple; i++)
7309 poly_nodes.push_back(simpleNodes[i]);
7315 //=======================================================================
7316 //function : MergeNodes
7317 //purpose : In each group, the cdr of nodes are substituted by the first one
7319 //=======================================================================
7321 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7323 MESSAGE("MergeNodes");
7324 myLastCreatedElems.Clear();
7325 myLastCreatedNodes.Clear();
7327 SMESHDS_Mesh* aMesh = GetMeshDS();
7329 TNodeNodeMap nodeNodeMap; // node to replace - new node
7330 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7331 list< int > rmElemIds, rmNodeIds;
7333 // Fill nodeNodeMap and elems
7335 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7336 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7338 list<const SMDS_MeshNode*>& nodes = *grIt;
7339 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7340 const SMDS_MeshNode* nToKeep = *nIt;
7341 for ( ++nIt; nIt != nodes.end(); nIt++ )
7343 const SMDS_MeshNode* nToRemove = *nIt;
7344 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7345 if ( nToRemove != nToKeep )
7347 rmNodeIds.push_back( nToRemove->GetID() );
7348 AddToSameGroups( nToKeep, nToRemove, aMesh );
7349 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7350 // after MergeNodes() w/o creating node in place of merged ones.
7351 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7352 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7353 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7354 sm->SetIsAlwaysComputed( true );
7356 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7357 while ( invElemIt->more() ) {
7358 const SMDS_MeshElement* elem = invElemIt->next();
7363 // Change element nodes or remove an element
7365 set<const SMDS_MeshNode*> nodeSet;
7366 vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7368 ElemFeatures elemType;
7370 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7371 for ( ; eIt != elems.end(); eIt++ )
7373 const SMDS_MeshElement* elem = *eIt;
7374 const int nbNodes = elem->NbNodes();
7375 const int aShapeId = FindShape( elem );
7378 curNodes.resize( nbNodes );
7379 uniqueNodes.resize( nbNodes );
7380 iRepl.resize( nbNodes );
7381 int iUnique = 0, iCur = 0, nbRepl = 0;
7383 // get new seq of nodes
7384 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7385 while ( itN->more() )
7387 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7389 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7390 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7392 { ////////// BUG 0020185: begin
7393 bool stopRecur = false;
7394 set<const SMDS_MeshNode*> nodesRecur;
7395 nodesRecur.insert(n);
7396 while (!stopRecur) {
7397 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7398 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7399 n = (*nnIt_i).second;
7400 if (!nodesRecur.insert(n).second) {
7401 // error: recursive dependancy
7408 } ////////// BUG 0020185: end
7410 curNodes[ iCur ] = n;
7411 bool isUnique = nodeSet.insert( n ).second;
7413 uniqueNodes[ iUnique++ ] = n;
7415 iRepl[ nbRepl++ ] = iCur;
7419 // Analyse element topology after replacement
7422 int nbUniqueNodes = nodeSet.size();
7423 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7425 if (elem->IsPoly()) // Polygons and Polyhedral volumes
7427 if (elem->GetType() == SMDSAbs_Face) // Polygon
7429 elemType.Init( elem );
7430 const bool isQuad = elemType.myIsQuad;
7432 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7433 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7435 // a polygon can divide into several elements
7436 vector<const SMDS_MeshNode *> polygons_nodes;
7437 vector<int> quantities;
7438 int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7441 vector<const SMDS_MeshNode *> face_nodes;
7443 for (int iface = 0; iface < nbNew; iface++)
7445 int nbNewNodes = quantities[iface];
7446 face_nodes.assign( polygons_nodes.begin() + inode,
7447 polygons_nodes.begin() + inode + nbNewNodes );
7448 inode += nbNewNodes;
7449 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7451 bool isValid = ( nbNewNodes % 2 == 0 );
7452 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7453 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7454 elemType.SetQuad( isValid );
7455 if ( isValid ) // put medium nodes after corners
7456 SMDS_MeshCell::applyInterlaceRev
7457 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7458 nbNewNodes ), face_nodes );
7460 elemType.SetPoly(( nbNewNodes / ( elemType.myIsQuad + 1 ) > 4 ));
7462 SMDS_MeshElement* newElem = AddElement( face_nodes, elemType );
7464 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7467 rmElemIds.push_back(elem->GetID());
7471 else if (elem->GetType() == SMDSAbs_Volume) // Polyhedral volume
7473 if (nbUniqueNodes < 4) {
7474 rmElemIds.push_back(elem->GetID());
7477 // each face has to be analyzed in order to check volume validity
7478 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
7481 int nbFaces = aPolyedre->NbFaces();
7483 vector<const SMDS_MeshNode *> poly_nodes;
7484 vector<int> quantities;
7486 for (int iface = 1; iface <= nbFaces; iface++) {
7487 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7488 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7490 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7491 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7492 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7493 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7494 faceNode = (*nnIt).second;
7496 faceNodes[inode - 1] = faceNode;
7499 SimplifyFace(faceNodes, poly_nodes, quantities);
7502 if (quantities.size() > 3) {
7503 // to be done: remove coincident faces
7506 if (quantities.size() > 3)
7508 const SMDS_MeshElement* newElem =
7509 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7510 myLastCreatedElems.Append(newElem);
7511 if ( aShapeId && newElem )
7512 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7513 rmElemIds.push_back(elem->GetID());
7517 rmElemIds.push_back(elem->GetID());
7528 // TODO not all the possible cases are solved. Find something more generic?
7529 switch ( nbNodes ) {
7530 case 2: ///////////////////////////////////// EDGE
7531 isOk = false; break;
7532 case 3: ///////////////////////////////////// TRIANGLE
7533 isOk = false; break;
7535 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7537 else { //////////////////////////////////// QUADRANGLE
7538 if ( nbUniqueNodes < 3 )
7540 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7541 isOk = false; // opposite nodes stick
7542 //MESSAGE("isOk " << isOk);
7545 case 6: ///////////////////////////////////// PENTAHEDRON
7546 if ( nbUniqueNodes == 4 ) {
7547 // ---------------------------------> tetrahedron
7549 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7550 // all top nodes stick: reverse a bottom
7551 uniqueNodes[ 0 ] = curNodes [ 1 ];
7552 uniqueNodes[ 1 ] = curNodes [ 0 ];
7554 else if (nbRepl == 3 &&
7555 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7556 // all bottom nodes stick: set a top before
7557 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7558 uniqueNodes[ 0 ] = curNodes [ 3 ];
7559 uniqueNodes[ 1 ] = curNodes [ 4 ];
7560 uniqueNodes[ 2 ] = curNodes [ 5 ];
7562 else if (nbRepl == 4 &&
7563 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7564 // a lateral face turns into a line: reverse a bottom
7565 uniqueNodes[ 0 ] = curNodes [ 1 ];
7566 uniqueNodes[ 1 ] = curNodes [ 0 ];
7571 else if ( nbUniqueNodes == 5 ) {
7572 // PENTAHEDRON --------------------> 2 tetrahedrons
7573 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7574 // a bottom node sticks with a linked top one
7576 SMDS_MeshElement* newElem =
7577 aMesh->AddVolume(curNodes[ 3 ],
7580 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7581 myLastCreatedElems.Append(newElem);
7583 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7584 // 2. : reverse a bottom
7585 uniqueNodes[ 0 ] = curNodes [ 1 ];
7586 uniqueNodes[ 1 ] = curNodes [ 0 ];
7596 if(elem->IsQuadratic()) { // Quadratic quadrangle
7608 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7611 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7613 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7614 uniqueNodes[0] = curNodes[0];
7615 uniqueNodes[1] = curNodes[2];
7616 uniqueNodes[2] = curNodes[3];
7617 uniqueNodes[3] = curNodes[5];
7618 uniqueNodes[4] = curNodes[6];
7619 uniqueNodes[5] = curNodes[7];
7622 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7623 uniqueNodes[0] = curNodes[0];
7624 uniqueNodes[1] = curNodes[1];
7625 uniqueNodes[2] = curNodes[2];
7626 uniqueNodes[3] = curNodes[4];
7627 uniqueNodes[4] = curNodes[5];
7628 uniqueNodes[5] = curNodes[6];
7631 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7632 uniqueNodes[0] = curNodes[1];
7633 uniqueNodes[1] = curNodes[2];
7634 uniqueNodes[2] = curNodes[3];
7635 uniqueNodes[3] = curNodes[5];
7636 uniqueNodes[4] = curNodes[6];
7637 uniqueNodes[5] = curNodes[0];
7640 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7641 uniqueNodes[0] = curNodes[0];
7642 uniqueNodes[1] = curNodes[1];
7643 uniqueNodes[2] = curNodes[3];
7644 uniqueNodes[3] = curNodes[4];
7645 uniqueNodes[4] = curNodes[6];
7646 uniqueNodes[5] = curNodes[7];
7649 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7650 uniqueNodes[0] = curNodes[0];
7651 uniqueNodes[1] = curNodes[2];
7652 uniqueNodes[2] = curNodes[3];
7653 uniqueNodes[3] = curNodes[1];
7654 uniqueNodes[4] = curNodes[6];
7655 uniqueNodes[5] = curNodes[7];
7658 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7659 uniqueNodes[0] = curNodes[0];
7660 uniqueNodes[1] = curNodes[1];
7661 uniqueNodes[2] = curNodes[2];
7662 uniqueNodes[3] = curNodes[4];
7663 uniqueNodes[4] = curNodes[5];
7664 uniqueNodes[5] = curNodes[7];
7667 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7668 uniqueNodes[0] = curNodes[0];
7669 uniqueNodes[1] = curNodes[1];
7670 uniqueNodes[2] = curNodes[3];
7671 uniqueNodes[3] = curNodes[4];
7672 uniqueNodes[4] = curNodes[2];
7673 uniqueNodes[5] = curNodes[7];
7676 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7677 uniqueNodes[0] = curNodes[0];
7678 uniqueNodes[1] = curNodes[1];
7679 uniqueNodes[2] = curNodes[2];
7680 uniqueNodes[3] = curNodes[4];
7681 uniqueNodes[4] = curNodes[5];
7682 uniqueNodes[5] = curNodes[3];
7687 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7690 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7694 //////////////////////////////////// HEXAHEDRON
7696 SMDS_VolumeTool hexa (elem);
7697 hexa.SetExternalNormal();
7698 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7699 //////////////////////// HEX ---> 1 tetrahedron
7700 for ( int iFace = 0; iFace < 6; iFace++ ) {
7701 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7702 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7703 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7704 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7705 // one face turns into a point ...
7706 int iOppFace = hexa.GetOppFaceIndex( iFace );
7707 ind = hexa.GetFaceNodesIndices( iOppFace );
7709 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7710 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7713 if ( nbStick == 1 ) {
7714 // ... and the opposite one - into a triangle.
7716 ind = hexa.GetFaceNodesIndices( iFace );
7717 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7724 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7725 //////////////////////// HEX ---> 1 prism
7726 int nbTria = 0, iTria[3];
7727 const int *ind; // indices of face nodes
7728 // look for triangular faces
7729 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7730 ind = hexa.GetFaceNodesIndices( iFace );
7731 TIDSortedNodeSet faceNodes;
7732 for ( iCur = 0; iCur < 4; iCur++ )
7733 faceNodes.insert( curNodes[ind[iCur]] );
7734 if ( faceNodes.size() == 3 )
7735 iTria[ nbTria++ ] = iFace;
7737 // check if triangles are opposite
7738 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7741 // set nodes of the bottom triangle
7742 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7744 for ( iCur = 0; iCur < 4; iCur++ )
7745 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7746 indB.push_back( ind[iCur] );
7747 if ( !hexa.IsForward() )
7748 std::swap( indB[0], indB[2] );
7749 for ( iCur = 0; iCur < 3; iCur++ )
7750 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7751 // set nodes of the top triangle
7752 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7753 for ( iCur = 0; iCur < 3; ++iCur )
7754 for ( int j = 0; j < 4; ++j )
7755 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7757 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7763 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7764 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7765 for ( int iFace = 0; iFace < 6; iFace++ ) {
7766 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7767 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7768 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7769 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7770 // one face turns into a point ...
7771 int iOppFace = hexa.GetOppFaceIndex( iFace );
7772 ind = hexa.GetFaceNodesIndices( iOppFace );
7774 iUnique = 2; // reverse a tetrahedron 1 bottom
7775 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7776 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7778 else if ( iUnique >= 0 )
7779 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7781 if ( nbStick == 0 ) {
7782 // ... and the opposite one is a quadrangle
7784 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7785 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7788 SMDS_MeshElement* newElem =
7789 aMesh->AddVolume(curNodes[ind[ 0 ]],
7792 curNodes[indTop[ 0 ]]);
7793 myLastCreatedElems.Append(newElem);
7795 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7802 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7803 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7804 // find indices of quad and tri faces
7805 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7806 for ( iFace = 0; iFace < 6; iFace++ ) {
7807 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7809 for ( iCur = 0; iCur < 4; iCur++ )
7810 nodeSet.insert( curNodes[ind[ iCur ]] );
7811 nbUniqueNodes = nodeSet.size();
7812 if ( nbUniqueNodes == 3 )
7813 iTriFace[ nbTri++ ] = iFace;
7814 else if ( nbUniqueNodes == 4 )
7815 iQuadFace[ nbQuad++ ] = iFace;
7817 if (nbQuad == 2 && nbTri == 4 &&
7818 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7819 // 2 opposite quadrangles stuck with a diagonal;
7820 // sample groups of merged indices: (0-4)(2-6)
7821 // --------------------------------------------> 2 tetrahedrons
7822 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7823 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7824 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7825 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7826 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7827 // stuck with 0-2 diagonal
7835 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7836 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7837 // stuck with 1-3 diagonal
7849 uniqueNodes[ 0 ] = curNodes [ i0 ];
7850 uniqueNodes[ 1 ] = curNodes [ i1d ];
7851 uniqueNodes[ 2 ] = curNodes [ i3d ];
7852 uniqueNodes[ 3 ] = curNodes [ i0t ];
7855 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7859 myLastCreatedElems.Append(newElem);
7861 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7864 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7865 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7866 // --------------------------------------------> prism
7867 // find 2 opposite triangles
7869 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7870 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7871 // find indices of kept and replaced nodes
7872 // and fill unique nodes of 2 opposite triangles
7873 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7874 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7875 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7876 // fill unique nodes
7879 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7880 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7881 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7883 // iCur of a linked node of the opposite face (make normals co-directed):
7884 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7885 // check that correspondent corners of triangles are linked
7886 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7889 uniqueNodes[ iUnique ] = n;
7890 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7899 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7902 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7909 } // switch ( nbNodes )
7911 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7913 if ( isOk ) // the non-poly elem remains valid after sticking nodes
7915 if ( nbNodes != nbUniqueNodes ||
7916 !aMesh->ChangeElementNodes( elem, & curNodes[0], nbNodes ))
7918 elemType.Init( elem ).SetID( elem->GetID() );
7920 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7921 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7923 uniqueNodes.resize(nbUniqueNodes);
7924 SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7925 if ( sm && newElem )
7926 sm->AddElement( newElem );
7927 if ( elem != newElem )
7928 ReplaceElemInGroups( elem, newElem, aMesh );
7932 // Remove invalid regular element or invalid polygon
7933 rmElemIds.push_back( elem->GetID() );
7936 } // loop on elements
7938 // Remove bad elements, then equal nodes (order important)
7940 Remove( rmElemIds, false );
7941 Remove( rmNodeIds, true );
7947 // ========================================================
7948 // class : SortableElement
7949 // purpose : allow sorting elements basing on their nodes
7950 // ========================================================
7951 class SortableElement : public set <const SMDS_MeshElement*>
7955 SortableElement( const SMDS_MeshElement* theElem )
7958 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7959 while ( nodeIt->more() )
7960 this->insert( nodeIt->next() );
7963 const SMDS_MeshElement* Get() const
7966 void Set(const SMDS_MeshElement* e) const
7971 mutable const SMDS_MeshElement* myElem;
7974 //=======================================================================
7975 //function : FindEqualElements
7976 //purpose : Return list of group of elements built on the same nodes.
7977 // Search among theElements or in the whole mesh if theElements is empty
7978 //=======================================================================
7980 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7981 TListOfListOfElementsID & theGroupsOfElementsID)
7983 myLastCreatedElems.Clear();
7984 myLastCreatedNodes.Clear();
7986 typedef map< SortableElement, int > TMapOfNodeSet;
7987 typedef list<int> TGroupOfElems;
7989 if ( theElements.empty() )
7990 { // get all elements in the mesh
7991 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7992 while ( eIt->more() )
7993 theElements.insert( theElements.end(), eIt->next() );
7996 vector< TGroupOfElems > arrayOfGroups;
7997 TGroupOfElems groupOfElems;
7998 TMapOfNodeSet mapOfNodeSet;
8000 TIDSortedElemSet::iterator elemIt = theElements.begin();
8001 for ( int i = 0; elemIt != theElements.end(); ++elemIt )
8003 const SMDS_MeshElement* curElem = *elemIt;
8004 SortableElement SE(curElem);
8006 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8007 if ( !pp.second ) { // one more coincident elem
8008 TMapOfNodeSet::iterator& itSE = pp.first;
8009 int ind = (*itSE).second;
8010 arrayOfGroups[ind].push_back( curElem->GetID() );
8013 arrayOfGroups.push_back( groupOfElems );
8014 arrayOfGroups.back().push_back( curElem->GetID() );
8019 groupOfElems.clear();
8020 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8021 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8023 if ( groupIt->size() > 1 ) {
8024 //groupOfElems.sort(); -- theElements is sorted already
8025 theGroupsOfElementsID.push_back( groupOfElems );
8026 theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8031 //=======================================================================
8032 //function : MergeElements
8033 //purpose : In each given group, substitute all elements by the first one.
8034 //=======================================================================
8036 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8038 myLastCreatedElems.Clear();
8039 myLastCreatedNodes.Clear();
8041 typedef list<int> TListOfIDs;
8042 TListOfIDs rmElemIds; // IDs of elems to remove
8044 SMESHDS_Mesh* aMesh = GetMeshDS();
8046 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8047 while ( groupsIt != theGroupsOfElementsID.end() ) {
8048 TListOfIDs& aGroupOfElemID = *groupsIt;
8049 aGroupOfElemID.sort();
8050 int elemIDToKeep = aGroupOfElemID.front();
8051 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8052 aGroupOfElemID.pop_front();
8053 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8054 while ( idIt != aGroupOfElemID.end() ) {
8055 int elemIDToRemove = *idIt;
8056 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8057 // add the kept element in groups of removed one (PAL15188)
8058 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8059 rmElemIds.push_back( elemIDToRemove );
8065 Remove( rmElemIds, false );
8068 //=======================================================================
8069 //function : MergeEqualElements
8070 //purpose : Remove all but one of elements built on the same nodes.
8071 //=======================================================================
8073 void SMESH_MeshEditor::MergeEqualElements()
8075 TIDSortedElemSet aMeshElements; /* empty input ==
8076 to merge equal elements in the whole mesh */
8077 TListOfListOfElementsID aGroupsOfElementsID;
8078 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8079 MergeElements(aGroupsOfElementsID);
8082 //=======================================================================
8083 //function : findAdjacentFace
8085 //=======================================================================
8087 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8088 const SMDS_MeshNode* n2,
8089 const SMDS_MeshElement* elem)
8091 TIDSortedElemSet elemSet, avoidSet;
8093 avoidSet.insert ( elem );
8094 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8097 //=======================================================================
8098 //function : findSegment
8099 //purpose : Return a mesh segment by two nodes one of which can be medium
8100 //=======================================================================
8102 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8103 const SMDS_MeshNode* n2)
8105 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8106 while ( it->more() )
8108 const SMDS_MeshElement* seg = it->next();
8109 if ( seg->GetNodeIndex( n2 ) >= 0 )
8115 //=======================================================================
8116 //function : FindFreeBorder
8118 //=======================================================================
8120 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8122 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8123 const SMDS_MeshNode* theSecondNode,
8124 const SMDS_MeshNode* theLastNode,
8125 list< const SMDS_MeshNode* > & theNodes,
8126 list< const SMDS_MeshElement* >& theFaces)
8128 if ( !theFirstNode || !theSecondNode )
8130 // find border face between theFirstNode and theSecondNode
8131 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8135 theFaces.push_back( curElem );
8136 theNodes.push_back( theFirstNode );
8137 theNodes.push_back( theSecondNode );
8139 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8140 TIDSortedElemSet foundElems;
8141 bool needTheLast = ( theLastNode != 0 );
8143 while ( nStart != theLastNode ) {
8144 if ( nStart == theFirstNode )
8145 return !needTheLast;
8147 // find all free border faces sharing form nStart
8149 list< const SMDS_MeshElement* > curElemList;
8150 list< const SMDS_MeshNode* > nStartList;
8151 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8152 while ( invElemIt->more() ) {
8153 const SMDS_MeshElement* e = invElemIt->next();
8154 if ( e == curElem || foundElems.insert( e ).second ) {
8156 int iNode = 0, nbNodes = e->NbNodes();
8157 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8159 if ( e->IsQuadratic() ) {
8160 const SMDS_VtkFace* F =
8161 dynamic_cast<const SMDS_VtkFace*>(e);
8162 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8163 // use special nodes iterator
8164 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8165 while( anIter->more() ) {
8166 nodes[ iNode++ ] = cast2Node(anIter->next());
8170 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8171 while ( nIt->more() )
8172 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8174 nodes[ iNode ] = nodes[ 0 ];
8176 for ( iNode = 0; iNode < nbNodes; iNode++ )
8177 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8178 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8179 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8181 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8182 curElemList.push_back( e );
8186 // analyse the found
8188 int nbNewBorders = curElemList.size();
8189 if ( nbNewBorders == 0 ) {
8190 // no free border furthermore
8191 return !needTheLast;
8193 else if ( nbNewBorders == 1 ) {
8194 // one more element found
8196 nStart = nStartList.front();
8197 curElem = curElemList.front();
8198 theFaces.push_back( curElem );
8199 theNodes.push_back( nStart );
8202 // several continuations found
8203 list< const SMDS_MeshElement* >::iterator curElemIt;
8204 list< const SMDS_MeshNode* >::iterator nStartIt;
8205 // check if one of them reached the last node
8206 if ( needTheLast ) {
8207 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8208 curElemIt!= curElemList.end();
8209 curElemIt++, nStartIt++ )
8210 if ( *nStartIt == theLastNode ) {
8211 theFaces.push_back( *curElemIt );
8212 theNodes.push_back( *nStartIt );
8216 // find the best free border by the continuations
8217 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8218 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8219 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8220 curElemIt!= curElemList.end();
8221 curElemIt++, nStartIt++ )
8223 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8224 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8225 // find one more free border
8226 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8230 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8231 // choice: clear a worse one
8232 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8233 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8234 contNodes[ iWorse ].clear();
8235 contFaces[ iWorse ].clear();
8238 if ( contNodes[0].empty() && contNodes[1].empty() )
8241 // append the best free border
8242 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8243 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8244 theNodes.pop_back(); // remove nIgnore
8245 theNodes.pop_back(); // remove nStart
8246 theFaces.pop_back(); // remove curElem
8247 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8248 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8249 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8250 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8253 } // several continuations found
8254 } // while ( nStart != theLastNode )
8259 //=======================================================================
8260 //function : CheckFreeBorderNodes
8261 //purpose : Return true if the tree nodes are on a free border
8262 //=======================================================================
8264 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8265 const SMDS_MeshNode* theNode2,
8266 const SMDS_MeshNode* theNode3)
8268 list< const SMDS_MeshNode* > nodes;
8269 list< const SMDS_MeshElement* > faces;
8270 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8273 //=======================================================================
8274 //function : SewFreeBorder
8276 //warning : for border-to-side sewing theSideSecondNode is considered as
8277 // the last side node and theSideThirdNode is not used
8278 //=======================================================================
8280 SMESH_MeshEditor::Sew_Error
8281 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8282 const SMDS_MeshNode* theBordSecondNode,
8283 const SMDS_MeshNode* theBordLastNode,
8284 const SMDS_MeshNode* theSideFirstNode,
8285 const SMDS_MeshNode* theSideSecondNode,
8286 const SMDS_MeshNode* theSideThirdNode,
8287 const bool theSideIsFreeBorder,
8288 const bool toCreatePolygons,
8289 const bool toCreatePolyedrs)
8291 myLastCreatedElems.Clear();
8292 myLastCreatedNodes.Clear();
8294 MESSAGE("::SewFreeBorder()");
8295 Sew_Error aResult = SEW_OK;
8297 // ====================================
8298 // find side nodes and elements
8299 // ====================================
8301 list< const SMDS_MeshNode* > nSide[ 2 ];
8302 list< const SMDS_MeshElement* > eSide[ 2 ];
8303 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8304 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8308 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8309 nSide[0], eSide[0])) {
8310 MESSAGE(" Free Border 1 not found " );
8311 aResult = SEW_BORDER1_NOT_FOUND;
8313 if (theSideIsFreeBorder) {
8316 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8317 nSide[1], eSide[1])) {
8318 MESSAGE(" Free Border 2 not found " );
8319 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8322 if ( aResult != SEW_OK )
8325 if (!theSideIsFreeBorder) {
8329 // -------------------------------------------------------------------------
8331 // 1. If nodes to merge are not coincident, move nodes of the free border
8332 // from the coord sys defined by the direction from the first to last
8333 // nodes of the border to the correspondent sys of the side 2
8334 // 2. On the side 2, find the links most co-directed with the correspondent
8335 // links of the free border
8336 // -------------------------------------------------------------------------
8338 // 1. Since sewing may break if there are volumes to split on the side 2,
8339 // we wont move nodes but just compute new coordinates for them
8340 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8341 TNodeXYZMap nBordXYZ;
8342 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8343 list< const SMDS_MeshNode* >::iterator nBordIt;
8345 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8346 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8347 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8348 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8349 double tol2 = 1.e-8;
8350 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8351 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8352 // Need node movement.
8354 // find X and Z axes to create trsf
8355 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8357 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8359 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8362 gp_Ax3 toBordAx( Pb1, Zb, X );
8363 gp_Ax3 fromSideAx( Ps1, Zs, X );
8364 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8366 gp_Trsf toBordSys, fromSide2Sys;
8367 toBordSys.SetTransformation( toBordAx );
8368 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8369 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8372 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8373 const SMDS_MeshNode* n = *nBordIt;
8374 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8375 toBordSys.Transforms( xyz );
8376 fromSide2Sys.Transforms( xyz );
8377 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8381 // just insert nodes XYZ in the nBordXYZ map
8382 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8383 const SMDS_MeshNode* n = *nBordIt;
8384 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8388 // 2. On the side 2, find the links most co-directed with the correspondent
8389 // links of the free border
8391 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8392 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8393 sideNodes.push_back( theSideFirstNode );
8395 bool hasVolumes = false;
8396 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8397 set<long> foundSideLinkIDs, checkedLinkIDs;
8398 SMDS_VolumeTool volume;
8399 //const SMDS_MeshNode* faceNodes[ 4 ];
8401 const SMDS_MeshNode* sideNode;
8402 const SMDS_MeshElement* sideElem;
8403 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8404 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8405 nBordIt = bordNodes.begin();
8407 // border node position and border link direction to compare with
8408 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8409 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8410 // choose next side node by link direction or by closeness to
8411 // the current border node:
8412 bool searchByDir = ( *nBordIt != theBordLastNode );
8414 // find the next node on the Side 2
8416 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8418 checkedLinkIDs.clear();
8419 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8421 // loop on inverse elements of current node (prevSideNode) on the Side 2
8422 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8423 while ( invElemIt->more() )
8425 const SMDS_MeshElement* elem = invElemIt->next();
8426 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8427 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8428 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8429 bool isVolume = volume.Set( elem );
8430 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8431 if ( isVolume ) // --volume
8433 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8434 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8435 if(elem->IsQuadratic()) {
8436 const SMDS_VtkFace* F =
8437 dynamic_cast<const SMDS_VtkFace*>(elem);
8438 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8439 // use special nodes iterator
8440 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8441 while( anIter->more() ) {
8442 nodes[ iNode ] = cast2Node(anIter->next());
8443 if ( nodes[ iNode++ ] == prevSideNode )
8444 iPrevNode = iNode - 1;
8448 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8449 while ( nIt->more() ) {
8450 nodes[ iNode ] = cast2Node( nIt->next() );
8451 if ( nodes[ iNode++ ] == prevSideNode )
8452 iPrevNode = iNode - 1;
8455 // there are 2 links to check
8460 // loop on links, to be precise, on the second node of links
8461 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8462 const SMDS_MeshNode* n = nodes[ iNode ];
8464 if ( !volume.IsLinked( n, prevSideNode ))
8468 if ( iNode ) // a node before prevSideNode
8469 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8470 else // a node after prevSideNode
8471 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8473 // check if this link was already used
8474 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8475 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8476 if (!isJustChecked &&
8477 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8479 // test a link geometrically
8480 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8481 bool linkIsBetter = false;
8482 double dot = 0.0, dist = 0.0;
8483 if ( searchByDir ) { // choose most co-directed link
8484 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8485 linkIsBetter = ( dot > maxDot );
8487 else { // choose link with the node closest to bordPos
8488 dist = ( nextXYZ - bordPos ).SquareModulus();
8489 linkIsBetter = ( dist < minDist );
8491 if ( linkIsBetter ) {
8500 } // loop on inverse elements of prevSideNode
8503 MESSAGE(" Cant find path by links of the Side 2 ");
8504 return SEW_BAD_SIDE_NODES;
8506 sideNodes.push_back( sideNode );
8507 sideElems.push_back( sideElem );
8508 foundSideLinkIDs.insert ( linkID );
8509 prevSideNode = sideNode;
8511 if ( *nBordIt == theBordLastNode )
8512 searchByDir = false;
8514 // find the next border link to compare with
8515 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8516 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8517 // move to next border node if sideNode is before forward border node (bordPos)
8518 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8519 prevBordNode = *nBordIt;
8521 bordPos = nBordXYZ[ *nBordIt ];
8522 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8523 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8527 while ( sideNode != theSideSecondNode );
8529 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8530 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8531 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8533 } // end nodes search on the side 2
8535 // ============================
8536 // sew the border to the side 2
8537 // ============================
8539 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8540 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8542 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8543 if ( toMergeConformal && toCreatePolygons )
8545 // do not merge quadrangles if polygons are OK (IPAL0052824)
8546 eIt[0] = eSide[0].begin();
8547 eIt[1] = eSide[1].begin();
8548 bool allQuads[2] = { true, true };
8549 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8550 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8551 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8553 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8556 TListOfListOfNodes nodeGroupsToMerge;
8557 if (( toMergeConformal ) ||
8558 ( theSideIsFreeBorder && !theSideThirdNode )) {
8560 // all nodes are to be merged
8562 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8563 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8564 nIt[0]++, nIt[1]++ )
8566 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8567 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8568 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8573 // insert new nodes into the border and the side to get equal nb of segments
8575 // get normalized parameters of nodes on the borders
8576 vector< double > param[ 2 ];
8577 param[0].resize( maxNbNodes );
8578 param[1].resize( maxNbNodes );
8580 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8581 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8582 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8583 const SMDS_MeshNode* nPrev = *nIt;
8584 double bordLength = 0;
8585 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8586 const SMDS_MeshNode* nCur = *nIt;
8587 gp_XYZ segment (nCur->X() - nPrev->X(),
8588 nCur->Y() - nPrev->Y(),
8589 nCur->Z() - nPrev->Z());
8590 double segmentLen = segment.Modulus();
8591 bordLength += segmentLen;
8592 param[ iBord ][ iNode ] = bordLength;
8595 // normalize within [0,1]
8596 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8597 param[ iBord ][ iNode ] /= bordLength;
8601 // loop on border segments
8602 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8603 int i[ 2 ] = { 0, 0 };
8604 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8605 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8607 TElemOfNodeListMap insertMap;
8608 TElemOfNodeListMap::iterator insertMapIt;
8610 // key: elem to insert nodes into
8611 // value: 2 nodes to insert between + nodes to be inserted
8613 bool next[ 2 ] = { false, false };
8615 // find min adjacent segment length after sewing
8616 double nextParam = 10., prevParam = 0;
8617 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8618 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8619 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8620 if ( i[ iBord ] > 0 )
8621 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8623 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8624 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8625 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8627 // choose to insert or to merge nodes
8628 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8629 if ( Abs( du ) <= minSegLen * 0.2 ) {
8632 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8633 const SMDS_MeshNode* n0 = *nIt[0];
8634 const SMDS_MeshNode* n1 = *nIt[1];
8635 nodeGroupsToMerge.back().push_back( n1 );
8636 nodeGroupsToMerge.back().push_back( n0 );
8637 // position of node of the border changes due to merge
8638 param[ 0 ][ i[0] ] += du;
8639 // move n1 for the sake of elem shape evaluation during insertion.
8640 // n1 will be removed by MergeNodes() anyway
8641 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8642 next[0] = next[1] = true;
8647 int intoBord = ( du < 0 ) ? 0 : 1;
8648 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8649 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8650 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8651 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8652 if ( intoBord == 1 ) {
8653 // move node of the border to be on a link of elem of the side
8654 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8655 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8656 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8657 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8658 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8660 insertMapIt = insertMap.find( elem );
8661 bool notFound = ( insertMapIt == insertMap.end() );
8662 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8664 // insert into another link of the same element:
8665 // 1. perform insertion into the other link of the elem
8666 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8667 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8668 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8669 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8670 // 2. perform insertion into the link of adjacent faces
8671 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8672 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8674 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8675 InsertNodesIntoLink( seg, n12, n22, nodeList );
8677 if (toCreatePolyedrs) {
8678 // perform insertion into the links of adjacent volumes
8679 UpdateVolumes(n12, n22, nodeList);
8681 // 3. find an element appeared on n1 and n2 after the insertion
8682 insertMap.erase( elem );
8683 elem = findAdjacentFace( n1, n2, 0 );
8685 if ( notFound || otherLink ) {
8686 // add element and nodes of the side into the insertMap
8687 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8688 (*insertMapIt).second.push_back( n1 );
8689 (*insertMapIt).second.push_back( n2 );
8691 // add node to be inserted into elem
8692 (*insertMapIt).second.push_back( nIns );
8693 next[ 1 - intoBord ] = true;
8696 // go to the next segment
8697 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8698 if ( next[ iBord ] ) {
8699 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8701 nPrev[ iBord ] = *nIt[ iBord ];
8702 nIt[ iBord ]++; i[ iBord ]++;
8706 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8708 // perform insertion of nodes into elements
8710 for (insertMapIt = insertMap.begin();
8711 insertMapIt != insertMap.end();
8714 const SMDS_MeshElement* elem = (*insertMapIt).first;
8715 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8716 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8717 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8719 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8721 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8722 InsertNodesIntoLink( seg, n1, n2, nodeList );
8725 if ( !theSideIsFreeBorder ) {
8726 // look for and insert nodes into the faces adjacent to elem
8727 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8728 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8731 if (toCreatePolyedrs) {
8732 // perform insertion into the links of adjacent volumes
8733 UpdateVolumes(n1, n2, nodeList);
8736 } // end: insert new nodes
8738 MergeNodes ( nodeGroupsToMerge );
8741 // Remove coincident segments
8744 TIDSortedElemSet segments;
8745 SMESH_SequenceOfElemPtr newFaces;
8746 for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8748 if ( !myLastCreatedElems(i) ) continue;
8749 if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8750 segments.insert( segments.end(), myLastCreatedElems(i) );
8752 newFaces.Append( myLastCreatedElems(i) );
8754 // get segments adjacent to merged nodes
8755 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8756 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8758 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8759 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8760 while ( segIt->more() )
8761 segments.insert( segIt->next() );
8765 TListOfListOfElementsID equalGroups;
8766 if ( !segments.empty() )
8767 FindEqualElements( segments, equalGroups );
8768 if ( !equalGroups.empty() )
8770 // remove from segments those that will be removed
8771 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8772 for ( ; itGroups != equalGroups.end(); ++itGroups )
8774 list< int >& group = *itGroups;
8775 list< int >::iterator id = group.begin();
8776 for ( ++id; id != group.end(); ++id )
8777 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8778 segments.erase( seg );
8780 // remove equal segments
8781 MergeElements( equalGroups );
8783 // restore myLastCreatedElems
8784 myLastCreatedElems = newFaces;
8785 TIDSortedElemSet::iterator seg = segments.begin();
8786 for ( ; seg != segments.end(); ++seg )
8787 myLastCreatedElems.Append( *seg );
8793 //=======================================================================
8794 //function : InsertNodesIntoLink
8795 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8796 // and theBetweenNode2 and split theElement
8797 //=======================================================================
8799 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8800 const SMDS_MeshNode* theBetweenNode1,
8801 const SMDS_MeshNode* theBetweenNode2,
8802 list<const SMDS_MeshNode*>& theNodesToInsert,
8803 const bool toCreatePoly)
8805 if ( !theElement ) return;
8807 SMESHDS_Mesh *aMesh = GetMeshDS();
8808 vector<const SMDS_MeshElement*> newElems;
8810 if ( theElement->GetType() == SMDSAbs_Edge )
8812 theNodesToInsert.push_front( theBetweenNode1 );
8813 theNodesToInsert.push_back ( theBetweenNode2 );
8814 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8815 const SMDS_MeshNode* n1 = *n;
8816 for ( ++n; n != theNodesToInsert.end(); ++n )
8818 const SMDS_MeshNode* n2 = *n;
8819 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8820 AddToSameGroups( seg, theElement, aMesh );
8822 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8825 theNodesToInsert.pop_front();
8826 theNodesToInsert.pop_back();
8828 if ( theElement->IsQuadratic() ) // add a not split part
8830 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8831 theElement->end_nodes() );
8832 int iOther = 0, nbN = nodes.size();
8833 for ( ; iOther < nbN; ++iOther )
8834 if ( nodes[iOther] != theBetweenNode1 &&
8835 nodes[iOther] != theBetweenNode2 )
8839 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8840 AddToSameGroups( seg, theElement, aMesh );
8842 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8844 else if ( iOther == 2 )
8846 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8847 AddToSameGroups( seg, theElement, aMesh );
8849 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8852 // treat new elements
8853 for ( size_t i = 0; i < newElems.size(); ++i )
8856 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8857 myLastCreatedElems.Append( newElems[i] );
8859 ReplaceElemInGroups( theElement, newElems, aMesh );
8860 aMesh->RemoveElement( theElement );
8863 } // if ( theElement->GetType() == SMDSAbs_Edge )
8865 const SMDS_MeshElement* theFace = theElement;
8866 if ( theFace->GetType() != SMDSAbs_Face ) return;
8868 // find indices of 2 link nodes and of the rest nodes
8869 int iNode = 0, il1, il2, i3, i4;
8870 il1 = il2 = i3 = i4 = -1;
8871 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8873 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8874 while ( nodeIt->more() ) {
8875 const SMDS_MeshNode* n = nodeIt->next();
8876 if ( n == theBetweenNode1 )
8878 else if ( n == theBetweenNode2 )
8884 nodes[ iNode++ ] = n;
8886 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8889 // arrange link nodes to go one after another regarding the face orientation
8890 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8891 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8896 aNodesToInsert.reverse();
8898 // check that not link nodes of a quadrangles are in good order
8899 int nbFaceNodes = theFace->NbNodes();
8900 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8906 if (toCreatePoly || theFace->IsPoly()) {
8909 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8911 // add nodes of face up to first node of link
8914 if ( theFace->IsQuadratic() ) {
8915 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8916 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8917 // use special nodes iterator
8918 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8919 while( anIter->more() && !isFLN ) {
8920 const SMDS_MeshNode* n = cast2Node(anIter->next());
8921 poly_nodes[iNode++] = n;
8922 if (n == nodes[il1]) {
8926 // add nodes to insert
8927 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8928 for (; nIt != aNodesToInsert.end(); nIt++) {
8929 poly_nodes[iNode++] = *nIt;
8931 // add nodes of face starting from last node of link
8932 while ( anIter->more() ) {
8933 poly_nodes[iNode++] = cast2Node(anIter->next());
8937 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8938 while ( nodeIt->more() && !isFLN ) {
8939 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8940 poly_nodes[iNode++] = n;
8941 if (n == nodes[il1]) {
8945 // add nodes to insert
8946 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8947 for (; nIt != aNodesToInsert.end(); nIt++) {
8948 poly_nodes[iNode++] = *nIt;
8950 // add nodes of face starting from last node of link
8951 while ( nodeIt->more() ) {
8952 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8953 poly_nodes[iNode++] = n;
8958 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8961 else if ( !theFace->IsQuadratic() )
8963 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8964 int nbLinkNodes = 2 + aNodesToInsert.size();
8965 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8966 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8967 linkNodes[ 0 ] = nodes[ il1 ];
8968 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8969 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8970 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8971 linkNodes[ iNode++ ] = *nIt;
8973 // decide how to split a quadrangle: compare possible variants
8974 // and choose which of splits to be a quadrangle
8975 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8976 if ( nbFaceNodes == 3 ) {
8977 iBestQuad = nbSplits;
8980 else if ( nbFaceNodes == 4 ) {
8981 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8982 double aBestRate = DBL_MAX;
8983 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8985 double aBadRate = 0;
8986 // evaluate elements quality
8987 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8988 if ( iSplit == iQuad ) {
8989 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8993 aBadRate += getBadRate( &quad, aCrit );
8996 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8998 nodes[ iSplit < iQuad ? i4 : i3 ]);
8999 aBadRate += getBadRate( &tria, aCrit );
9003 if ( aBadRate < aBestRate ) {
9005 aBestRate = aBadRate;
9010 // create new elements
9012 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
9013 SMDS_MeshElement* newElem = 0;
9014 if ( iSplit == iBestQuad )
9015 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9020 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9022 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
9025 const SMDS_MeshNode* newNodes[ 4 ];
9026 newNodes[ 0 ] = linkNodes[ i1 ];
9027 newNodes[ 1 ] = linkNodes[ i2 ];
9028 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9029 newNodes[ 3 ] = nodes[ i4 ];
9030 if (iSplit == iBestQuad)
9031 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
9033 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
9035 } // end if(!theFace->IsQuadratic())
9037 else { // theFace is quadratic
9038 // we have to split theFace on simple triangles and one simple quadrangle
9040 int nbshift = tmp*2;
9041 // shift nodes in nodes[] by nbshift
9043 for(i=0; i<nbshift; i++) {
9044 const SMDS_MeshNode* n = nodes[0];
9045 for(j=0; j<nbFaceNodes-1; j++) {
9046 nodes[j] = nodes[j+1];
9048 nodes[nbFaceNodes-1] = n;
9050 il1 = il1 - nbshift;
9051 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9052 // n0 n1 n2 n0 n1 n2
9053 // +-----+-----+ +-----+-----+
9062 // create new elements
9064 if ( nbFaceNodes == 6 ) { // quadratic triangle
9065 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9066 if ( theFace->IsMediumNode(nodes[il1]) ) {
9067 // create quadrangle
9068 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9074 // create quadrangle
9075 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9081 else { // nbFaceNodes==8 - quadratic quadrangle
9082 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9083 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9084 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9085 if ( theFace->IsMediumNode( nodes[ il1 ])) {
9086 // create quadrangle
9087 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9093 // create quadrangle
9094 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9100 // create needed triangles using n1,n2,n3 and inserted nodes
9101 int nbn = 2 + aNodesToInsert.size();
9102 vector<const SMDS_MeshNode*> aNodes(nbn);
9103 aNodes[0 ] = nodes[n1];
9104 aNodes[nbn-1] = nodes[n2];
9105 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9106 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9107 aNodes[iNode++] = *nIt;
9109 for ( i = 1; i < nbn; i++ )
9110 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9113 // remove the old face
9114 for ( size_t i = 0; i < newElems.size(); ++i )
9117 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9118 myLastCreatedElems.Append( newElems[i] );
9120 ReplaceElemInGroups( theFace, newElems, aMesh );
9121 aMesh->RemoveElement(theFace);
9123 } // InsertNodesIntoLink()
9125 //=======================================================================
9126 //function : UpdateVolumes
9128 //=======================================================================
9130 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9131 const SMDS_MeshNode* theBetweenNode2,
9132 list<const SMDS_MeshNode*>& theNodesToInsert)
9134 myLastCreatedElems.Clear();
9135 myLastCreatedNodes.Clear();
9137 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9138 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9139 const SMDS_MeshElement* elem = invElemIt->next();
9141 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9142 SMDS_VolumeTool aVolume (elem);
9143 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9146 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9147 int iface, nbFaces = aVolume.NbFaces();
9148 vector<const SMDS_MeshNode *> poly_nodes;
9149 vector<int> quantities (nbFaces);
9151 for (iface = 0; iface < nbFaces; iface++) {
9152 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9153 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9154 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9156 for (int inode = 0; inode < nbFaceNodes; inode++) {
9157 poly_nodes.push_back(faceNodes[inode]);
9159 if (nbInserted == 0) {
9160 if (faceNodes[inode] == theBetweenNode1) {
9161 if (faceNodes[inode + 1] == theBetweenNode2) {
9162 nbInserted = theNodesToInsert.size();
9164 // add nodes to insert
9165 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9166 for (; nIt != theNodesToInsert.end(); nIt++) {
9167 poly_nodes.push_back(*nIt);
9171 else if (faceNodes[inode] == theBetweenNode2) {
9172 if (faceNodes[inode + 1] == theBetweenNode1) {
9173 nbInserted = theNodesToInsert.size();
9175 // add nodes to insert in reversed order
9176 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9178 for (; nIt != theNodesToInsert.begin(); nIt--) {
9179 poly_nodes.push_back(*nIt);
9181 poly_nodes.push_back(*nIt);
9188 quantities[iface] = nbFaceNodes + nbInserted;
9191 // Replace the volume
9192 SMESHDS_Mesh *aMesh = GetMeshDS();
9194 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9196 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9197 myLastCreatedElems.Append( newElem );
9198 ReplaceElemInGroups( elem, newElem, aMesh );
9200 aMesh->RemoveElement( elem );
9206 //================================================================================
9208 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9210 //================================================================================
9212 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9213 vector<const SMDS_MeshNode *> & nodes,
9214 vector<int> & nbNodeInFaces )
9217 nbNodeInFaces.clear();
9218 SMDS_VolumeTool vTool ( elem );
9219 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9221 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9222 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9223 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9228 //=======================================================================
9230 * \brief Convert elements contained in a sub-mesh to quadratic
9231 * \return int - nb of checked elements
9233 //=======================================================================
9235 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9236 SMESH_MesherHelper& theHelper,
9237 const bool theForce3d)
9240 if( !theSm ) return nbElem;
9242 vector<int> nbNodeInFaces;
9243 vector<const SMDS_MeshNode *> nodes;
9244 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9245 while(ElemItr->more())
9248 const SMDS_MeshElement* elem = ElemItr->next();
9249 if( !elem ) continue;
9251 // analyse a necessity of conversion
9252 const SMDSAbs_ElementType aType = elem->GetType();
9253 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9255 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9256 bool hasCentralNodes = false;
9257 if ( elem->IsQuadratic() )
9260 switch ( aGeomType ) {
9261 case SMDSEntity_Quad_Triangle:
9262 case SMDSEntity_Quad_Quadrangle:
9263 case SMDSEntity_Quad_Hexa:
9264 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9266 case SMDSEntity_BiQuad_Triangle:
9267 case SMDSEntity_BiQuad_Quadrangle:
9268 case SMDSEntity_TriQuad_Hexa:
9269 alreadyOK = theHelper.GetIsBiQuadratic();
9270 hasCentralNodes = true;
9275 // take into account already present modium nodes
9277 case SMDSAbs_Volume:
9278 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9280 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9282 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9288 // get elem data needed to re-create it
9290 const int id = elem->GetID();
9291 const int nbNodes = elem->NbCornerNodes();
9292 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9293 if ( aGeomType == SMDSEntity_Polyhedra )
9294 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9295 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9296 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9298 // remove a linear element
9299 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9301 // remove central nodes of biquadratic elements (biquad->quad convertion)
9302 if ( hasCentralNodes )
9303 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9304 if ( nodes[i]->NbInverseElements() == 0 )
9305 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9307 const SMDS_MeshElement* NewElem = 0;
9313 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9321 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9324 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9327 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9331 case SMDSAbs_Volume :
9335 case SMDSEntity_Tetra:
9336 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9338 case SMDSEntity_Pyramid:
9339 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9341 case SMDSEntity_Penta:
9342 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9344 case SMDSEntity_Hexa:
9345 case SMDSEntity_Quad_Hexa:
9346 case SMDSEntity_TriQuad_Hexa:
9347 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9348 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9350 case SMDSEntity_Hexagonal_Prism:
9352 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9359 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9360 if( NewElem && NewElem->getshapeId() < 1 )
9361 theSm->AddElement( NewElem );
9365 //=======================================================================
9366 //function : ConvertToQuadratic
9368 //=======================================================================
9370 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9372 SMESHDS_Mesh* meshDS = GetMeshDS();
9374 SMESH_MesherHelper aHelper(*myMesh);
9376 aHelper.SetIsQuadratic( true );
9377 aHelper.SetIsBiQuadratic( theToBiQuad );
9378 aHelper.SetElementsOnShape(true);
9379 aHelper.ToFixNodeParameters( true );
9381 // convert elements assigned to sub-meshes
9382 int nbCheckedElems = 0;
9383 if ( myMesh->HasShapeToMesh() )
9385 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9387 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9388 while ( smIt->more() ) {
9389 SMESH_subMesh* sm = smIt->next();
9390 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9391 aHelper.SetSubShape( sm->GetSubShape() );
9392 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9398 // convert elements NOT assigned to sub-meshes
9399 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9400 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9402 aHelper.SetElementsOnShape(false);
9403 SMESHDS_SubMesh *smDS = 0;
9406 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9407 while( aEdgeItr->more() )
9409 const SMDS_MeshEdge* edge = aEdgeItr->next();
9410 if ( !edge->IsQuadratic() )
9412 int id = edge->GetID();
9413 const SMDS_MeshNode* n1 = edge->GetNode(0);
9414 const SMDS_MeshNode* n2 = edge->GetNode(1);
9416 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9418 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9419 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9423 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9428 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9429 while( aFaceItr->more() )
9431 const SMDS_MeshFace* face = aFaceItr->next();
9432 if ( !face ) continue;
9434 const SMDSAbs_EntityType type = face->GetEntityType();
9438 case SMDSEntity_Quad_Triangle:
9439 case SMDSEntity_Quad_Quadrangle:
9440 alreadyOK = !theToBiQuad;
9441 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9443 case SMDSEntity_BiQuad_Triangle:
9444 case SMDSEntity_BiQuad_Quadrangle:
9445 alreadyOK = theToBiQuad;
9446 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9448 default: alreadyOK = false;
9453 const int id = face->GetID();
9454 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9456 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9458 SMDS_MeshFace * NewFace = 0;
9461 case SMDSEntity_Triangle:
9462 case SMDSEntity_Quad_Triangle:
9463 case SMDSEntity_BiQuad_Triangle:
9464 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9465 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9466 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9469 case SMDSEntity_Quadrangle:
9470 case SMDSEntity_Quad_Quadrangle:
9471 case SMDSEntity_BiQuad_Quadrangle:
9472 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9473 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9474 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9478 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9480 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9484 vector<int> nbNodeInFaces;
9485 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9486 while(aVolumeItr->more())
9488 const SMDS_MeshVolume* volume = aVolumeItr->next();
9489 if ( !volume ) continue;
9491 const SMDSAbs_EntityType type = volume->GetEntityType();
9492 if ( volume->IsQuadratic() )
9497 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9498 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9499 default: alreadyOK = true;
9503 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9507 const int id = volume->GetID();
9508 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9509 if ( type == SMDSEntity_Polyhedra )
9510 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9511 else if ( type == SMDSEntity_Hexagonal_Prism )
9512 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9514 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9516 SMDS_MeshVolume * NewVolume = 0;
9519 case SMDSEntity_Tetra:
9520 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9522 case SMDSEntity_Hexa:
9523 case SMDSEntity_Quad_Hexa:
9524 case SMDSEntity_TriQuad_Hexa:
9525 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9526 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9527 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9528 if ( nodes[i]->NbInverseElements() == 0 )
9529 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9531 case SMDSEntity_Pyramid:
9532 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9533 nodes[3], nodes[4], id, theForce3d);
9535 case SMDSEntity_Penta:
9536 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9537 nodes[3], nodes[4], nodes[5], id, theForce3d);
9539 case SMDSEntity_Hexagonal_Prism:
9541 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9543 ReplaceElemInGroups(volume, NewVolume, meshDS);
9548 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9549 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9550 // aHelper.FixQuadraticElements(myError);
9551 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9555 //================================================================================
9557 * \brief Makes given elements quadratic
9558 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9559 * \param theElements - elements to make quadratic
9561 //================================================================================
9563 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9564 TIDSortedElemSet& theElements,
9565 const bool theToBiQuad)
9567 if ( theElements.empty() ) return;
9569 // we believe that all theElements are of the same type
9570 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9572 // get all nodes shared by theElements
9573 TIDSortedNodeSet allNodes;
9574 TIDSortedElemSet::iterator eIt = theElements.begin();
9575 for ( ; eIt != theElements.end(); ++eIt )
9576 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9578 // complete theElements with elements of lower dim whose all nodes are in allNodes
9580 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9581 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9582 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9583 for ( ; nIt != allNodes.end(); ++nIt )
9585 const SMDS_MeshNode* n = *nIt;
9586 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9587 while ( invIt->more() )
9589 const SMDS_MeshElement* e = invIt->next();
9590 const SMDSAbs_ElementType type = e->GetType();
9591 if ( e->IsQuadratic() )
9593 quadAdjacentElems[ type ].insert( e );
9596 switch ( e->GetEntityType() ) {
9597 case SMDSEntity_Quad_Triangle:
9598 case SMDSEntity_Quad_Quadrangle:
9599 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9600 case SMDSEntity_BiQuad_Triangle:
9601 case SMDSEntity_BiQuad_Quadrangle:
9602 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9603 default: alreadyOK = true;
9608 if ( type >= elemType )
9609 continue; // same type or more complex linear element
9611 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9612 continue; // e is already checked
9616 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9617 while ( nodeIt->more() && allIn )
9618 allIn = allNodes.count( nodeIt->next() );
9620 theElements.insert(e );
9624 SMESH_MesherHelper helper(*myMesh);
9625 helper.SetIsQuadratic( true );
9626 helper.SetIsBiQuadratic( theToBiQuad );
9628 // add links of quadratic adjacent elements to the helper
9630 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9631 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9632 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9634 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9636 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9637 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9638 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9640 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9642 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9643 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9644 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9646 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9649 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9651 SMESHDS_Mesh* meshDS = GetMeshDS();
9652 SMESHDS_SubMesh* smDS = 0;
9653 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9655 const SMDS_MeshElement* elem = *eIt;
9658 int nbCentralNodes = 0;
9659 switch ( elem->GetEntityType() ) {
9660 // linear convertible
9661 case SMDSEntity_Edge:
9662 case SMDSEntity_Triangle:
9663 case SMDSEntity_Quadrangle:
9664 case SMDSEntity_Tetra:
9665 case SMDSEntity_Pyramid:
9666 case SMDSEntity_Hexa:
9667 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9668 // quadratic that can become bi-quadratic
9669 case SMDSEntity_Quad_Triangle:
9670 case SMDSEntity_Quad_Quadrangle:
9671 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9673 case SMDSEntity_BiQuad_Triangle:
9674 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9675 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9677 default: alreadyOK = true;
9679 if ( alreadyOK ) continue;
9681 const SMDSAbs_ElementType type = elem->GetType();
9682 const int id = elem->GetID();
9683 const int nbNodes = elem->NbCornerNodes();
9684 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9686 helper.SetSubShape( elem->getshapeId() );
9688 if ( !smDS || !smDS->Contains( elem ))
9689 smDS = meshDS->MeshElements( elem->getshapeId() );
9690 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9692 SMDS_MeshElement * newElem = 0;
9695 case 4: // cases for most frequently used element types go first (for optimization)
9696 if ( type == SMDSAbs_Volume )
9697 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9699 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9702 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9703 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9706 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9709 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9712 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9713 nodes[4], id, theForce3d);
9716 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9717 nodes[4], nodes[5], id, theForce3d);
9721 ReplaceElemInGroups( elem, newElem, meshDS);
9722 if( newElem && smDS )
9723 smDS->AddElement( newElem );
9725 // remove central nodes
9726 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9727 if ( nodes[i]->NbInverseElements() == 0 )
9728 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9730 } // loop on theElements
9733 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9734 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9735 // helper.FixQuadraticElements( myError );
9736 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9740 //=======================================================================
9742 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9743 * \return int - nb of checked elements
9745 //=======================================================================
9747 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9748 SMDS_ElemIteratorPtr theItr,
9749 const int theShapeID)
9752 SMESHDS_Mesh* meshDS = GetMeshDS();
9753 ElemFeatures elemType;
9754 vector<const SMDS_MeshNode *> nodes;
9756 while( theItr->more() )
9758 const SMDS_MeshElement* elem = theItr->next();
9760 if( elem && elem->IsQuadratic())
9763 int nbCornerNodes = elem->NbCornerNodes();
9764 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9766 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9768 //remove a quadratic element
9769 if ( !theSm || !theSm->Contains( elem ))
9770 theSm = meshDS->MeshElements( elem->getshapeId() );
9771 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9773 // remove medium nodes
9774 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9775 if ( nodes[i]->NbInverseElements() == 0 )
9776 meshDS->RemoveFreeNode( nodes[i], theSm );
9778 // add a linear element
9779 nodes.resize( nbCornerNodes );
9780 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9781 ReplaceElemInGroups(elem, newElem, meshDS);
9782 if( theSm && newElem )
9783 theSm->AddElement( newElem );
9789 //=======================================================================
9790 //function : ConvertFromQuadratic
9792 //=======================================================================
9794 bool SMESH_MeshEditor::ConvertFromQuadratic()
9796 int nbCheckedElems = 0;
9797 if ( myMesh->HasShapeToMesh() )
9799 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9801 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9802 while ( smIt->more() ) {
9803 SMESH_subMesh* sm = smIt->next();
9804 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9805 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9811 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9812 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9814 SMESHDS_SubMesh *aSM = 0;
9815 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9823 //================================================================================
9825 * \brief Return true if all medium nodes of the element are in the node set
9827 //================================================================================
9829 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9831 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9832 if ( !nodeSet.count( elem->GetNode(i) ))
9838 //================================================================================
9840 * \brief Makes given elements linear
9842 //================================================================================
9844 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9846 if ( theElements.empty() ) return;
9848 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9849 set<int> mediumNodeIDs;
9850 TIDSortedElemSet::iterator eIt = theElements.begin();
9851 for ( ; eIt != theElements.end(); ++eIt )
9853 const SMDS_MeshElement* e = *eIt;
9854 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9855 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9858 // replace given elements by linear ones
9859 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9860 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9862 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9863 // except those elements sharing medium nodes of quadratic element whose medium nodes
9864 // are not all in mediumNodeIDs
9866 // get remaining medium nodes
9867 TIDSortedNodeSet mediumNodes;
9868 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9869 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9870 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9871 mediumNodes.insert( mediumNodes.end(), n );
9873 // find more quadratic elements to convert
9874 TIDSortedElemSet moreElemsToConvert;
9875 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9876 for ( ; nIt != mediumNodes.end(); ++nIt )
9878 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9879 while ( invIt->more() )
9881 const SMDS_MeshElement* e = invIt->next();
9882 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9884 // find a more complex element including e and
9885 // whose medium nodes are not in mediumNodes
9886 bool complexFound = false;
9887 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9889 SMDS_ElemIteratorPtr invIt2 =
9890 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9891 while ( invIt2->more() )
9893 const SMDS_MeshElement* eComplex = invIt2->next();
9894 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9896 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9897 if ( nbCommonNodes == e->NbNodes())
9899 complexFound = true;
9900 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9906 if ( !complexFound )
9907 moreElemsToConvert.insert( e );
9911 elemIt = elemSetIterator( moreElemsToConvert );
9912 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9915 //=======================================================================
9916 //function : SewSideElements
9918 //=======================================================================
9920 SMESH_MeshEditor::Sew_Error
9921 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9922 TIDSortedElemSet& theSide2,
9923 const SMDS_MeshNode* theFirstNode1,
9924 const SMDS_MeshNode* theFirstNode2,
9925 const SMDS_MeshNode* theSecondNode1,
9926 const SMDS_MeshNode* theSecondNode2)
9928 myLastCreatedElems.Clear();
9929 myLastCreatedNodes.Clear();
9931 MESSAGE ("::::SewSideElements()");
9932 if ( theSide1.size() != theSide2.size() )
9933 return SEW_DIFF_NB_OF_ELEMENTS;
9935 Sew_Error aResult = SEW_OK;
9937 // 1. Build set of faces representing each side
9938 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9939 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9941 // =======================================================================
9942 // 1. Build set of faces representing each side:
9943 // =======================================================================
9944 // a. build set of nodes belonging to faces
9945 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9946 // c. create temporary faces representing side of volumes if correspondent
9947 // face does not exist
9949 SMESHDS_Mesh* aMesh = GetMeshDS();
9950 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9951 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9952 TIDSortedElemSet faceSet1, faceSet2;
9953 set<const SMDS_MeshElement*> volSet1, volSet2;
9954 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9955 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9956 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9957 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9958 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9959 int iSide, iFace, iNode;
9961 list<const SMDS_MeshElement* > tempFaceList;
9962 for ( iSide = 0; iSide < 2; iSide++ ) {
9963 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9964 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9965 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9966 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9967 set<const SMDS_MeshElement*>::iterator vIt;
9968 TIDSortedElemSet::iterator eIt;
9969 set<const SMDS_MeshNode*>::iterator nIt;
9971 // check that given nodes belong to given elements
9972 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9973 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9974 int firstIndex = -1, secondIndex = -1;
9975 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9976 const SMDS_MeshElement* elem = *eIt;
9977 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9978 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9979 if ( firstIndex > -1 && secondIndex > -1 ) break;
9981 if ( firstIndex < 0 || secondIndex < 0 ) {
9982 // we can simply return until temporary faces created
9983 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9986 // -----------------------------------------------------------
9987 // 1a. Collect nodes of existing faces
9988 // and build set of face nodes in order to detect missing
9989 // faces corresponding to sides of volumes
9990 // -----------------------------------------------------------
9992 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9994 // loop on the given element of a side
9995 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9996 //const SMDS_MeshElement* elem = *eIt;
9997 const SMDS_MeshElement* elem = *eIt;
9998 if ( elem->GetType() == SMDSAbs_Face ) {
9999 faceSet->insert( elem );
10000 set <const SMDS_MeshNode*> faceNodeSet;
10001 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10002 while ( nodeIt->more() ) {
10003 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10004 nodeSet->insert( n );
10005 faceNodeSet.insert( n );
10007 setOfFaceNodeSet.insert( faceNodeSet );
10009 else if ( elem->GetType() == SMDSAbs_Volume )
10010 volSet->insert( elem );
10012 // ------------------------------------------------------------------------------
10013 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10014 // ------------------------------------------------------------------------------
10016 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10017 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10018 while ( fIt->more() ) { // loop on faces sharing a node
10019 const SMDS_MeshElement* f = fIt->next();
10020 if ( faceSet->find( f ) == faceSet->end() ) {
10021 // check if all nodes are in nodeSet and
10022 // complete setOfFaceNodeSet if they are
10023 set <const SMDS_MeshNode*> faceNodeSet;
10024 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10025 bool allInSet = true;
10026 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10027 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10028 if ( nodeSet->find( n ) == nodeSet->end() )
10031 faceNodeSet.insert( n );
10034 faceSet->insert( f );
10035 setOfFaceNodeSet.insert( faceNodeSet );
10041 // -------------------------------------------------------------------------
10042 // 1c. Create temporary faces representing sides of volumes if correspondent
10043 // face does not exist
10044 // -------------------------------------------------------------------------
10046 if ( !volSet->empty() ) {
10047 //int nodeSetSize = nodeSet->size();
10049 // loop on given volumes
10050 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10051 SMDS_VolumeTool vol (*vIt);
10052 // loop on volume faces: find free faces
10053 // --------------------------------------
10054 list<const SMDS_MeshElement* > freeFaceList;
10055 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10056 if ( !vol.IsFreeFace( iFace ))
10058 // check if there is already a face with same nodes in a face set
10059 const SMDS_MeshElement* aFreeFace = 0;
10060 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10061 int nbNodes = vol.NbFaceNodes( iFace );
10062 set <const SMDS_MeshNode*> faceNodeSet;
10063 vol.GetFaceNodes( iFace, faceNodeSet );
10064 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10066 // no such a face is given but it still can exist, check it
10067 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10068 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10070 if ( !aFreeFace ) {
10071 // create a temporary face
10072 if ( nbNodes == 3 ) {
10073 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10074 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10076 else if ( nbNodes == 4 ) {
10077 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10078 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10081 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10082 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10083 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10086 tempFaceList.push_back( aFreeFace );
10090 freeFaceList.push_back( aFreeFace );
10092 } // loop on faces of a volume
10094 // choose one of several free faces of a volume
10095 // --------------------------------------------
10096 if ( freeFaceList.size() > 1 ) {
10097 // choose a face having max nb of nodes shared by other elems of a side
10098 int maxNbNodes = -1;
10099 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10100 while ( fIt != freeFaceList.end() ) { // loop on free faces
10101 int nbSharedNodes = 0;
10102 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10103 while ( nodeIt->more() ) { // loop on free face nodes
10104 const SMDS_MeshNode* n =
10105 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10106 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10107 while ( invElemIt->more() ) {
10108 const SMDS_MeshElement* e = invElemIt->next();
10109 nbSharedNodes += faceSet->count( e );
10110 nbSharedNodes += elemSet->count( e );
10113 if ( nbSharedNodes > maxNbNodes ) {
10114 maxNbNodes = nbSharedNodes;
10115 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10117 else if ( nbSharedNodes == maxNbNodes ) {
10121 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10124 if ( freeFaceList.size() > 1 )
10126 // could not choose one face, use another way
10127 // choose a face most close to the bary center of the opposite side
10128 gp_XYZ aBC( 0., 0., 0. );
10129 set <const SMDS_MeshNode*> addedNodes;
10130 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10131 eIt = elemSet2->begin();
10132 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10133 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10134 while ( nodeIt->more() ) { // loop on free face nodes
10135 const SMDS_MeshNode* n =
10136 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10137 if ( addedNodes.insert( n ).second )
10138 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10141 aBC /= addedNodes.size();
10142 double minDist = DBL_MAX;
10143 fIt = freeFaceList.begin();
10144 while ( fIt != freeFaceList.end() ) { // loop on free faces
10146 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10147 while ( nodeIt->more() ) { // loop on free face nodes
10148 const SMDS_MeshNode* n =
10149 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10150 gp_XYZ p( n->X(),n->Y(),n->Z() );
10151 dist += ( aBC - p ).SquareModulus();
10153 if ( dist < minDist ) {
10155 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10158 fIt = freeFaceList.erase( fIt++ );
10161 } // choose one of several free faces of a volume
10163 if ( freeFaceList.size() == 1 ) {
10164 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10165 faceSet->insert( aFreeFace );
10166 // complete a node set with nodes of a found free face
10167 // for ( iNode = 0; iNode < ; iNode++ )
10168 // nodeSet->insert( fNodes[ iNode ] );
10171 } // loop on volumes of a side
10173 // // complete a set of faces if new nodes in a nodeSet appeared
10174 // // ----------------------------------------------------------
10175 // if ( nodeSetSize != nodeSet->size() ) {
10176 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10177 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10178 // while ( fIt->more() ) { // loop on faces sharing a node
10179 // const SMDS_MeshElement* f = fIt->next();
10180 // if ( faceSet->find( f ) == faceSet->end() ) {
10181 // // check if all nodes are in nodeSet and
10182 // // complete setOfFaceNodeSet if they are
10183 // set <const SMDS_MeshNode*> faceNodeSet;
10184 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10185 // bool allInSet = true;
10186 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10187 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10188 // if ( nodeSet->find( n ) == nodeSet->end() )
10189 // allInSet = false;
10191 // faceNodeSet.insert( n );
10193 // if ( allInSet ) {
10194 // faceSet->insert( f );
10195 // setOfFaceNodeSet.insert( faceNodeSet );
10201 } // Create temporary faces, if there are volumes given
10204 if ( faceSet1.size() != faceSet2.size() ) {
10205 // delete temporary faces: they are in reverseElements of actual nodes
10206 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10207 // while ( tmpFaceIt->more() )
10208 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10209 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10210 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10211 // aMesh->RemoveElement(*tmpFaceIt);
10212 MESSAGE("Diff nb of faces");
10213 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10216 // ============================================================
10217 // 2. Find nodes to merge:
10218 // bind a node to remove to a node to put instead
10219 // ============================================================
10221 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10222 if ( theFirstNode1 != theFirstNode2 )
10223 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10224 if ( theSecondNode1 != theSecondNode2 )
10225 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10227 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10228 set< long > linkIdSet; // links to process
10229 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10231 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10232 list< NLink > linkList[2];
10233 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10234 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10235 // loop on links in linkList; find faces by links and append links
10236 // of the found faces to linkList
10237 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10238 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10240 NLink link[] = { *linkIt[0], *linkIt[1] };
10241 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10242 if ( !linkIdSet.count( linkID ) )
10245 // by links, find faces in the face sets,
10246 // and find indices of link nodes in the found faces;
10247 // in a face set, there is only one or no face sharing a link
10248 // ---------------------------------------------------------------
10250 const SMDS_MeshElement* face[] = { 0, 0 };
10251 vector<const SMDS_MeshNode*> fnodes[2];
10252 int iLinkNode[2][2];
10253 TIDSortedElemSet avoidSet;
10254 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10255 const SMDS_MeshNode* n1 = link[iSide].first;
10256 const SMDS_MeshNode* n2 = link[iSide].second;
10257 //cout << "Side " << iSide << " ";
10258 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10259 // find a face by two link nodes
10260 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10261 *faceSetPtr[ iSide ], avoidSet,
10262 &iLinkNode[iSide][0],
10263 &iLinkNode[iSide][1] );
10264 if ( face[ iSide ])
10266 //cout << " F " << face[ iSide]->GetID() <<endl;
10267 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10268 // put face nodes to fnodes
10269 if ( face[ iSide ]->IsQuadratic() )
10271 // use interlaced nodes iterator
10272 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10273 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10274 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10275 while ( nIter->more() )
10276 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10280 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10281 face[ iSide ]->end_nodes() );
10283 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10287 // check similarity of elements of the sides
10288 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10289 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10290 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10291 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10294 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10296 break; // do not return because it's necessary to remove tmp faces
10299 // set nodes to merge
10300 // -------------------
10302 if ( face[0] && face[1] ) {
10303 const int nbNodes = face[0]->NbNodes();
10304 if ( nbNodes != face[1]->NbNodes() ) {
10305 MESSAGE("Diff nb of face nodes");
10306 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10307 break; // do not return because it s necessary to remove tmp faces
10309 bool reverse[] = { false, false }; // order of nodes in the link
10310 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10311 // analyse link orientation in faces
10312 int i1 = iLinkNode[ iSide ][ 0 ];
10313 int i2 = iLinkNode[ iSide ][ 1 ];
10314 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10316 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10317 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10318 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10320 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10321 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10324 // add other links of the faces to linkList
10325 // -----------------------------------------
10327 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10328 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10329 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10330 if ( !iter_isnew.second ) { // already in a set: no need to process
10331 linkIdSet.erase( iter_isnew.first );
10333 else // new in set == encountered for the first time: add
10335 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10336 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10337 linkList[0].push_back ( NLink( n1, n2 ));
10338 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10343 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10346 } // loop on link lists
10348 if ( aResult == SEW_OK &&
10349 ( //linkIt[0] != linkList[0].end() ||
10350 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10351 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10352 " " << (faceSetPtr[1]->empty()));
10353 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10356 // ====================================================================
10357 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10358 // ====================================================================
10360 // delete temporary faces
10361 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10362 // while ( tmpFaceIt->more() )
10363 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10364 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10365 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10366 aMesh->RemoveElement(*tmpFaceIt);
10368 if ( aResult != SEW_OK)
10371 list< int > nodeIDsToRemove;
10372 vector< const SMDS_MeshNode*> nodes;
10373 ElemFeatures elemType;
10375 // loop on nodes replacement map
10376 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10377 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10378 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10380 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10381 nodeIDsToRemove.push_back( nToRemove->GetID() );
10382 // loop on elements sharing nToRemove
10383 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10384 while ( invElemIt->more() ) {
10385 const SMDS_MeshElement* e = invElemIt->next();
10386 // get a new suite of nodes: make replacement
10387 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10388 nodes.resize( nbNodes );
10389 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10390 while ( nIt->more() ) {
10391 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10392 nnIt = nReplaceMap.find( n );
10393 if ( nnIt != nReplaceMap.end() ) {
10395 n = (*nnIt).second;
10399 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10400 // elemIDsToRemove.push_back( e->GetID() );
10404 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10405 aMesh->RemoveElement( e );
10407 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10409 AddToSameGroups( newElem, e, aMesh );
10410 if ( int aShapeId = e->getshapeId() )
10411 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10417 Remove( nodeIDsToRemove, true );
10422 //================================================================================
10424 * \brief Find corresponding nodes in two sets of faces
10425 * \param theSide1 - first face set
10426 * \param theSide2 - second first face
10427 * \param theFirstNode1 - a boundary node of set 1
10428 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10429 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10430 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10431 * \param nReplaceMap - output map of corresponding nodes
10432 * \return bool - is a success or not
10434 //================================================================================
10437 //#define DEBUG_MATCHING_NODES
10440 SMESH_MeshEditor::Sew_Error
10441 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10442 set<const SMDS_MeshElement*>& theSide2,
10443 const SMDS_MeshNode* theFirstNode1,
10444 const SMDS_MeshNode* theFirstNode2,
10445 const SMDS_MeshNode* theSecondNode1,
10446 const SMDS_MeshNode* theSecondNode2,
10447 TNodeNodeMap & nReplaceMap)
10449 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10451 nReplaceMap.clear();
10452 if ( theFirstNode1 != theFirstNode2 )
10453 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10454 if ( theSecondNode1 != theSecondNode2 )
10455 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10457 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10458 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10460 list< NLink > linkList[2];
10461 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10462 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10464 // loop on links in linkList; find faces by links and append links
10465 // of the found faces to linkList
10466 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10467 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10468 NLink link[] = { *linkIt[0], *linkIt[1] };
10469 if ( linkSet.find( link[0] ) == linkSet.end() )
10472 // by links, find faces in the face sets,
10473 // and find indices of link nodes in the found faces;
10474 // in a face set, there is only one or no face sharing a link
10475 // ---------------------------------------------------------------
10477 const SMDS_MeshElement* face[] = { 0, 0 };
10478 list<const SMDS_MeshNode*> notLinkNodes[2];
10479 //bool reverse[] = { false, false }; // order of notLinkNodes
10481 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10483 const SMDS_MeshNode* n1 = link[iSide].first;
10484 const SMDS_MeshNode* n2 = link[iSide].second;
10485 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10486 set< const SMDS_MeshElement* > facesOfNode1;
10487 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10489 // during a loop of the first node, we find all faces around n1,
10490 // during a loop of the second node, we find one face sharing both n1 and n2
10491 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10492 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10493 while ( fIt->more() ) { // loop on faces sharing a node
10494 const SMDS_MeshElement* f = fIt->next();
10495 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10496 ! facesOfNode1.insert( f ).second ) // f encounters twice
10498 if ( face[ iSide ] ) {
10499 MESSAGE( "2 faces per link " );
10500 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10503 faceSet->erase( f );
10505 // get not link nodes
10506 int nbN = f->NbNodes();
10507 if ( f->IsQuadratic() )
10509 nbNodes[ iSide ] = nbN;
10510 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10511 int i1 = f->GetNodeIndex( n1 );
10512 int i2 = f->GetNodeIndex( n2 );
10513 int iEnd = nbN, iBeg = -1, iDelta = 1;
10514 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10516 std::swap( iEnd, iBeg ); iDelta = -1;
10521 if ( i == iEnd ) i = iBeg + iDelta;
10522 if ( i == i1 ) break;
10523 nodes.push_back ( f->GetNode( i ) );
10529 // check similarity of elements of the sides
10530 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10531 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10532 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10533 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10536 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10540 // set nodes to merge
10541 // -------------------
10543 if ( face[0] && face[1] ) {
10544 if ( nbNodes[0] != nbNodes[1] ) {
10545 MESSAGE("Diff nb of face nodes");
10546 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10548 #ifdef DEBUG_MATCHING_NODES
10549 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10550 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10551 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10553 int nbN = nbNodes[0];
10555 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10556 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10557 for ( int i = 0 ; i < nbN - 2; ++i ) {
10558 #ifdef DEBUG_MATCHING_NODES
10559 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10561 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10565 // add other links of the face 1 to linkList
10566 // -----------------------------------------
10568 const SMDS_MeshElement* f0 = face[0];
10569 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10570 for ( int i = 0; i < nbN; i++ )
10572 const SMDS_MeshNode* n2 = f0->GetNode( i );
10573 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10574 linkSet.insert( SMESH_TLink( n1, n2 ));
10575 if ( !iter_isnew.second ) { // already in a set: no need to process
10576 linkSet.erase( iter_isnew.first );
10578 else // new in set == encountered for the first time: add
10580 #ifdef DEBUG_MATCHING_NODES
10581 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10582 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10584 linkList[0].push_back ( NLink( n1, n2 ));
10585 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10590 } // loop on link lists
10595 //================================================================================
10597 * \brief Create elements equal (on same nodes) to given ones
10598 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10599 * elements of the uppest dimension are duplicated.
10601 //================================================================================
10603 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10605 ClearLastCreated();
10606 SMESHDS_Mesh* mesh = GetMeshDS();
10608 // get an element type and an iterator over elements
10610 SMDSAbs_ElementType type;
10611 SMDS_ElemIteratorPtr elemIt;
10612 vector< const SMDS_MeshElement* > allElems;
10613 if ( theElements.empty() )
10615 if ( mesh->NbNodes() == 0 )
10617 // get most complex type
10618 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10619 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10620 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10622 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10623 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10628 // put all elements in the vector <allElems>
10629 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10630 elemIt = mesh->elementsIterator( type );
10631 while ( elemIt->more() )
10632 allElems.push_back( elemIt->next());
10633 elemIt = elemSetIterator( allElems );
10637 type = (*theElements.begin())->GetType();
10638 elemIt = elemSetIterator( theElements );
10641 // duplicate elements
10643 ElemFeatures elemType;
10645 vector< const SMDS_MeshNode* > nodes;
10646 while ( elemIt->more() )
10648 const SMDS_MeshElement* elem = elemIt->next();
10649 if ( elem->GetType() != type )
10652 elemType.Init( elem, /*basicOnly=*/false );
10653 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10655 AddElement( nodes, elemType );
10659 //================================================================================
10661 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10662 \param theElems - the list of elements (edges or faces) to be replicated
10663 The nodes for duplication could be found from these elements
10664 \param theNodesNot - list of nodes to NOT replicate
10665 \param theAffectedElems - the list of elements (cells and edges) to which the
10666 replicated nodes should be associated to.
10667 \return TRUE if operation has been completed successfully, FALSE otherwise
10669 //================================================================================
10671 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10672 const TIDSortedElemSet& theNodesNot,
10673 const TIDSortedElemSet& theAffectedElems )
10675 myLastCreatedElems.Clear();
10676 myLastCreatedNodes.Clear();
10678 if ( theElems.size() == 0 )
10681 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10686 TNodeNodeMap anOldNodeToNewNode;
10687 // duplicate elements and nodes
10688 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10689 // replce nodes by duplications
10690 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10694 //================================================================================
10696 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10697 \param theMeshDS - mesh instance
10698 \param theElems - the elements replicated or modified (nodes should be changed)
10699 \param theNodesNot - nodes to NOT replicate
10700 \param theNodeNodeMap - relation of old node to new created node
10701 \param theIsDoubleElem - flag os to replicate element or modify
10702 \return TRUE if operation has been completed successfully, FALSE otherwise
10704 //================================================================================
10706 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10707 const TIDSortedElemSet& theElems,
10708 const TIDSortedElemSet& theNodesNot,
10709 TNodeNodeMap& theNodeNodeMap,
10710 const bool theIsDoubleElem )
10712 MESSAGE("doubleNodes");
10713 // iterate through element and duplicate them (by nodes duplication)
10715 std::vector<const SMDS_MeshNode*> newNodes;
10716 ElemFeatures elemType;
10718 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10719 for ( ; elemItr != theElems.end(); ++elemItr )
10721 const SMDS_MeshElement* anElem = *elemItr;
10725 // duplicate nodes to duplicate element
10726 bool isDuplicate = false;
10727 newNodes.resize( anElem->NbNodes() );
10728 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10730 while ( anIter->more() )
10732 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10733 const SMDS_MeshNode* aNewNode = aCurrNode;
10734 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10735 if ( n2n != theNodeNodeMap.end() )
10737 aNewNode = n2n->second;
10739 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10742 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10743 copyPosition( aCurrNode, aNewNode );
10744 theNodeNodeMap[ aCurrNode ] = aNewNode;
10745 myLastCreatedNodes.Append( aNewNode );
10747 isDuplicate |= (aCurrNode != aNewNode);
10748 newNodes[ ind++ ] = aNewNode;
10750 if ( !isDuplicate )
10753 if ( theIsDoubleElem )
10754 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10756 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10763 //================================================================================
10765 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10766 \param theNodes - identifiers of nodes to be doubled
10767 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10768 nodes. If list of element identifiers is empty then nodes are doubled but
10769 they not assigned to elements
10770 \return TRUE if operation has been completed successfully, FALSE otherwise
10772 //================================================================================
10774 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10775 const std::list< int >& theListOfModifiedElems )
10777 MESSAGE("DoubleNodes");
10778 myLastCreatedElems.Clear();
10779 myLastCreatedNodes.Clear();
10781 if ( theListOfNodes.size() == 0 )
10784 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10788 // iterate through nodes and duplicate them
10790 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10792 std::list< int >::const_iterator aNodeIter;
10793 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10795 int aCurr = *aNodeIter;
10796 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10802 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10805 copyPosition( aNode, aNewNode );
10806 anOldNodeToNewNode[ aNode ] = aNewNode;
10807 myLastCreatedNodes.Append( aNewNode );
10811 // Create map of new nodes for modified elements
10813 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10815 std::list< int >::const_iterator anElemIter;
10816 for ( anElemIter = theListOfModifiedElems.begin();
10817 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10819 int aCurr = *anElemIter;
10820 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10824 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10826 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10828 while ( anIter->more() )
10830 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10831 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10833 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10834 aNodeArr[ ind++ ] = aNewNode;
10837 aNodeArr[ ind++ ] = aCurrNode;
10839 anElemToNodes[ anElem ] = aNodeArr;
10842 // Change nodes of elements
10844 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10845 anElemToNodesIter = anElemToNodes.begin();
10846 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10848 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10849 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10852 MESSAGE("ChangeElementNodes");
10853 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10862 //================================================================================
10864 \brief Check if element located inside shape
10865 \return TRUE if IN or ON shape, FALSE otherwise
10867 //================================================================================
10869 template<class Classifier>
10870 bool isInside(const SMDS_MeshElement* theElem,
10871 Classifier& theClassifier,
10872 const double theTol)
10874 gp_XYZ centerXYZ (0, 0, 0);
10875 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10876 while (aNodeItr->more())
10877 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10879 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10880 theClassifier.Perform(aPnt, theTol);
10881 TopAbs_State aState = theClassifier.State();
10882 return (aState == TopAbs_IN || aState == TopAbs_ON );
10885 //================================================================================
10887 * \brief Classifier of the 3D point on the TopoDS_Face
10888 * with interaface suitable for isInside()
10890 //================================================================================
10892 struct _FaceClassifier
10894 Extrema_ExtPS _extremum;
10895 BRepAdaptor_Surface _surface;
10896 TopAbs_State _state;
10898 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10900 _extremum.Initialize( _surface,
10901 _surface.FirstUParameter(), _surface.LastUParameter(),
10902 _surface.FirstVParameter(), _surface.LastVParameter(),
10903 _surface.Tolerance(), _surface.Tolerance() );
10905 void Perform(const gp_Pnt& aPnt, double theTol)
10908 _state = TopAbs_OUT;
10909 _extremum.Perform(aPnt);
10910 if ( _extremum.IsDone() )
10911 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10912 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10914 TopAbs_State State() const
10921 //================================================================================
10923 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10924 This method is the first step of DoubleNodeElemGroupsInRegion.
10925 \param theElems - list of groups of elements (edges or faces) to be replicated
10926 \param theNodesNot - list of groups of nodes not to replicated
10927 \param theShape - shape to detect affected elements (element which geometric center
10928 located on or inside shape). If the shape is null, detection is done on faces orientations
10929 (select elements with a gravity center on the side given by faces normals).
10930 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10931 The replicated nodes should be associated to affected elements.
10932 \return groups of affected elements
10933 \sa DoubleNodeElemGroupsInRegion()
10935 //================================================================================
10937 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10938 const TIDSortedElemSet& theNodesNot,
10939 const TopoDS_Shape& theShape,
10940 TIDSortedElemSet& theAffectedElems)
10942 if ( theShape.IsNull() )
10944 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10945 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10946 std::set<const SMDS_MeshElement*> edgesToCheck;
10947 alreadyCheckedNodes.clear();
10948 alreadyCheckedElems.clear();
10949 edgesToCheck.clear();
10951 // --- iterates on elements to be replicated and get elements by back references from their nodes
10953 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10955 for ( ielem=1; elemItr != theElems.end(); ++elemItr )
10957 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10958 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10961 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10962 MESSAGE("element " << ielem++ << " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10963 std::set<const SMDS_MeshNode*> nodesElem;
10965 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10966 while ( nodeItr->more() )
10968 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10969 nodesElem.insert(aNode);
10971 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10972 for (; nodit != nodesElem.end(); nodit++)
10974 MESSAGE(" noeud ");
10975 const SMDS_MeshNode* aNode = *nodit;
10976 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10978 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10980 alreadyCheckedNodes.insert(aNode);
10981 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10982 while ( backElemItr->more() )
10984 MESSAGE(" backelem ");
10985 const SMDS_MeshElement* curElem = backElemItr->next();
10986 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10988 if (theElems.find(curElem) != theElems.end())
10990 alreadyCheckedElems.insert(curElem);
10991 double x=0, y=0, z=0;
10993 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10994 while ( nodeItr2->more() )
10996 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10997 x += anotherNode->X();
10998 y += anotherNode->Y();
10999 z += anotherNode->Z();
11003 p.SetCoord( x/nb -aNode->X(),
11005 z/nb -aNode->Z() );
11006 MESSAGE(" check " << p.X() << " " << p.Y() << " " << p.Z());
11009 MESSAGE(" --- inserted")
11010 theAffectedElems.insert( curElem );
11012 else if (curElem->GetType() == SMDSAbs_Edge)
11013 edgesToCheck.insert(curElem);
11017 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
11018 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
11019 for( ; eit != edgesToCheck.end(); eit++)
11021 bool onside = true;
11022 const SMDS_MeshElement* anEdge = *eit;
11023 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
11024 while ( nodeItr->more() )
11026 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11027 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
11035 MESSAGE(" --- edge onside inserted")
11036 theAffectedElems.insert(anEdge);
11042 const double aTol = Precision::Confusion();
11043 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11044 auto_ptr<_FaceClassifier> aFaceClassifier;
11045 if ( theShape.ShapeType() == TopAbs_SOLID )
11047 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11048 bsc3d->PerformInfinitePoint(aTol);
11050 else if (theShape.ShapeType() == TopAbs_FACE )
11052 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11055 // iterates on indicated elements and get elements by back references from their nodes
11056 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11058 for ( ielem = 1; elemItr != theElems.end(); ++elemItr )
11060 MESSAGE("element " << ielem++);
11061 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11064 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11065 while ( nodeItr->more() )
11067 MESSAGE(" noeud ");
11068 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11069 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11071 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11072 while ( backElemItr->more() )
11074 MESSAGE(" backelem ");
11075 const SMDS_MeshElement* curElem = backElemItr->next();
11076 if ( curElem && theElems.find(curElem) == theElems.end() &&
11078 isInside( curElem, *bsc3d, aTol ) :
11079 isInside( curElem, *aFaceClassifier, aTol )))
11080 theAffectedElems.insert( curElem );
11088 //================================================================================
11090 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11091 \param theElems - group of of elements (edges or faces) to be replicated
11092 \param theNodesNot - group of nodes not to replicate
11093 \param theShape - shape to detect affected elements (element which geometric center
11094 located on or inside shape).
11095 The replicated nodes should be associated to affected elements.
11096 \return TRUE if operation has been completed successfully, FALSE otherwise
11098 //================================================================================
11100 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11101 const TIDSortedElemSet& theNodesNot,
11102 const TopoDS_Shape& theShape )
11104 if ( theShape.IsNull() )
11107 const double aTol = Precision::Confusion();
11108 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11109 auto_ptr<_FaceClassifier> aFaceClassifier;
11110 if ( theShape.ShapeType() == TopAbs_SOLID )
11112 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11113 bsc3d->PerformInfinitePoint(aTol);
11115 else if (theShape.ShapeType() == TopAbs_FACE )
11117 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11120 // iterates on indicated elements and get elements by back references from their nodes
11121 TIDSortedElemSet anAffected;
11122 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11123 for ( ; elemItr != theElems.end(); ++elemItr )
11125 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11129 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11130 while ( nodeItr->more() )
11132 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11133 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11135 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11136 while ( backElemItr->more() )
11138 const SMDS_MeshElement* curElem = backElemItr->next();
11139 if ( curElem && theElems.find(curElem) == theElems.end() &&
11141 isInside( curElem, *bsc3d, aTol ) :
11142 isInside( curElem, *aFaceClassifier, aTol )))
11143 anAffected.insert( curElem );
11147 return DoubleNodes( theElems, theNodesNot, anAffected );
11151 * \brief compute an oriented angle between two planes defined by four points.
11152 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11153 * @param p0 base of the rotation axe
11154 * @param p1 extremity of the rotation axe
11155 * @param g1 belongs to the first plane
11156 * @param g2 belongs to the second plane
11158 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11160 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11161 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11162 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11163 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11164 gp_Vec vref(p0, p1);
11167 gp_Vec n1 = vref.Crossed(v1);
11168 gp_Vec n2 = vref.Crossed(v2);
11170 return n2.AngleWithRef(n1, vref);
11172 catch ( Standard_Failure ) {
11174 return Max( v1.Magnitude(), v2.Magnitude() );
11178 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11179 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11180 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11181 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11182 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11183 * 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.
11184 * 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.
11185 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11186 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11187 * \param theElems - list of groups of volumes, where a group of volume is a set of
11188 * SMDS_MeshElements sorted by Id.
11189 * \param createJointElems - if TRUE, create the elements
11190 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11191 * the boundary between \a theDomains and the rest mesh
11192 * \return TRUE if operation has been completed successfully, FALSE otherwise
11194 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11195 bool createJointElems,
11196 bool onAllBoundaries)
11198 MESSAGE("----------------------------------------------");
11199 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11200 MESSAGE("----------------------------------------------");
11202 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11203 meshDS->BuildDownWardConnectivity(true);
11205 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11207 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11208 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11209 // build the list of nodes shared by 2 or more domains, with their domain indexes
11211 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11212 std::map<int,int>celldom; // cell vtkId --> domain
11213 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11214 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11215 faceDomains.clear();
11217 cellDomains.clear();
11218 nodeDomains.clear();
11219 std::map<int,int> emptyMap;
11220 std::set<int> emptySet;
11223 MESSAGE(".. Number of domains :"<<theElems.size());
11225 TIDSortedElemSet theRestDomElems;
11226 const int iRestDom = -1;
11227 const int idom0 = onAllBoundaries ? iRestDom : 0;
11228 const int nbDomains = theElems.size();
11230 // Check if the domains do not share an element
11231 for (int idom = 0; idom < nbDomains-1; idom++)
11233 // MESSAGE("... Check of domain #" << idom);
11234 const TIDSortedElemSet& domain = theElems[idom];
11235 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11236 for (; elemItr != domain.end(); ++elemItr)
11238 const SMDS_MeshElement* anElem = *elemItr;
11239 int idombisdeb = idom + 1 ;
11240 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
11242 const TIDSortedElemSet& domainbis = theElems[idombis];
11243 if ( domainbis.count(anElem) )
11245 MESSAGE(".... Domain #" << idom);
11246 MESSAGE(".... Domain #" << idombis);
11247 throw SALOME_Exception("The domains are not disjoint.");
11254 for (int idom = 0; idom < nbDomains; idom++)
11257 // --- build a map (face to duplicate --> volume to modify)
11258 // with all the faces shared by 2 domains (group of elements)
11259 // and corresponding volume of this domain, for each shared face.
11260 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11262 MESSAGE("... Neighbors of domain #" << idom);
11263 const TIDSortedElemSet& domain = theElems[idom];
11264 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11265 for (; elemItr != domain.end(); ++elemItr)
11267 const SMDS_MeshElement* anElem = *elemItr;
11270 int vtkId = anElem->getVtkId();
11271 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11272 int neighborsVtkIds[NBMAXNEIGHBORS];
11273 int downIds[NBMAXNEIGHBORS];
11274 unsigned char downTypes[NBMAXNEIGHBORS];
11275 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11276 for (int n = 0; n < nbNeighbors; n++)
11278 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11279 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11280 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11283 for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11285 // MESSAGE("Domain " << idombis);
11286 const TIDSortedElemSet& domainbis = theElems[idombis];
11287 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11289 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11291 DownIdType face(downIds[n], downTypes[n]);
11292 if (!faceDomains[face].count(idom))
11294 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11295 celldom[vtkId] = idom;
11296 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11300 theRestDomElems.insert( elem );
11301 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11302 celldom[neighborsVtkIds[n]] = iRestDom;
11310 //MESSAGE("Number of shared faces " << faceDomains.size());
11311 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11313 // --- explore the shared faces domain by domain,
11314 // explore the nodes of the face and see if they belong to a cell in the domain,
11315 // which has only a node or an edge on the border (not a shared face)
11317 for (int idomain = idom0; idomain < nbDomains; idomain++)
11319 //MESSAGE("Domain " << idomain);
11320 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11321 itface = faceDomains.begin();
11322 for (; itface != faceDomains.end(); ++itface)
11324 const std::map<int, int>& domvol = itface->second;
11325 if (!domvol.count(idomain))
11327 DownIdType face = itface->first;
11328 //MESSAGE(" --- face " << face.cellId);
11329 std::set<int> oldNodes;
11331 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11332 std::set<int>::iterator itn = oldNodes.begin();
11333 for (; itn != oldNodes.end(); ++itn)
11336 //MESSAGE(" node " << oldId);
11337 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11338 for (int i=0; i<l.ncells; i++)
11340 int vtkId = l.cells[i];
11341 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11342 if (!domain.count(anElem))
11344 int vtkType = grid->GetCellType(vtkId);
11345 int downId = grid->CellIdToDownId(vtkId);
11348 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11349 continue; // not OK at this stage of the algorithm:
11350 //no cells created after BuildDownWardConnectivity
11352 DownIdType aCell(downId, vtkType);
11353 cellDomains[aCell][idomain] = vtkId;
11354 celldom[vtkId] = idomain;
11355 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11361 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11362 // for each shared face, get the nodes
11363 // for each node, for each domain of the face, create a clone of the node
11365 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11366 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11367 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11369 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11370 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11371 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11373 MESSAGE(".. Duplication of the nodes");
11374 for (int idomain = idom0; idomain < nbDomains; idomain++)
11376 itface = faceDomains.begin();
11377 for (; itface != faceDomains.end(); ++itface)
11379 const std::map<int, int>& domvol = itface->second;
11380 if (!domvol.count(idomain))
11382 DownIdType face = itface->first;
11383 //MESSAGE(" --- face " << face.cellId);
11384 std::set<int> oldNodes;
11386 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11387 std::set<int>::iterator itn = oldNodes.begin();
11388 for (; itn != oldNodes.end(); ++itn)
11391 if (nodeDomains[oldId].empty())
11393 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11394 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11396 std::map<int, int>::const_iterator itdom = domvol.begin();
11397 for (; itdom != domvol.end(); ++itdom)
11399 int idom = itdom->first;
11400 //MESSAGE(" domain " << idom);
11401 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11403 if (nodeDomains[oldId].size() >= 2) // a multiple node
11405 vector<int> orderedDoms;
11406 //MESSAGE("multiple node " << oldId);
11407 if (mutipleNodes.count(oldId))
11408 orderedDoms = mutipleNodes[oldId];
11411 map<int,int>::iterator it = nodeDomains[oldId].begin();
11412 for (; it != nodeDomains[oldId].end(); ++it)
11413 orderedDoms.push_back(it->first);
11415 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11416 //stringstream txt;
11417 //for (int i=0; i<orderedDoms.size(); i++)
11418 // txt << orderedDoms[i] << " ";
11419 //MESSAGE("orderedDoms " << txt.str());
11420 mutipleNodes[oldId] = orderedDoms;
11422 double *coords = grid->GetPoint(oldId);
11423 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11424 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11425 int newId = newNode->getVtkId();
11426 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11427 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11434 MESSAGE(".. Creation of elements");
11435 for (int idomain = idom0; idomain < nbDomains; idomain++)
11437 itface = faceDomains.begin();
11438 for (; itface != faceDomains.end(); ++itface)
11440 std::map<int, int> domvol = itface->second;
11441 if (!domvol.count(idomain))
11443 DownIdType face = itface->first;
11444 //MESSAGE(" --- face " << face.cellId);
11445 std::set<int> oldNodes;
11447 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11448 int nbMultipleNodes = 0;
11449 std::set<int>::iterator itn = oldNodes.begin();
11450 for (; itn != oldNodes.end(); ++itn)
11453 if (mutipleNodes.count(oldId))
11456 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11458 //MESSAGE("multiple Nodes detected on a shared face");
11459 int downId = itface->first.cellId;
11460 unsigned char cellType = itface->first.cellType;
11461 // --- shared edge or shared face ?
11462 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11465 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11466 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11467 if (mutipleNodes.count(nodes[i]))
11468 if (!mutipleNodesToFace.count(nodes[i]))
11469 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11471 else // shared face (between two volumes)
11473 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11474 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11475 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11476 for (int ie =0; ie < nbEdges; ie++)
11479 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11480 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11482 vector<int> vn0 = mutipleNodes[nodes[0]];
11483 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11485 for (int i0 = 0; i0 < vn0.size(); i0++)
11486 for (int i1 = 0; i1 < vn1.size(); i1++)
11487 if (vn0[i0] == vn1[i1])
11488 doms.push_back(vn0[i0]);
11489 if (doms.size() >2)
11491 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11492 double *coords = grid->GetPoint(nodes[0]);
11493 gp_Pnt p0(coords[0], coords[1], coords[2]);
11494 coords = grid->GetPoint(nodes[nbNodes - 1]);
11495 gp_Pnt p1(coords[0], coords[1], coords[2]);
11497 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11498 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11499 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11500 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11501 for (int id=0; id < doms.size(); id++)
11503 int idom = doms[id];
11504 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11505 for (int ivol=0; ivol<nbvol; ivol++)
11507 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11508 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11509 if (domain.count(elem))
11511 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11512 domvol[idom] = svol;
11513 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11515 vtkIdType npts = 0;
11516 vtkIdType* pts = 0;
11517 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11518 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11521 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11522 angleDom[idom] = 0;
11526 gp_Pnt g(values[0], values[1], values[2]);
11527 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11528 //MESSAGE(" angle=" << angleDom[idom]);
11534 map<double, int> sortedDom; // sort domains by angle
11535 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11536 sortedDom[ia->second] = ia->first;
11537 vector<int> vnodes;
11539 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11541 vdom.push_back(ib->second);
11542 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11544 for (int ino = 0; ino < nbNodes; ino++)
11545 vnodes.push_back(nodes[ino]);
11546 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11555 // --- iterate on shared faces (volumes to modify, face to extrude)
11556 // get node id's of the face (id SMDS = id VTK)
11557 // create flat element with old and new nodes if requested
11559 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11560 // (domain1 X domain2) = domain1 + MAXINT*domain2
11562 std::map<int, std::map<long,int> > nodeQuadDomains;
11563 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11565 MESSAGE(".. Creation of elements: simple junction");
11566 if (createJointElems)
11569 string joints2DName = "joints2D";
11570 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11571 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11572 string joints3DName = "joints3D";
11573 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11574 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11576 itface = faceDomains.begin();
11577 for (; itface != faceDomains.end(); ++itface)
11579 DownIdType face = itface->first;
11580 std::set<int> oldNodes;
11581 std::set<int>::iterator itn;
11583 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11585 std::map<int, int> domvol = itface->second;
11586 std::map<int, int>::iterator itdom = domvol.begin();
11587 int dom1 = itdom->first;
11588 int vtkVolId = itdom->second;
11590 int dom2 = itdom->first;
11591 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11593 stringstream grpname;
11596 grpname << dom1 << "_" << dom2;
11598 grpname << dom2 << "_" << dom1;
11599 string namegrp = grpname.str();
11600 if (!mapOfJunctionGroups.count(namegrp))
11601 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11602 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11604 sgrp->Add(vol->GetID());
11605 if (vol->GetType() == SMDSAbs_Volume)
11606 joints3DGrp->Add(vol->GetID());
11607 else if (vol->GetType() == SMDSAbs_Face)
11608 joints2DGrp->Add(vol->GetID());
11612 // --- create volumes on multiple domain intersection if requested
11613 // iterate on mutipleNodesToFace
11614 // iterate on edgesMultiDomains
11616 MESSAGE(".. Creation of elements: multiple junction");
11617 if (createJointElems)
11619 // --- iterate on mutipleNodesToFace
11621 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11622 for (; itn != mutipleNodesToFace.end(); ++itn)
11624 int node = itn->first;
11625 vector<int> orderDom = itn->second;
11626 vector<vtkIdType> orderedNodes;
11627 for (int idom = 0; idom <orderDom.size(); idom++)
11628 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11629 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11631 stringstream grpname;
11633 grpname << 0 << "_" << 0;
11635 string namegrp = grpname.str();
11636 if (!mapOfJunctionGroups.count(namegrp))
11637 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11638 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11640 sgrp->Add(face->GetID());
11643 // --- iterate on edgesMultiDomains
11645 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11646 for (; ite != edgesMultiDomains.end(); ++ite)
11648 vector<int> nodes = ite->first;
11649 vector<int> orderDom = ite->second;
11650 vector<vtkIdType> orderedNodes;
11651 if (nodes.size() == 2)
11653 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11654 for (int ino=0; ino < nodes.size(); ino++)
11655 if (orderDom.size() == 3)
11656 for (int idom = 0; idom <orderDom.size(); idom++)
11657 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11659 for (int idom = orderDom.size()-1; idom >=0; idom--)
11660 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11661 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11664 string namegrp = "jointsMultiples";
11665 if (!mapOfJunctionGroups.count(namegrp))
11666 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11667 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11669 sgrp->Add(vol->GetID());
11673 INFOS("Quadratic multiple joints not implemented");
11674 // TODO quadratic nodes
11679 // --- list the explicit faces and edges of the mesh that need to be modified,
11680 // i.e. faces and edges built with one or more duplicated nodes.
11681 // associate these faces or edges to their corresponding domain.
11682 // only the first domain found is kept when a face or edge is shared
11684 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11685 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11686 faceOrEdgeDom.clear();
11689 MESSAGE(".. Modification of elements");
11690 for (int idomain = idom0; idomain < nbDomains; idomain++)
11692 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11693 for (; itnod != nodeDomains.end(); ++itnod)
11695 int oldId = itnod->first;
11696 //MESSAGE(" node " << oldId);
11697 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11698 for (int i = 0; i < l.ncells; i++)
11700 int vtkId = l.cells[i];
11701 int vtkType = grid->GetCellType(vtkId);
11702 int downId = grid->CellIdToDownId(vtkId);
11704 continue; // new cells: not to be modified
11705 DownIdType aCell(downId, vtkType);
11706 int volParents[1000];
11707 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11708 for (int j = 0; j < nbvol; j++)
11709 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11710 if (!feDom.count(vtkId))
11712 feDom[vtkId] = idomain;
11713 faceOrEdgeDom[aCell] = emptyMap;
11714 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11715 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11716 // << " type " << vtkType << " downId " << downId);
11722 // --- iterate on shared faces (volumes to modify, face to extrude)
11723 // get node id's of the face
11724 // replace old nodes by new nodes in volumes, and update inverse connectivity
11726 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11727 for (int m=0; m<3; m++)
11729 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11730 itface = (*amap).begin();
11731 for (; itface != (*amap).end(); ++itface)
11733 DownIdType face = itface->first;
11734 std::set<int> oldNodes;
11735 std::set<int>::iterator itn;
11737 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11738 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11739 std::map<int, int> localClonedNodeIds;
11741 std::map<int, int> domvol = itface->second;
11742 std::map<int, int>::iterator itdom = domvol.begin();
11743 for (; itdom != domvol.end(); ++itdom)
11745 int idom = itdom->first;
11746 int vtkVolId = itdom->second;
11747 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11748 localClonedNodeIds.clear();
11749 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11752 if (nodeDomains[oldId].count(idom))
11754 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11755 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11758 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11763 // Remove empty groups (issue 0022812)
11764 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11765 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11767 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11768 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11771 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11772 grid->BuildLinks();
11780 * \brief Double nodes on some external faces and create flat elements.
11781 * Flat elements are mainly used by some types of mechanic calculations.
11783 * Each group of the list must be constituted of faces.
11784 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11785 * @param theElems - list of groups of faces, where a group of faces is a set of
11786 * SMDS_MeshElements sorted by Id.
11787 * @return TRUE if operation has been completed successfully, FALSE otherwise
11789 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11791 MESSAGE("-------------------------------------------------");
11792 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11793 MESSAGE("-------------------------------------------------");
11795 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11797 // --- For each group of faces
11798 // duplicate the nodes, create a flat element based on the face
11799 // replace the nodes of the faces by their clones
11801 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11802 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11803 clonedNodes.clear();
11804 intermediateNodes.clear();
11805 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11806 mapOfJunctionGroups.clear();
11808 for (int idom = 0; idom < theElems.size(); idom++)
11810 const TIDSortedElemSet& domain = theElems[idom];
11811 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11812 for (; elemItr != domain.end(); ++elemItr)
11814 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11815 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11818 // MESSAGE("aFace=" << aFace->GetID());
11819 bool isQuad = aFace->IsQuadratic();
11820 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11822 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11824 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11825 while (nodeIt->more())
11827 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11828 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11830 ln2.push_back(node);
11832 ln0.push_back(node);
11834 const SMDS_MeshNode* clone = 0;
11835 if (!clonedNodes.count(node))
11837 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11838 copyPosition( node, clone );
11839 clonedNodes[node] = clone;
11842 clone = clonedNodes[node];
11845 ln3.push_back(clone);
11847 ln1.push_back(clone);
11849 const SMDS_MeshNode* inter = 0;
11850 if (isQuad && (!isMedium))
11852 if (!intermediateNodes.count(node))
11854 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11855 copyPosition( node, inter );
11856 intermediateNodes[node] = inter;
11859 inter = intermediateNodes[node];
11860 ln4.push_back(inter);
11864 // --- extrude the face
11866 vector<const SMDS_MeshNode*> ln;
11867 SMDS_MeshVolume* vol = 0;
11868 vtkIdType aType = aFace->GetVtkType();
11872 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11873 // MESSAGE("vol prism " << vol->GetID());
11874 ln.push_back(ln1[0]);
11875 ln.push_back(ln1[1]);
11876 ln.push_back(ln1[2]);
11879 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11880 // MESSAGE("vol hexa " << vol->GetID());
11881 ln.push_back(ln1[0]);
11882 ln.push_back(ln1[1]);
11883 ln.push_back(ln1[2]);
11884 ln.push_back(ln1[3]);
11886 case VTK_QUADRATIC_TRIANGLE:
11887 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11888 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11889 // MESSAGE("vol quad prism " << vol->GetID());
11890 ln.push_back(ln1[0]);
11891 ln.push_back(ln1[1]);
11892 ln.push_back(ln1[2]);
11893 ln.push_back(ln3[0]);
11894 ln.push_back(ln3[1]);
11895 ln.push_back(ln3[2]);
11897 case VTK_QUADRATIC_QUAD:
11898 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11899 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11900 // ln4[0], ln4[1], ln4[2], ln4[3]);
11901 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11902 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11903 ln4[0], ln4[1], ln4[2], ln4[3]);
11904 // MESSAGE("vol quad hexa " << vol->GetID());
11905 ln.push_back(ln1[0]);
11906 ln.push_back(ln1[1]);
11907 ln.push_back(ln1[2]);
11908 ln.push_back(ln1[3]);
11909 ln.push_back(ln3[0]);
11910 ln.push_back(ln3[1]);
11911 ln.push_back(ln3[2]);
11912 ln.push_back(ln3[3]);
11922 stringstream grpname;
11926 string namegrp = grpname.str();
11927 if (!mapOfJunctionGroups.count(namegrp))
11928 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11929 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11931 sgrp->Add(vol->GetID());
11934 // --- modify the face
11936 aFace->ChangeNodes(&ln[0], ln.size());
11943 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11944 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11945 * groups of faces to remove inside the object, (idem edges).
11946 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11948 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11949 const TopoDS_Shape& theShape,
11950 SMESH_NodeSearcher* theNodeSearcher,
11951 const char* groupName,
11952 std::vector<double>& nodesCoords,
11953 std::vector<std::vector<int> >& listOfListOfNodes)
11955 MESSAGE("--------------------------------");
11956 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11957 MESSAGE("--------------------------------");
11959 // --- zone of volumes to remove is given :
11960 // 1 either by a geom shape (one or more vertices) and a radius,
11961 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11962 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11963 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11964 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11965 // defined by it's name.
11967 SMESHDS_GroupBase* groupDS = 0;
11968 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11969 while ( groupIt->more() )
11972 SMESH_Group * group = groupIt->next();
11973 if ( !group ) continue;
11974 groupDS = group->GetGroupDS();
11975 if ( !groupDS || groupDS->IsEmpty() ) continue;
11976 std::string grpName = group->GetName();
11977 //MESSAGE("grpName=" << grpName);
11978 if (grpName == groupName)
11984 bool isNodeGroup = false;
11985 bool isNodeCoords = false;
11988 if (groupDS->GetType() != SMDSAbs_Node)
11990 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11993 if (nodesCoords.size() > 0)
11994 isNodeCoords = true; // a list o nodes given by their coordinates
11995 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11997 // --- define groups to build
11999 int idg; // --- group of SMDS volumes
12000 string grpvName = groupName;
12001 grpvName += "_vol";
12002 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
12005 MESSAGE("group not created " << grpvName);
12008 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12010 int idgs; // --- group of SMDS faces on the skin
12011 string grpsName = groupName;
12012 grpsName += "_skin";
12013 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12016 MESSAGE("group not created " << grpsName);
12019 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12021 int idgi; // --- group of SMDS faces internal (several shapes)
12022 string grpiName = groupName;
12023 grpiName += "_internalFaces";
12024 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12027 MESSAGE("group not created " << grpiName);
12030 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12032 int idgei; // --- group of SMDS faces internal (several shapes)
12033 string grpeiName = groupName;
12034 grpeiName += "_internalEdges";
12035 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12038 MESSAGE("group not created " << grpeiName);
12041 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12043 // --- build downward connectivity
12045 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12046 meshDS->BuildDownWardConnectivity(true);
12047 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12049 // --- set of volumes detected inside
12051 std::set<int> setOfInsideVol;
12052 std::set<int> setOfVolToCheck;
12054 std::vector<gp_Pnt> gpnts;
12057 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12059 MESSAGE("group of nodes provided");
12060 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12061 while ( elemIt->more() )
12063 const SMDS_MeshElement* elem = elemIt->next();
12066 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12069 SMDS_MeshElement* vol = 0;
12070 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12071 while (volItr->more())
12073 vol = (SMDS_MeshElement*)volItr->next();
12074 setOfInsideVol.insert(vol->getVtkId());
12075 sgrp->Add(vol->GetID());
12079 else if (isNodeCoords)
12081 MESSAGE("list of nodes coordinates provided");
12084 while (i < nodesCoords.size()-2)
12086 double x = nodesCoords[i++];
12087 double y = nodesCoords[i++];
12088 double z = nodesCoords[i++];
12089 gp_Pnt p = gp_Pnt(x, y ,z);
12090 gpnts.push_back(p);
12091 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12095 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12097 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12098 TopTools_IndexedMapOfShape vertexMap;
12099 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12100 gp_Pnt p = gp_Pnt(0,0,0);
12101 if (vertexMap.Extent() < 1)
12104 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12106 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12107 p = BRep_Tool::Pnt(vertex);
12108 gpnts.push_back(p);
12109 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12113 if (gpnts.size() > 0)
12116 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12118 nodeId = startNode->GetID();
12119 MESSAGE("nodeId " << nodeId);
12121 double radius2 = radius*radius;
12122 MESSAGE("radius2 " << radius2);
12124 // --- volumes on start node
12126 setOfVolToCheck.clear();
12127 SMDS_MeshElement* startVol = 0;
12128 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12129 while (volItr->more())
12131 startVol = (SMDS_MeshElement*)volItr->next();
12132 setOfVolToCheck.insert(startVol->getVtkId());
12134 if (setOfVolToCheck.empty())
12136 MESSAGE("No volumes found");
12140 // --- starting with central volumes then their neighbors, check if they are inside
12141 // or outside the domain, until no more new neighbor volume is inside.
12142 // Fill the group of inside volumes
12144 std::map<int, double> mapOfNodeDistance2;
12145 mapOfNodeDistance2.clear();
12146 std::set<int> setOfOutsideVol;
12147 while (!setOfVolToCheck.empty())
12149 std::set<int>::iterator it = setOfVolToCheck.begin();
12151 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12152 bool volInside = false;
12153 vtkIdType npts = 0;
12154 vtkIdType* pts = 0;
12155 grid->GetCellPoints(vtkId, npts, pts);
12156 for (int i=0; i<npts; i++)
12158 double distance2 = 0;
12159 if (mapOfNodeDistance2.count(pts[i]))
12161 distance2 = mapOfNodeDistance2[pts[i]];
12162 MESSAGE("point " << pts[i] << " distance2 " << distance2);
12166 double *coords = grid->GetPoint(pts[i]);
12167 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12169 for (int j=0; j<gpnts.size(); j++)
12171 double d2 = aPoint.SquareDistance(gpnts[j]);
12172 if (d2 < distance2)
12175 if (distance2 < radius2)
12179 mapOfNodeDistance2[pts[i]] = distance2;
12180 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12182 if (distance2 < radius2)
12184 volInside = true; // one or more nodes inside the domain
12185 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12191 setOfInsideVol.insert(vtkId);
12192 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12193 int neighborsVtkIds[NBMAXNEIGHBORS];
12194 int downIds[NBMAXNEIGHBORS];
12195 unsigned char downTypes[NBMAXNEIGHBORS];
12196 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12197 for (int n = 0; n < nbNeighbors; n++)
12198 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12199 setOfVolToCheck.insert(neighborsVtkIds[n]);
12203 setOfOutsideVol.insert(vtkId);
12204 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12206 setOfVolToCheck.erase(vtkId);
12210 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12211 // If yes, add the volume to the inside set
12213 bool addedInside = true;
12214 std::set<int> setOfVolToReCheck;
12215 while (addedInside)
12217 MESSAGE(" --------------------------- re check");
12218 addedInside = false;
12219 std::set<int>::iterator itv = setOfInsideVol.begin();
12220 for (; itv != setOfInsideVol.end(); ++itv)
12223 int neighborsVtkIds[NBMAXNEIGHBORS];
12224 int downIds[NBMAXNEIGHBORS];
12225 unsigned char downTypes[NBMAXNEIGHBORS];
12226 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12227 for (int n = 0; n < nbNeighbors; n++)
12228 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12229 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12231 setOfVolToCheck = setOfVolToReCheck;
12232 setOfVolToReCheck.clear();
12233 while (!setOfVolToCheck.empty())
12235 std::set<int>::iterator it = setOfVolToCheck.begin();
12237 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12239 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12240 int countInside = 0;
12241 int neighborsVtkIds[NBMAXNEIGHBORS];
12242 int downIds[NBMAXNEIGHBORS];
12243 unsigned char downTypes[NBMAXNEIGHBORS];
12244 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12245 for (int n = 0; n < nbNeighbors; n++)
12246 if (setOfInsideVol.count(neighborsVtkIds[n]))
12248 MESSAGE("countInside " << countInside);
12249 if (countInside > 1)
12251 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12252 setOfInsideVol.insert(vtkId);
12253 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12254 addedInside = true;
12257 setOfVolToReCheck.insert(vtkId);
12259 setOfVolToCheck.erase(vtkId);
12263 // --- map of Downward faces at the boundary, inside the global volume
12264 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12265 // fill group of SMDS faces inside the volume (when several volume shapes)
12266 // fill group of SMDS faces on the skin of the global volume (if skin)
12268 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12269 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12270 std::set<int>::iterator it = setOfInsideVol.begin();
12271 for (; it != setOfInsideVol.end(); ++it)
12274 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12275 int neighborsVtkIds[NBMAXNEIGHBORS];
12276 int downIds[NBMAXNEIGHBORS];
12277 unsigned char downTypes[NBMAXNEIGHBORS];
12278 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12279 for (int n = 0; n < nbNeighbors; n++)
12281 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12282 if (neighborDim == 3)
12284 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12286 DownIdType face(downIds[n], downTypes[n]);
12287 boundaryFaces[face] = vtkId;
12289 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12290 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12291 if (vtkFaceId >= 0)
12293 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12294 // find also the smds edges on this face
12295 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12296 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12297 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12298 for (int i = 0; i < nbEdges; i++)
12300 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12301 if (vtkEdgeId >= 0)
12302 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12306 else if (neighborDim == 2) // skin of the volume
12308 DownIdType face(downIds[n], downTypes[n]);
12309 skinFaces[face] = vtkId;
12310 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12311 if (vtkFaceId >= 0)
12312 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12317 // --- identify the edges constituting the wire of each subshape on the skin
12318 // define polylines with the nodes of edges, equivalent to wires
12319 // project polylines on subshapes, and partition, to get geom faces
12321 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12322 std::set<int> emptySet;
12324 std::set<int> shapeIds;
12326 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12327 while (itelem->more())
12329 const SMDS_MeshElement *elem = itelem->next();
12330 int shapeId = elem->getshapeId();
12331 int vtkId = elem->getVtkId();
12332 if (!shapeIdToVtkIdSet.count(shapeId))
12334 shapeIdToVtkIdSet[shapeId] = emptySet;
12335 shapeIds.insert(shapeId);
12337 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12340 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12341 std::set<DownIdType, DownIdCompare> emptyEdges;
12342 emptyEdges.clear();
12344 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12345 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12347 int shapeId = itShape->first;
12348 MESSAGE(" --- Shape ID --- "<< shapeId);
12349 shapeIdToEdges[shapeId] = emptyEdges;
12351 std::vector<int> nodesEdges;
12353 std::set<int>::iterator its = itShape->second.begin();
12354 for (; its != itShape->second.end(); ++its)
12357 MESSAGE(" " << vtkId);
12358 int neighborsVtkIds[NBMAXNEIGHBORS];
12359 int downIds[NBMAXNEIGHBORS];
12360 unsigned char downTypes[NBMAXNEIGHBORS];
12361 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12362 for (int n = 0; n < nbNeighbors; n++)
12364 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12366 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12367 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12368 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12370 DownIdType edge(downIds[n], downTypes[n]);
12371 if (!shapeIdToEdges[shapeId].count(edge))
12373 shapeIdToEdges[shapeId].insert(edge);
12375 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12376 nodesEdges.push_back(vtkNodeId[0]);
12377 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12378 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12384 std::list<int> order;
12386 if (nodesEdges.size() > 0)
12388 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12389 nodesEdges[0] = -1;
12390 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12391 nodesEdges[1] = -1; // do not reuse this edge
12395 int nodeTofind = order.back(); // try first to push back
12397 for (i = 0; i<nodesEdges.size(); i++)
12398 if (nodesEdges[i] == nodeTofind)
12400 if (i == nodesEdges.size())
12401 found = false; // no follower found on back
12404 if (i%2) // odd ==> use the previous one
12405 if (nodesEdges[i-1] < 0)
12409 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12410 nodesEdges[i-1] = -1;
12412 else // even ==> use the next one
12413 if (nodesEdges[i+1] < 0)
12417 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12418 nodesEdges[i+1] = -1;
12423 // try to push front
12425 nodeTofind = order.front(); // try to push front
12426 for (i = 0; i<nodesEdges.size(); i++)
12427 if (nodesEdges[i] == nodeTofind)
12429 if (i == nodesEdges.size())
12431 found = false; // no predecessor found on front
12434 if (i%2) // odd ==> use the previous one
12435 if (nodesEdges[i-1] < 0)
12439 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12440 nodesEdges[i-1] = -1;
12442 else // even ==> use the next one
12443 if (nodesEdges[i+1] < 0)
12447 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12448 nodesEdges[i+1] = -1;
12454 std::vector<int> nodes;
12455 nodes.push_back(shapeId);
12456 std::list<int>::iterator itl = order.begin();
12457 for (; itl != order.end(); itl++)
12459 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12460 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12462 listOfListOfNodes.push_back(nodes);
12465 // partition geom faces with blocFissure
12466 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12467 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12473 //================================================================================
12475 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12476 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12477 * \return TRUE if operation has been completed successfully, FALSE otherwise
12479 //================================================================================
12481 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12483 // iterates on volume elements and detect all free faces on them
12484 SMESHDS_Mesh* aMesh = GetMeshDS();
12488 ElemFeatures faceType( SMDSAbs_Face );
12489 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12490 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12493 const SMDS_MeshVolume* volume = vIt->next();
12494 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12495 vTool.SetExternalNormal();
12496 const int iQuad = volume->IsQuadratic();
12497 faceType.SetQuad( iQuad );
12498 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12500 if (!vTool.IsFreeFace(iface))
12503 vector<const SMDS_MeshNode *> nodes;
12504 int nbFaceNodes = vTool.NbFaceNodes(iface);
12505 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12507 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12508 nodes.push_back(faceNodes[inode]);
12510 if (iQuad) // add medium nodes
12512 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12513 nodes.push_back(faceNodes[inode]);
12514 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12515 nodes.push_back(faceNodes[8]);
12517 // add new face based on volume nodes
12518 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12520 nbExisted++; // face already exsist
12524 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12529 return ( nbFree == ( nbExisted + nbCreated ));
12534 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12536 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12538 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12541 //================================================================================
12543 * \brief Creates missing boundary elements
12544 * \param elements - elements whose boundary is to be checked
12545 * \param dimension - defines type of boundary elements to create
12546 * \param group - a group to store created boundary elements in
12547 * \param targetMesh - a mesh to store created boundary elements in
12548 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12549 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12550 * boundary elements will be copied into the targetMesh
12551 * \param toAddExistingBondary - if true, not only new but also pre-existing
12552 * boundary elements will be added into the new group
12553 * \param aroundElements - if true, elements will be created on boundary of given
12554 * elements else, on boundary of the whole mesh.
12555 * \return nb of added boundary elements
12557 //================================================================================
12559 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12560 Bnd_Dimension dimension,
12561 SMESH_Group* group/*=0*/,
12562 SMESH_Mesh* targetMesh/*=0*/,
12563 bool toCopyElements/*=false*/,
12564 bool toCopyExistingBoundary/*=false*/,
12565 bool toAddExistingBondary/*= false*/,
12566 bool aroundElements/*= false*/)
12568 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12569 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12570 // hope that all elements are of the same type, do not check them all
12571 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12572 throw SALOME_Exception(LOCALIZED("wrong element type"));
12575 toCopyElements = toCopyExistingBoundary = false;
12577 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12578 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12579 int nbAddedBnd = 0;
12581 // editor adding present bnd elements and optionally holding elements to add to the group
12582 SMESH_MeshEditor* presentEditor;
12583 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12584 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12586 SMESH_MesherHelper helper( *myMesh );
12587 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12588 SMDS_VolumeTool vTool;
12589 TIDSortedElemSet avoidSet;
12590 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12593 typedef vector<const SMDS_MeshNode*> TConnectivity;
12594 TConnectivity tgtNodes;
12595 ElemFeatures elemKind( missType ), elemToCopy;
12597 vector<const SMDS_MeshElement*> presentBndElems;
12598 vector<TConnectivity> missingBndElems;
12599 vector<int> freeFacets;
12600 TConnectivity nodes, elemNodes;
12602 SMDS_ElemIteratorPtr eIt;
12603 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12604 else eIt = elemSetIterator( elements );
12606 while (eIt->more())
12608 const SMDS_MeshElement* elem = eIt->next();
12609 const int iQuad = elem->IsQuadratic();
12610 elemKind.SetQuad( iQuad );
12612 // ------------------------------------------------------------------------------------
12613 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12614 // ------------------------------------------------------------------------------------
12615 presentBndElems.clear();
12616 missingBndElems.clear();
12617 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12618 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12620 const SMDS_MeshElement* otherVol = 0;
12621 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12623 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12624 ( !aroundElements || elements.count( otherVol )))
12626 freeFacets.push_back( iface );
12628 if ( missType == SMDSAbs_Face )
12629 vTool.SetExternalNormal();
12630 for ( size_t i = 0; i < freeFacets.size(); ++i )
12632 int iface = freeFacets[i];
12633 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12634 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12635 if ( missType == SMDSAbs_Edge ) // boundary edges
12637 nodes.resize( 2+iQuad );
12638 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12640 for ( int j = 0; j < nodes.size(); ++j )
12641 nodes[j] = nn[ i+j ];
12642 if ( const SMDS_MeshElement* edge =
12643 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12644 presentBndElems.push_back( edge );
12646 missingBndElems.push_back( nodes );
12649 else // boundary face
12652 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12653 nodes.push_back( nn[inode] ); // add corner nodes
12655 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12656 nodes.push_back( nn[inode] ); // add medium nodes
12657 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12659 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12661 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12662 SMDSAbs_Face, /*noMedium=*/false ))
12663 presentBndElems.push_back( f );
12665 missingBndElems.push_back( nodes );
12667 if ( targetMesh != myMesh )
12669 // add 1D elements on face boundary to be added to a new mesh
12670 const SMDS_MeshElement* edge;
12671 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12674 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12676 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12677 if ( edge && avoidSet.insert( edge ).second )
12678 presentBndElems.push_back( edge );
12684 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12686 avoidSet.clear(), avoidSet.insert( elem );
12687 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12688 SMDS_MeshElement::iterator() );
12689 elemNodes.push_back( elemNodes[0] );
12690 nodes.resize( 2 + iQuad );
12691 const int nbLinks = elem->NbCornerNodes();
12692 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12694 nodes[0] = elemNodes[iN];
12695 nodes[1] = elemNodes[iN+1+iQuad];
12696 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12697 continue; // not free link
12699 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12700 if ( const SMDS_MeshElement* edge =
12701 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12702 presentBndElems.push_back( edge );
12704 missingBndElems.push_back( nodes );
12708 // ---------------------------------
12709 // 2. Add missing boundary elements
12710 // ---------------------------------
12711 if ( targetMesh != myMesh )
12712 // instead of making a map of nodes in this mesh and targetMesh,
12713 // we create nodes with same IDs.
12714 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12716 TConnectivity& srcNodes = missingBndElems[i];
12717 tgtNodes.resize( srcNodes.size() );
12718 for ( inode = 0; inode < srcNodes.size(); ++inode )
12719 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12720 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12722 /*noMedium=*/false))
12724 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12728 for ( int i = 0; i < missingBndElems.size(); ++i )
12730 TConnectivity& nodes = missingBndElems[i];
12731 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12733 /*noMedium=*/false))
12735 SMDS_MeshElement* newElem =
12736 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12737 nbAddedBnd += bool( newElem );
12739 // try to set a new element to a shape
12740 if ( myMesh->HasShapeToMesh() )
12743 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12744 const size_t nbN = nodes.size() / (iQuad+1 );
12745 for ( inode = 0; inode < nbN && ok; ++inode )
12747 pair<int, TopAbs_ShapeEnum> i_stype =
12748 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12749 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12750 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12752 if ( ok && mediumShapes.size() > 1 )
12754 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12755 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12756 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12758 if (( ok = ( stype_i->first != stype_i_0.first )))
12759 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12760 aMesh->IndexToShape( stype_i_0.second ));
12763 if ( ok && mediumShapes.begin()->first == missShapeType )
12764 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12768 // ----------------------------------
12769 // 3. Copy present boundary elements
12770 // ----------------------------------
12771 if ( toCopyExistingBoundary )
12772 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12774 const SMDS_MeshElement* e = presentBndElems[i];
12775 tgtNodes.resize( e->NbNodes() );
12776 for ( inode = 0; inode < nodes.size(); ++inode )
12777 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12778 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12780 else // store present elements to add them to a group
12781 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12783 presentEditor->myLastCreatedElems.Append( presentBndElems[i] );
12786 } // loop on given elements
12788 // ---------------------------------------------
12789 // 4. Fill group with boundary elements
12790 // ---------------------------------------------
12793 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12794 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12795 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12797 tgtEditor.myLastCreatedElems.Clear();
12798 tgtEditor2.myLastCreatedElems.Clear();
12800 // -----------------------
12801 // 5. Copy given elements
12802 // -----------------------
12803 if ( toCopyElements && targetMesh != myMesh )
12805 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12806 else eIt = elemSetIterator( elements );
12807 while (eIt->more())
12809 const SMDS_MeshElement* elem = eIt->next();
12810 tgtNodes.resize( elem->NbNodes() );
12811 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12812 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12813 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12815 tgtEditor.myLastCreatedElems.Clear();
12821 //================================================================================
12823 * \brief Copy node position and set \a to node on the same geometry
12825 //================================================================================
12827 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12828 const SMDS_MeshNode* to )
12830 if ( !from || !to ) return;
12832 SMDS_PositionPtr pos = from->GetPosition();
12833 if ( !pos || from->getshapeId() < 1 ) return;
12835 switch ( pos->GetTypeOfPosition() )
12837 case SMDS_TOP_3DSPACE: break;
12839 case SMDS_TOP_FACE:
12841 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12842 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12843 fPos->GetUParameter(), fPos->GetVParameter() );
12846 case SMDS_TOP_EDGE:
12848 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12849 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12850 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12853 case SMDS_TOP_VERTEX:
12855 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12858 case SMDS_TOP_UNSPEC: