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 size_t 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 ( size_t 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 ( size_t 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 helper.SetIsQuadratic ( nodes.size() > 4 );
1605 helper.SetIsBiQuadratic( nodes.size() == 9 );
1606 if ( helper.GetIsQuadratic() )
1607 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1609 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
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 ( size_t 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 ( size_t 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 = ((int) 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 ( size_t 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++ )
2944 const SMDS_MeshElement* elem = *itElem;
2945 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2948 if ( elem->NbNodes() == 4 ) {
2949 // retrieve element nodes
2950 const SMDS_MeshNode* aNodes [4];
2951 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2953 while ( itN->more() )
2954 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2956 int aShapeId = FindShape( elem );
2957 const SMDS_MeshElement* newElem1 = 0;
2958 const SMDS_MeshElement* newElem2 = 0;
2960 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2961 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2964 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2965 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2967 myLastCreatedElems.Append(newElem1);
2968 myLastCreatedElems.Append(newElem2);
2969 // put a new triangle on the same shape and add to the same groups
2972 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2973 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2975 AddToSameGroups( newElem1, elem, aMesh );
2976 AddToSameGroups( newElem2, elem, aMesh );
2977 aMesh->RemoveElement( elem );
2980 // Quadratic quadrangle
2982 else if ( elem->NbNodes() == 8 )
2984 // get surface elem is on
2985 int aShapeId = FindShape( elem );
2986 if ( aShapeId != helper.GetSubShapeID() ) {
2990 shape = aMesh->IndexToShape( aShapeId );
2991 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2992 TopoDS_Face face = TopoDS::Face( shape );
2993 surface = BRep_Tool::Surface( face );
2994 if ( !surface.IsNull() )
2995 helper.SetSubShape( shape );
2999 const SMDS_MeshNode* aNodes [8];
3000 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3001 for ( int i = 0; itN->more(); ++i )
3002 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3004 const SMDS_MeshNode* inFaceNode = 0;
3005 if ( helper.GetNodeUVneedInFaceNode() )
3006 for ( int i = 0; i < 8 && !inFaceNode; ++i )
3007 if ( aNodes[ i ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
3008 inFaceNode = aNodes[ i ];
3010 // find middle point for (0,1,2,3)
3011 // and create a node in this point;
3013 if ( surface.IsNull() ) {
3014 for ( int i = 0; i < 4; i++ ) p += SMESH_TNodeXYZ( aNodes[i] );
3018 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
3020 for ( int i = 0; i < 4; i++ )
3021 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
3023 p = surface->Value( uv.X(), uv.Y() ).XYZ();
3025 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
3026 myLastCreatedNodes.Append(newN);
3028 // create a new element
3029 const SMDS_MeshElement* newElem1 = 0;
3030 const SMDS_MeshElement* newElem2 = 0;
3032 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3033 aNodes[6], aNodes[7], newN );
3034 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3035 newN, aNodes[4], aNodes[5] );
3038 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3039 aNodes[7], aNodes[4], newN );
3040 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3041 newN, aNodes[5], aNodes[6] );
3043 myLastCreatedElems.Append(newElem1);
3044 myLastCreatedElems.Append(newElem2);
3045 // put a new triangle on the same shape and add to the same groups
3048 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3049 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3051 AddToSameGroups( newElem1, elem, aMesh );
3052 AddToSameGroups( newElem2, elem, aMesh );
3053 aMesh->RemoveElement( elem );
3060 //=======================================================================
3061 //function : getAngle
3063 //=======================================================================
3065 double getAngle(const SMDS_MeshElement * tr1,
3066 const SMDS_MeshElement * tr2,
3067 const SMDS_MeshNode * n1,
3068 const SMDS_MeshNode * n2)
3070 double angle = 2. * M_PI; // bad angle
3073 SMESH::Controls::TSequenceOfXYZ P1, P2;
3074 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3075 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3078 if(!tr1->IsQuadratic())
3079 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3081 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3082 if ( N1.SquareMagnitude() <= gp::Resolution() )
3084 if(!tr2->IsQuadratic())
3085 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3087 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3088 if ( N2.SquareMagnitude() <= gp::Resolution() )
3091 // find the first diagonal node n1 in the triangles:
3092 // take in account a diagonal link orientation
3093 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3094 for ( int t = 0; t < 2; t++ ) {
3095 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3096 int i = 0, iDiag = -1;
3097 while ( it->more()) {
3098 const SMDS_MeshElement *n = it->next();
3099 if ( n == n1 || n == n2 ) {
3103 if ( i - iDiag == 1 )
3104 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3113 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3116 angle = N1.Angle( N2 );
3121 // =================================================
3122 // class generating a unique ID for a pair of nodes
3123 // and able to return nodes by that ID
3124 // =================================================
3128 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3129 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3132 long GetLinkID (const SMDS_MeshNode * n1,
3133 const SMDS_MeshNode * n2) const
3135 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3138 bool GetNodes (const long theLinkID,
3139 const SMDS_MeshNode* & theNode1,
3140 const SMDS_MeshNode* & theNode2) const
3142 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3143 if ( !theNode1 ) return false;
3144 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3145 if ( !theNode2 ) return false;
3151 const SMESHDS_Mesh* myMesh;
3156 //=======================================================================
3157 //function : TriToQuad
3158 //purpose : Fuse neighbour triangles into quadrangles.
3159 // theCrit is used to select a neighbour to fuse with.
3160 // theMaxAngle is a max angle between element normals at which
3161 // fusion is still performed.
3162 //=======================================================================
3164 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3165 SMESH::Controls::NumericalFunctorPtr theCrit,
3166 const double theMaxAngle)
3168 myLastCreatedElems.Clear();
3169 myLastCreatedNodes.Clear();
3171 MESSAGE( "::TriToQuad()" );
3173 if ( !theCrit.get() )
3176 SMESHDS_Mesh * aMesh = GetMeshDS();
3178 // Prepare data for algo: build
3179 // 1. map of elements with their linkIDs
3180 // 2. map of linkIDs with their elements
3182 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3183 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3184 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3185 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3187 TIDSortedElemSet::iterator itElem;
3188 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3190 const SMDS_MeshElement* elem = *itElem;
3191 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3192 bool IsTria = ( elem->NbCornerNodes()==3 );
3193 if (!IsTria) continue;
3195 // retrieve element nodes
3196 const SMDS_MeshNode* aNodes [4];
3197 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3200 aNodes[ i++ ] = itN->next();
3201 aNodes[ 3 ] = aNodes[ 0 ];
3204 for ( i = 0; i < 3; i++ ) {
3205 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3206 // check if elements sharing a link can be fused
3207 itLE = mapLi_listEl.find( link );
3208 if ( itLE != mapLi_listEl.end() ) {
3209 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3211 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3212 //if ( FindShape( elem ) != FindShape( elem2 ))
3213 // continue; // do not fuse triangles laying on different shapes
3214 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3215 continue; // avoid making badly shaped quads
3216 (*itLE).second.push_back( elem );
3219 mapLi_listEl[ link ].push_back( elem );
3221 mapEl_setLi [ elem ].insert( link );
3224 // Clean the maps from the links shared by a sole element, ie
3225 // links to which only one element is bound in mapLi_listEl
3227 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3228 int nbElems = (*itLE).second.size();
3229 if ( nbElems < 2 ) {
3230 const SMDS_MeshElement* elem = (*itLE).second.front();
3231 SMESH_TLink link = (*itLE).first;
3232 mapEl_setLi[ elem ].erase( link );
3233 if ( mapEl_setLi[ elem ].empty() )
3234 mapEl_setLi.erase( elem );
3238 // Algo: fuse triangles into quadrangles
3240 while ( ! mapEl_setLi.empty() ) {
3241 // Look for the start element:
3242 // the element having the least nb of shared links
3243 const SMDS_MeshElement* startElem = 0;
3245 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3246 int nbLinks = (*itEL).second.size();
3247 if ( nbLinks < minNbLinks ) {
3248 startElem = (*itEL).first;
3249 minNbLinks = nbLinks;
3250 if ( minNbLinks == 1 )
3255 // search elements to fuse starting from startElem or links of elements
3256 // fused earlyer - startLinks
3257 list< SMESH_TLink > startLinks;
3258 while ( startElem || !startLinks.empty() ) {
3259 while ( !startElem && !startLinks.empty() ) {
3260 // Get an element to start, by a link
3261 SMESH_TLink linkId = startLinks.front();
3262 startLinks.pop_front();
3263 itLE = mapLi_listEl.find( linkId );
3264 if ( itLE != mapLi_listEl.end() ) {
3265 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3266 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3267 for ( ; itE != listElem.end() ; itE++ )
3268 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3270 mapLi_listEl.erase( itLE );
3275 // Get candidates to be fused
3276 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3277 const SMESH_TLink *link12, *link13;
3279 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3280 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3281 ASSERT( !setLi.empty() );
3282 set< SMESH_TLink >::iterator itLi;
3283 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3285 const SMESH_TLink & link = (*itLi);
3286 itLE = mapLi_listEl.find( link );
3287 if ( itLE == mapLi_listEl.end() )
3290 const SMDS_MeshElement* elem = (*itLE).second.front();
3292 elem = (*itLE).second.back();
3293 mapLi_listEl.erase( itLE );
3294 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3305 // add other links of elem to list of links to re-start from
3306 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3307 set< SMESH_TLink >::iterator it;
3308 for ( it = links.begin(); it != links.end(); it++ ) {
3309 const SMESH_TLink& link2 = (*it);
3310 if ( link2 != link )
3311 startLinks.push_back( link2 );
3315 // Get nodes of possible quadrangles
3316 const SMDS_MeshNode *n12 [4], *n13 [4];
3317 bool Ok12 = false, Ok13 = false;
3318 const SMDS_MeshNode *linkNode1, *linkNode2;
3320 linkNode1 = link12->first;
3321 linkNode2 = link12->second;
3322 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3326 linkNode1 = link13->first;
3327 linkNode2 = link13->second;
3328 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3332 // Choose a pair to fuse
3333 if ( Ok12 && Ok13 ) {
3334 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3335 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3336 double aBadRate12 = getBadRate( &quad12, theCrit );
3337 double aBadRate13 = getBadRate( &quad13, theCrit );
3338 if ( aBadRate13 < aBadRate12 )
3345 // and remove fused elems and remove links from the maps
3346 mapEl_setLi.erase( tr1 );
3349 mapEl_setLi.erase( tr2 );
3350 mapLi_listEl.erase( *link12 );
3351 if ( tr1->NbNodes() == 3 )
3353 const SMDS_MeshElement* newElem = 0;
3354 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3355 myLastCreatedElems.Append(newElem);
3356 AddToSameGroups( newElem, tr1, aMesh );
3357 int aShapeId = tr1->getshapeId();
3359 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3360 aMesh->RemoveElement( tr1 );
3361 aMesh->RemoveElement( tr2 );
3364 vector< const SMDS_MeshNode* > N1;
3365 vector< const SMDS_MeshNode* > N2;
3366 getNodesFromTwoTria(tr1,tr2,N1,N2);
3367 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3368 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3369 // i.e. first nodes from both arrays form a new diagonal
3370 const SMDS_MeshNode* aNodes[8];
3379 const SMDS_MeshElement* newElem = 0;
3380 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3381 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3382 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3384 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3385 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3386 myLastCreatedElems.Append(newElem);
3387 AddToSameGroups( newElem, tr1, aMesh );
3388 int aShapeId = tr1->getshapeId();
3390 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3391 aMesh->RemoveElement( tr1 );
3392 aMesh->RemoveElement( tr2 );
3393 // remove middle node (9)
3394 if ( N1[4]->NbInverseElements() == 0 )
3395 aMesh->RemoveNode( N1[4] );
3396 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3397 aMesh->RemoveNode( N1[6] );
3398 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3399 aMesh->RemoveNode( N2[6] );
3404 mapEl_setLi.erase( tr3 );
3405 mapLi_listEl.erase( *link13 );
3406 if ( tr1->NbNodes() == 3 ) {
3407 const SMDS_MeshElement* newElem = 0;
3408 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3409 myLastCreatedElems.Append(newElem);
3410 AddToSameGroups( newElem, tr1, aMesh );
3411 int aShapeId = tr1->getshapeId();
3413 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3414 aMesh->RemoveElement( tr1 );
3415 aMesh->RemoveElement( tr3 );
3418 vector< const SMDS_MeshNode* > N1;
3419 vector< const SMDS_MeshNode* > N2;
3420 getNodesFromTwoTria(tr1,tr3,N1,N2);
3421 // now we receive following N1 and N2 (using numeration as above image)
3422 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3423 // i.e. first nodes from both arrays form a new diagonal
3424 const SMDS_MeshNode* aNodes[8];
3433 const SMDS_MeshElement* newElem = 0;
3434 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3435 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3436 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3438 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3439 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3440 myLastCreatedElems.Append(newElem);
3441 AddToSameGroups( newElem, tr1, aMesh );
3442 int aShapeId = tr1->getshapeId();
3444 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3445 aMesh->RemoveElement( tr1 );
3446 aMesh->RemoveElement( tr3 );
3447 // remove middle node (9)
3448 if ( N1[4]->NbInverseElements() == 0 )
3449 aMesh->RemoveNode( N1[4] );
3450 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3451 aMesh->RemoveNode( N1[6] );
3452 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3453 aMesh->RemoveNode( N2[6] );
3457 // Next element to fuse: the rejected one
3459 startElem = Ok12 ? tr3 : tr2;
3461 } // if ( startElem )
3462 } // while ( startElem || !startLinks.empty() )
3463 } // while ( ! mapEl_setLi.empty() )
3469 /*#define DUMPSO(txt) \
3470 // cout << txt << endl;
3471 //=============================================================================
3475 //=============================================================================
3476 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3480 int tmp = idNodes[ i1 ];
3481 idNodes[ i1 ] = idNodes[ i2 ];
3482 idNodes[ i2 ] = tmp;
3483 gp_Pnt Ptmp = P[ i1 ];
3486 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3489 //=======================================================================
3490 //function : SortQuadNodes
3491 //purpose : Set 4 nodes of a quadrangle face in a good order.
3492 // Swap 1<->2 or 2<->3 nodes and correspondingly return
3494 //=======================================================================
3496 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3501 for ( i = 0; i < 4; i++ ) {
3502 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3504 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3507 gp_Vec V1(P[0], P[1]);
3508 gp_Vec V2(P[0], P[2]);
3509 gp_Vec V3(P[0], P[3]);
3511 gp_Vec Cross1 = V1 ^ V2;
3512 gp_Vec Cross2 = V2 ^ V3;
3515 if (Cross1.Dot(Cross2) < 0)
3520 if (Cross1.Dot(Cross2) < 0)
3524 swap ( i, i + 1, idNodes, P );
3526 // for ( int ii = 0; ii < 4; ii++ ) {
3527 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3528 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3534 //=======================================================================
3535 //function : SortHexaNodes
3536 //purpose : Set 8 nodes of a hexahedron in a good order.
3537 // Return success status
3538 //=======================================================================
3540 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3545 DUMPSO( "INPUT: ========================================");
3546 for ( i = 0; i < 8; i++ ) {
3547 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3548 if ( !n ) return false;
3549 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3550 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3552 DUMPSO( "========================================");
3555 set<int> faceNodes; // ids of bottom face nodes, to be found
3556 set<int> checkedId1; // ids of tried 2-nd nodes
3557 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3558 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
3559 int iMin, iLoop1 = 0;
3561 // Loop to try the 2-nd nodes
3563 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3565 // Find not checked 2-nd node
3566 for ( i = 1; i < 8; i++ )
3567 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3568 int id1 = idNodes[i];
3569 swap ( 1, i, idNodes, P );
3570 checkedId1.insert ( id1 );
3574 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3575 // ie that all but meybe one (id3 which is on the same face) nodes
3576 // lay on the same side from the triangle plane.
3578 bool manyInPlane = false; // more than 4 nodes lay in plane
3580 while ( ++iLoop2 < 6 ) {
3582 // get 1-2-3 plane coeffs
3583 Standard_Real A, B, C, D;
3584 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3585 if ( N.SquareMagnitude() > gp::Resolution() )
3587 gp_Pln pln ( P[0], N );
3588 pln.Coefficients( A, B, C, D );
3590 // find the node (iMin) closest to pln
3591 Standard_Real dist[ 8 ], minDist = DBL_MAX;
3593 for ( i = 3; i < 8; i++ ) {
3594 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3595 if ( fabs( dist[i] ) < minDist ) {
3596 minDist = fabs( dist[i] );
3599 if ( fabs( dist[i] ) <= tol )
3600 idInPln.insert( idNodes[i] );
3603 // there should not be more than 4 nodes in bottom plane
3604 if ( idInPln.size() > 1 )
3606 DUMPSO( "### idInPln.size() = " << idInPln.size());
3607 // idInPlane does not contain the first 3 nodes
3608 if ( manyInPlane || idInPln.size() == 5)
3609 return false; // all nodes in one plane
3612 // set the 1-st node to be not in plane
3613 for ( i = 3; i < 8; i++ ) {
3614 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3615 DUMPSO( "### Reset 0-th node");
3616 swap( 0, i, idNodes, P );
3621 // reset to re-check second nodes
3622 leastDist = DBL_MAX;
3626 break; // from iLoop2;
3629 // check that the other 4 nodes are on the same side
3630 bool sameSide = true;
3631 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3632 for ( i = 3; sameSide && i < 8; i++ ) {
3634 sameSide = ( isNeg == dist[i] <= 0.);
3637 // keep best solution
3638 if ( sameSide && minDist < leastDist ) {
3639 leastDist = minDist;
3641 faceNodes.insert( idNodes[ 1 ] );
3642 faceNodes.insert( idNodes[ 2 ] );
3643 faceNodes.insert( idNodes[ iMin ] );
3644 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3645 << " leastDist = " << leastDist);
3646 if ( leastDist <= DBL_MIN )
3651 // set next 3-d node to check
3652 int iNext = 2 + iLoop2;
3654 DUMPSO( "Try 2-nd");
3655 swap ( 2, iNext, idNodes, P );
3657 } // while ( iLoop2 < 6 )
3660 if ( faceNodes.empty() ) return false;
3662 // Put the faceNodes in proper places
3663 for ( i = 4; i < 8; i++ ) {
3664 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3665 // find a place to put
3667 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3669 DUMPSO( "Set faceNodes");
3670 swap ( iTo, i, idNodes, P );
3675 // Set nodes of the found bottom face in good order
3676 DUMPSO( " Found bottom face: ");
3677 i = SortQuadNodes( theMesh, idNodes );
3679 gp_Pnt Ptmp = P[ i ];
3684 // for ( int ii = 0; ii < 4; ii++ ) {
3685 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3686 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3689 // Gravity center of the top and bottom faces
3690 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3691 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3693 // Get direction from the bottom to the top face
3694 gp_Vec upDir ( aGCb, aGCt );
3695 Standard_Real upDirSize = upDir.Magnitude();
3696 if ( upDirSize <= gp::Resolution() ) return false;
3699 // Assure that the bottom face normal points up
3700 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3701 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3702 if ( Nb.Dot( upDir ) < 0 ) {
3703 DUMPSO( "Reverse bottom face");
3704 swap( 1, 3, idNodes, P );
3707 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3708 Standard_Real minDist = DBL_MAX;
3709 for ( i = 4; i < 8; i++ ) {
3710 // projection of P[i] to the plane defined by P[0] and upDir
3711 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3712 Standard_Real sqDist = P[0].SquareDistance( Pp );
3713 if ( sqDist < minDist ) {
3718 DUMPSO( "Set 4-th");
3719 swap ( 4, iMin, idNodes, P );
3721 // Set nodes of the top face in good order
3722 DUMPSO( "Sort top face");
3723 i = SortQuadNodes( theMesh, &idNodes[4] );
3726 gp_Pnt Ptmp = P[ i ];
3731 // Assure that direction of the top face normal is from the bottom face
3732 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3733 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3734 if ( Nt.Dot( upDir ) < 0 ) {
3735 DUMPSO( "Reverse top face");
3736 swap( 5, 7, idNodes, P );
3739 // DUMPSO( "OUTPUT: ========================================");
3740 // for ( i = 0; i < 8; i++ ) {
3741 // float *p = ugrid->GetPoint(idNodes[i]);
3742 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3748 //================================================================================
3750 * \brief Return nodes linked to the given one
3751 * \param theNode - the node
3752 * \param linkedNodes - the found nodes
3753 * \param type - the type of elements to check
3755 * Medium nodes are ignored
3757 //================================================================================
3759 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3760 TIDSortedElemSet & linkedNodes,
3761 SMDSAbs_ElementType type )
3763 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3764 while ( elemIt->more() )
3766 const SMDS_MeshElement* elem = elemIt->next();
3767 if(elem->GetType() == SMDSAbs_0DElement)
3770 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3771 if ( elem->GetType() == SMDSAbs_Volume )
3773 SMDS_VolumeTool vol( elem );
3774 while ( nodeIt->more() ) {
3775 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3776 if ( theNode != n && vol.IsLinked( theNode, n ))
3777 linkedNodes.insert( n );
3782 for ( int i = 0; nodeIt->more(); ++i ) {
3783 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3784 if ( n == theNode ) {
3785 int iBefore = i - 1;
3787 if ( elem->IsQuadratic() ) {
3788 int nb = elem->NbNodes() / 2;
3789 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3790 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3792 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3793 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3800 //=======================================================================
3801 //function : laplacianSmooth
3802 //purpose : pulls theNode toward the center of surrounding nodes directly
3803 // connected to that node along an element edge
3804 //=======================================================================
3806 void laplacianSmooth(const SMDS_MeshNode* theNode,
3807 const Handle(Geom_Surface)& theSurface,
3808 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3810 // find surrounding nodes
3812 TIDSortedElemSet nodeSet;
3813 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3815 // compute new coodrs
3817 double coord[] = { 0., 0., 0. };
3818 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3819 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3820 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3821 if ( theSurface.IsNull() ) { // smooth in 3D
3822 coord[0] += node->X();
3823 coord[1] += node->Y();
3824 coord[2] += node->Z();
3826 else { // smooth in 2D
3827 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3828 gp_XY* uv = theUVMap[ node ];
3829 coord[0] += uv->X();
3830 coord[1] += uv->Y();
3833 int nbNodes = nodeSet.size();
3836 coord[0] /= nbNodes;
3837 coord[1] /= nbNodes;
3839 if ( !theSurface.IsNull() ) {
3840 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3841 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3842 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3848 coord[2] /= nbNodes;
3852 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3855 //=======================================================================
3856 //function : centroidalSmooth
3857 //purpose : pulls theNode toward the element-area-weighted centroid of the
3858 // surrounding elements
3859 //=======================================================================
3861 void centroidalSmooth(const SMDS_MeshNode* theNode,
3862 const Handle(Geom_Surface)& theSurface,
3863 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3865 gp_XYZ aNewXYZ(0.,0.,0.);
3866 SMESH::Controls::Area anAreaFunc;
3867 double totalArea = 0.;
3872 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3873 while ( elemIt->more() )
3875 const SMDS_MeshElement* elem = elemIt->next();
3878 gp_XYZ elemCenter(0.,0.,0.);
3879 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3880 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3881 int nn = elem->NbNodes();
3882 if(elem->IsQuadratic()) nn = nn/2;
3884 //while ( itN->more() ) {
3886 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3888 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3889 aNodePoints.push_back( aP );
3890 if ( !theSurface.IsNull() ) { // smooth in 2D
3891 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3892 gp_XY* uv = theUVMap[ aNode ];
3893 aP.SetCoord( uv->X(), uv->Y(), 0. );
3897 double elemArea = anAreaFunc.GetValue( aNodePoints );
3898 totalArea += elemArea;
3900 aNewXYZ += elemCenter * elemArea;
3902 aNewXYZ /= totalArea;
3903 if ( !theSurface.IsNull() ) {
3904 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3905 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3910 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3913 //=======================================================================
3914 //function : getClosestUV
3915 //purpose : return UV of closest projection
3916 //=======================================================================
3918 static bool getClosestUV (Extrema_GenExtPS& projector,
3919 const gp_Pnt& point,
3922 projector.Perform( point );
3923 if ( projector.IsDone() ) {
3924 double u, v, minVal = DBL_MAX;
3925 for ( int i = projector.NbExt(); i > 0; i-- )
3926 if ( projector.SquareDistance( i ) < minVal ) {
3927 minVal = projector.SquareDistance( i );
3928 projector.Point( i ).Parameter( u, v );
3930 result.SetCoord( u, v );
3936 //=======================================================================
3938 //purpose : Smooth theElements during theNbIterations or until a worst
3939 // element has aspect ratio <= theTgtAspectRatio.
3940 // Aspect Ratio varies in range [1.0, inf].
3941 // If theElements is empty, the whole mesh is smoothed.
3942 // theFixedNodes contains additionally fixed nodes. Nodes built
3943 // on edges and boundary nodes are always fixed.
3944 //=======================================================================
3946 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3947 set<const SMDS_MeshNode*> & theFixedNodes,
3948 const SmoothMethod theSmoothMethod,
3949 const int theNbIterations,
3950 double theTgtAspectRatio,
3953 myLastCreatedElems.Clear();
3954 myLastCreatedNodes.Clear();
3956 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3958 if ( theTgtAspectRatio < 1.0 )
3959 theTgtAspectRatio = 1.0;
3961 const double disttol = 1.e-16;
3963 SMESH::Controls::AspectRatio aQualityFunc;
3965 SMESHDS_Mesh* aMesh = GetMeshDS();
3967 if ( theElems.empty() ) {
3968 // add all faces to theElems
3969 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3970 while ( fIt->more() ) {
3971 const SMDS_MeshElement* face = fIt->next();
3972 theElems.insert( theElems.end(), face );
3975 // get all face ids theElems are on
3976 set< int > faceIdSet;
3977 TIDSortedElemSet::iterator itElem;
3979 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3980 int fId = FindShape( *itElem );
3981 // check that corresponding submesh exists and a shape is face
3983 faceIdSet.find( fId ) == faceIdSet.end() &&
3984 aMesh->MeshElements( fId )) {
3985 TopoDS_Shape F = aMesh->IndexToShape( fId );
3986 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3987 faceIdSet.insert( fId );
3990 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3992 // ===============================================
3993 // smooth elements on each TopoDS_Face separately
3994 // ===============================================
3996 SMESH_MesherHelper helper( *GetMesh() );
3998 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3999 for ( ; fId != faceIdSet.rend(); ++fId )
4001 // get face surface and submesh
4002 Handle(Geom_Surface) surface;
4003 SMESHDS_SubMesh* faceSubMesh = 0;
4006 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
4007 bool isUPeriodic = false, isVPeriodic = false;
4010 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
4011 surface = BRep_Tool::Surface( face );
4012 faceSubMesh = aMesh->MeshElements( *fId );
4013 fToler2 = BRep_Tool::Tolerance( face );
4014 fToler2 *= fToler2 * 10.;
4015 isUPeriodic = surface->IsUPeriodic();
4018 isVPeriodic = surface->IsVPeriodic();
4021 surface->Bounds( u1, u2, v1, v2 );
4022 helper.SetSubShape( face );
4024 // ---------------------------------------------------------
4025 // for elements on a face, find movable and fixed nodes and
4026 // compute UV for them
4027 // ---------------------------------------------------------
4028 bool checkBoundaryNodes = false;
4029 bool isQuadratic = false;
4030 set<const SMDS_MeshNode*> setMovableNodes;
4031 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4032 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4033 list< const SMDS_MeshElement* > elemsOnFace;
4035 Extrema_GenExtPS projector;
4036 GeomAdaptor_Surface surfAdaptor;
4037 if ( !surface.IsNull() ) {
4038 surfAdaptor.Load( surface );
4039 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4041 int nbElemOnFace = 0;
4042 itElem = theElems.begin();
4043 // loop on not yet smoothed elements: look for elems on a face
4044 while ( itElem != theElems.end() )
4046 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4047 break; // all elements found
4049 const SMDS_MeshElement* elem = *itElem;
4050 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4051 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4055 elemsOnFace.push_back( elem );
4056 theElems.erase( itElem++ );
4060 isQuadratic = elem->IsQuadratic();
4062 // get movable nodes of elem
4063 const SMDS_MeshNode* node;
4064 SMDS_TypeOfPosition posType;
4065 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4066 int nn = 0, nbn = elem->NbNodes();
4067 if(elem->IsQuadratic())
4069 while ( nn++ < nbn ) {
4070 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4071 const SMDS_PositionPtr& pos = node->GetPosition();
4072 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4073 if (posType != SMDS_TOP_EDGE &&
4074 posType != SMDS_TOP_VERTEX &&
4075 theFixedNodes.find( node ) == theFixedNodes.end())
4077 // check if all faces around the node are on faceSubMesh
4078 // because a node on edge may be bound to face
4079 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4081 if ( faceSubMesh ) {
4082 while ( eIt->more() && all ) {
4083 const SMDS_MeshElement* e = eIt->next();
4084 all = faceSubMesh->Contains( e );
4088 setMovableNodes.insert( node );
4090 checkBoundaryNodes = true;
4092 if ( posType == SMDS_TOP_3DSPACE )
4093 checkBoundaryNodes = true;
4096 if ( surface.IsNull() )
4099 // get nodes to check UV
4100 list< const SMDS_MeshNode* > uvCheckNodes;
4101 const SMDS_MeshNode* nodeInFace = 0;
4102 itN = elem->nodesIterator();
4103 nn = 0; nbn = elem->NbNodes();
4104 if(elem->IsQuadratic())
4106 while ( nn++ < nbn ) {
4107 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4108 if ( node->GetPosition()->GetDim() == 2 )
4110 if ( uvMap.find( node ) == uvMap.end() )
4111 uvCheckNodes.push_back( node );
4112 // add nodes of elems sharing node
4113 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4114 // while ( eIt->more() ) {
4115 // const SMDS_MeshElement* e = eIt->next();
4116 // if ( e != elem ) {
4117 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4118 // while ( nIt->more() ) {
4119 // const SMDS_MeshNode* n =
4120 // static_cast<const SMDS_MeshNode*>( nIt->next() );
4121 // if ( uvMap.find( n ) == uvMap.end() )
4122 // uvCheckNodes.push_back( n );
4128 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4129 for ( ; n != uvCheckNodes.end(); ++n ) {
4132 const SMDS_PositionPtr& pos = node->GetPosition();
4133 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4137 bool toCheck = true;
4138 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4140 // compute not existing UV
4141 bool project = ( posType == SMDS_TOP_3DSPACE );
4142 // double dist1 = DBL_MAX, dist2 = 0;
4143 // if ( posType != SMDS_TOP_3DSPACE ) {
4144 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4145 // project = dist1 > fToler2;
4147 if ( project ) { // compute new UV
4149 gp_Pnt pNode = SMESH_TNodeXYZ( node );
4150 if ( !getClosestUV( projector, pNode, newUV )) {
4151 MESSAGE("Node Projection Failed " << node);
4155 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4157 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4159 // if ( posType != SMDS_TOP_3DSPACE )
4160 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4161 // if ( dist2 < dist1 )
4165 // store UV in the map
4166 listUV.push_back( uv );
4167 uvMap.insert( make_pair( node, &listUV.back() ));
4169 } // loop on not yet smoothed elements
4171 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4172 checkBoundaryNodes = true;
4174 // fix nodes on mesh boundary
4176 if ( checkBoundaryNodes ) {
4177 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4178 map< SMESH_TLink, int >::iterator link_nb;
4179 // put all elements links to linkNbMap
4180 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4181 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4182 const SMDS_MeshElement* elem = (*elemIt);
4183 int nbn = elem->NbCornerNodes();
4184 // loop on elem links: insert them in linkNbMap
4185 for ( int iN = 0; iN < nbn; ++iN ) {
4186 const SMDS_MeshNode* n1 = elem->GetNode( iN );
4187 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4188 SMESH_TLink link( n1, n2 );
4189 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4193 // remove nodes that are in links encountered only once from setMovableNodes
4194 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4195 if ( link_nb->second == 1 ) {
4196 setMovableNodes.erase( link_nb->first.node1() );
4197 setMovableNodes.erase( link_nb->first.node2() );
4202 // -----------------------------------------------------
4203 // for nodes on seam edge, compute one more UV ( uvMap2 );
4204 // find movable nodes linked to nodes on seam and which
4205 // are to be smoothed using the second UV ( uvMap2 )
4206 // -----------------------------------------------------
4208 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4209 if ( !surface.IsNull() ) {
4210 TopExp_Explorer eExp( face, TopAbs_EDGE );
4211 for ( ; eExp.More(); eExp.Next() ) {
4212 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4213 if ( !BRep_Tool::IsClosed( edge, face ))
4215 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4216 if ( !sm ) continue;
4217 // find out which parameter varies for a node on seam
4220 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4221 if ( pcurve.IsNull() ) continue;
4222 uv1 = pcurve->Value( f );
4224 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4225 if ( pcurve.IsNull() ) continue;
4226 uv2 = pcurve->Value( f );
4227 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4229 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4230 std::swap( uv1, uv2 );
4231 // get nodes on seam and its vertices
4232 list< const SMDS_MeshNode* > seamNodes;
4233 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4234 while ( nSeamIt->more() ) {
4235 const SMDS_MeshNode* node = nSeamIt->next();
4236 if ( !isQuadratic || !IsMedium( node ))
4237 seamNodes.push_back( node );
4239 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4240 for ( ; vExp.More(); vExp.Next() ) {
4241 sm = aMesh->MeshElements( vExp.Current() );
4243 nSeamIt = sm->GetNodes();
4244 while ( nSeamIt->more() )
4245 seamNodes.push_back( nSeamIt->next() );
4248 // loop on nodes on seam
4249 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4250 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4251 const SMDS_MeshNode* nSeam = *noSeIt;
4252 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4253 if ( n_uv == uvMap.end() )
4256 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4257 // set the second UV
4258 listUV.push_back( *n_uv->second );
4259 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4260 if ( uvMap2.empty() )
4261 uvMap2 = uvMap; // copy the uvMap contents
4262 uvMap2[ nSeam ] = &listUV.back();
4264 // collect movable nodes linked to ones on seam in nodesNearSeam
4265 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4266 while ( eIt->more() ) {
4267 const SMDS_MeshElement* e = eIt->next();
4268 int nbUseMap1 = 0, nbUseMap2 = 0;
4269 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4270 int nn = 0, nbn = e->NbNodes();
4271 if(e->IsQuadratic()) nbn = nbn/2;
4272 while ( nn++ < nbn )
4274 const SMDS_MeshNode* n =
4275 static_cast<const SMDS_MeshNode*>( nIt->next() );
4277 setMovableNodes.find( n ) == setMovableNodes.end() )
4279 // add only nodes being closer to uv2 than to uv1
4280 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4281 // 0.5 * ( n->Y() + nSeam->Y() ),
4282 // 0.5 * ( n->Z() + nSeam->Z() ));
4284 // getClosestUV( projector, pMid, uv );
4285 double x = uvMap[ n ]->Coord( iPar );
4286 if ( Abs( uv1.Coord( iPar ) - x ) >
4287 Abs( uv2.Coord( iPar ) - x )) {
4288 nodesNearSeam.insert( n );
4294 // for centroidalSmooth all element nodes must
4295 // be on one side of a seam
4296 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4297 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4299 while ( nn++ < nbn ) {
4300 const SMDS_MeshNode* n =
4301 static_cast<const SMDS_MeshNode*>( nIt->next() );
4302 setMovableNodes.erase( n );
4306 } // loop on nodes on seam
4307 } // loop on edge of a face
4308 } // if ( !face.IsNull() )
4310 if ( setMovableNodes.empty() ) {
4311 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4312 continue; // goto next face
4320 double maxRatio = -1., maxDisplacement = -1.;
4321 set<const SMDS_MeshNode*>::iterator nodeToMove;
4322 for ( it = 0; it < theNbIterations; it++ ) {
4323 maxDisplacement = 0.;
4324 nodeToMove = setMovableNodes.begin();
4325 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4326 const SMDS_MeshNode* node = (*nodeToMove);
4327 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4330 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4331 if ( theSmoothMethod == LAPLACIAN )
4332 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4334 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4336 // node displacement
4337 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4338 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4339 if ( aDispl > maxDisplacement )
4340 maxDisplacement = aDispl;
4342 // no node movement => exit
4343 //if ( maxDisplacement < 1.e-16 ) {
4344 if ( maxDisplacement < disttol ) {
4345 MESSAGE("-- no node movement --");
4349 // check elements quality
4351 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4352 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4353 const SMDS_MeshElement* elem = (*elemIt);
4354 if ( !elem || elem->GetType() != SMDSAbs_Face )
4356 SMESH::Controls::TSequenceOfXYZ aPoints;
4357 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4358 double aValue = aQualityFunc.GetValue( aPoints );
4359 if ( aValue > maxRatio )
4363 if ( maxRatio <= theTgtAspectRatio ) {
4364 MESSAGE("-- quality achived --");
4367 if (it+1 == theNbIterations) {
4368 MESSAGE("-- Iteration limit exceeded --");
4370 } // smoothing iterations
4372 MESSAGE(" Face id: " << *fId <<
4373 " Nb iterstions: " << it <<
4374 " Displacement: " << maxDisplacement <<
4375 " Aspect Ratio " << maxRatio);
4377 // ---------------------------------------
4378 // new nodes positions are computed,
4379 // record movement in DS and set new UV
4380 // ---------------------------------------
4381 nodeToMove = setMovableNodes.begin();
4382 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4383 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4384 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4385 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4386 if ( node_uv != uvMap.end() ) {
4387 gp_XY* uv = node_uv->second;
4389 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4393 // move medium nodes of quadratic elements
4396 vector<const SMDS_MeshNode*> nodes;
4398 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4399 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4401 const SMDS_MeshElement* QF = *elemIt;
4402 if ( QF->IsQuadratic() )
4404 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4405 SMDS_MeshElement::iterator() );
4406 nodes.push_back( nodes[0] );
4408 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4410 if ( !surface.IsNull() )
4412 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4413 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4414 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4415 xyz = surface->Value( uv.X(), uv.Y() );
4418 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4420 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4421 // we have to move a medium node
4422 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4428 } // loop on face ids
4434 //=======================================================================
4435 //function : isReverse
4436 //purpose : Return true if normal of prevNodes is not co-directied with
4437 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4438 // iNotSame is where prevNodes and nextNodes are different.
4439 // If result is true then future volume orientation is OK
4440 //=======================================================================
4442 bool isReverse(const SMDS_MeshElement* face,
4443 const vector<const SMDS_MeshNode*>& prevNodes,
4444 const vector<const SMDS_MeshNode*>& nextNodes,
4448 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4449 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4450 gp_XYZ extrDir( pN - pP ), faceNorm;
4451 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4453 return faceNorm * extrDir < 0.0;
4456 //================================================================================
4458 * \brief Assure that theElemSets[0] holds elements, not nodes
4460 //================================================================================
4462 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4464 if ( !theElemSets[0].empty() &&
4465 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4467 std::swap( theElemSets[0], theElemSets[1] );
4469 else if ( !theElemSets[1].empty() &&
4470 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4472 std::swap( theElemSets[0], theElemSets[1] );
4477 //=======================================================================
4479 * \brief Create elements by sweeping an element
4480 * \param elem - element to sweep
4481 * \param newNodesItVec - nodes generated from each node of the element
4482 * \param newElems - generated elements
4483 * \param nbSteps - number of sweeping steps
4484 * \param srcElements - to append elem for each generated element
4486 //=======================================================================
4488 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4489 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4490 list<const SMDS_MeshElement*>& newElems,
4491 const size_t nbSteps,
4492 SMESH_SequenceOfElemPtr& srcElements)
4494 //MESSAGE("sweepElement " << nbSteps);
4495 SMESHDS_Mesh* aMesh = GetMeshDS();
4497 const int nbNodes = elem->NbNodes();
4498 const int nbCorners = elem->NbCornerNodes();
4499 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4500 polyhedron creation !!! */
4501 // Loop on elem nodes:
4502 // find new nodes and detect same nodes indices
4503 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4504 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4505 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4506 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4508 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4509 vector<int> sames(nbNodes);
4510 vector<bool> isSingleNode(nbNodes);
4512 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4513 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4514 const SMDS_MeshNode* node = nnIt->first;
4515 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4516 if ( listNewNodes.empty() )
4519 itNN [ iNode ] = listNewNodes.begin();
4520 prevNod[ iNode ] = node;
4521 nextNod[ iNode ] = listNewNodes.front();
4523 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4524 corner node of linear */
4525 if ( prevNod[ iNode ] != nextNod [ iNode ])
4526 nbDouble += !isSingleNode[iNode];
4528 if( iNode < nbCorners ) { // check corners only
4529 if ( prevNod[ iNode ] == nextNod [ iNode ])
4530 sames[nbSame++] = iNode;
4532 iNotSameNode = iNode;
4536 if ( nbSame == nbNodes || nbSame > 2) {
4537 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4541 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4543 // fix nodes order to have bottom normal external
4544 if ( baseType == SMDSEntity_Polygon )
4546 std::reverse( itNN.begin(), itNN.end() );
4547 std::reverse( prevNod.begin(), prevNod.end() );
4548 std::reverse( midlNod.begin(), midlNod.end() );
4549 std::reverse( nextNod.begin(), nextNod.end() );
4550 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4554 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4555 SMDS_MeshCell::applyInterlace( ind, itNN );
4556 SMDS_MeshCell::applyInterlace( ind, prevNod );
4557 SMDS_MeshCell::applyInterlace( ind, nextNod );
4558 SMDS_MeshCell::applyInterlace( ind, midlNod );
4559 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4562 sames[nbSame] = iNotSameNode;
4563 for ( int j = 0; j <= nbSame; ++j )
4564 for ( size_t i = 0; i < ind.size(); ++i )
4565 if ( ind[i] == sames[j] )
4570 iNotSameNode = sames[nbSame];
4574 else if ( elem->GetType() == SMDSAbs_Edge )
4576 // orient a new face same as adjacent one
4578 const SMDS_MeshElement* e;
4579 TIDSortedElemSet dummy;
4580 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4581 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4582 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4584 // there is an adjacent face, check order of nodes in it
4585 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4588 std::swap( itNN[0], itNN[1] );
4589 std::swap( prevNod[0], prevNod[1] );
4590 std::swap( nextNod[0], nextNod[1] );
4591 isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4593 sames[0] = 1 - sames[0];
4594 iNotSameNode = 1 - iNotSameNode;
4599 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4601 iSameNode = sames[ nbSame-1 ];
4602 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4603 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4604 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4607 if ( baseType == SMDSEntity_Polygon )
4609 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4610 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4612 else if ( baseType == SMDSEntity_Quad_Polygon )
4614 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4615 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4618 // make new elements
4619 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4622 for ( iNode = 0; iNode < nbNodes; iNode++ )
4624 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4625 nextNod[ iNode ] = *itNN[ iNode ]++;
4628 SMDS_MeshElement* aNewElem = 0;
4629 /*if(!elem->IsPoly())*/ {
4630 switch ( baseType ) {
4632 case SMDSEntity_Node: { // sweep NODE
4633 if ( nbSame == 0 ) {
4634 if ( isSingleNode[0] )
4635 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4637 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4643 case SMDSEntity_Edge: { // sweep EDGE
4644 if ( nbDouble == 0 )
4646 if ( nbSame == 0 ) // ---> quadrangle
4647 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4648 nextNod[ 1 ], nextNod[ 0 ] );
4649 else // ---> triangle
4650 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4651 nextNod[ iNotSameNode ] );
4653 else // ---> polygon
4655 vector<const SMDS_MeshNode*> poly_nodes;
4656 poly_nodes.push_back( prevNod[0] );
4657 poly_nodes.push_back( prevNod[1] );
4658 if ( prevNod[1] != nextNod[1] )
4660 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4661 poly_nodes.push_back( nextNod[1] );
4663 if ( prevNod[0] != nextNod[0] )
4665 poly_nodes.push_back( nextNod[0] );
4666 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4668 switch ( poly_nodes.size() ) {
4670 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4673 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4674 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4677 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4682 case SMDSEntity_Triangle: // TRIANGLE --->
4684 if ( nbDouble > 0 ) break;
4685 if ( nbSame == 0 ) // ---> pentahedron
4686 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4687 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4689 else if ( nbSame == 1 ) // ---> pyramid
4690 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4691 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4692 nextNod[ iSameNode ]);
4694 else // 2 same nodes: ---> tetrahedron
4695 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4696 nextNod[ iNotSameNode ]);
4699 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4703 if ( nbDouble+nbSame == 2 )
4705 if(nbSame==0) { // ---> quadratic quadrangle
4706 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4707 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4709 else { //(nbSame==1) // ---> quadratic triangle
4711 return; // medium node on axis
4713 else if(sames[0]==0)
4714 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4715 prevNod[2], midlNod[1], nextNod[2] );
4717 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4718 prevNod[2], nextNod[2], midlNod[0]);
4721 else if ( nbDouble == 3 )
4723 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4724 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4725 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4732 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4733 if ( nbDouble > 0 ) break;
4735 if ( nbSame == 0 ) // ---> hexahedron
4736 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4737 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4739 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4740 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4741 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4742 nextNod[ iSameNode ]);
4743 newElems.push_back( aNewElem );
4744 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4745 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4746 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4748 else if ( nbSame == 2 ) { // ---> pentahedron
4749 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4750 // iBeforeSame is same too
4751 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4752 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4753 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4755 // iAfterSame is same too
4756 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4757 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4758 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4762 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4763 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4764 if ( nbDouble+nbSame != 3 ) break;
4766 // ---> pentahedron with 15 nodes
4767 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4768 nextNod[0], nextNod[1], nextNod[2],
4769 prevNod[3], prevNod[4], prevNod[5],
4770 nextNod[3], nextNod[4], nextNod[5],
4771 midlNod[0], midlNod[1], midlNod[2]);
4773 else if(nbSame==1) {
4774 // ---> 2d order pyramid of 13 nodes
4775 int apex = iSameNode;
4776 int i0 = ( apex + 1 ) % nbCorners;
4777 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4781 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4782 nextNod[i0], nextNod[i1], prevNod[apex],
4783 prevNod[i01], midlNod[i0],
4784 nextNod[i01], midlNod[i1],
4785 prevNod[i1a], prevNod[i0a],
4786 nextNod[i0a], nextNod[i1a]);
4788 else if(nbSame==2) {
4789 // ---> 2d order tetrahedron of 10 nodes
4790 int n1 = iNotSameNode;
4791 int n2 = ( n1 + 1 ) % nbCorners;
4792 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4796 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4797 prevNod[n12], prevNod[n23], prevNod[n31],
4798 midlNod[n1], nextNod[n12], nextNod[n31]);
4802 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4804 if ( nbDouble != 4 ) break;
4805 // ---> hexahedron with 20 nodes
4806 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4807 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4808 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4809 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4810 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4812 else if(nbSame==1) {
4813 // ---> pyramid + pentahedron - can not be created since it is needed
4814 // additional middle node at the center of face
4815 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4818 else if( nbSame == 2 ) {
4819 if ( nbDouble != 2 ) break;
4820 // ---> 2d order Pentahedron with 15 nodes
4822 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4823 // iBeforeSame is same too
4830 // iAfterSame is same too
4840 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4841 prevNod[n4], prevNod[n5], nextNod[n5],
4842 prevNod[n12], midlNod[n2], nextNod[n12],
4843 prevNod[n45], midlNod[n5], nextNod[n45],
4844 prevNod[n14], prevNod[n25], nextNod[n25]);
4848 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4850 if( nbSame == 0 && nbDouble == 9 ) {
4851 // ---> tri-quadratic hexahedron with 27 nodes
4852 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4853 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4854 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4855 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4856 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4857 prevNod[8], // bottom center
4858 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4859 nextNod[8], // top center
4860 midlNod[8]);// elem center
4868 case SMDSEntity_Polygon: { // sweep POLYGON
4870 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4871 // ---> hexagonal prism
4872 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4873 prevNod[3], prevNod[4], prevNod[5],
4874 nextNod[0], nextNod[1], nextNod[2],
4875 nextNod[3], nextNod[4], nextNod[5]);
4879 case SMDSEntity_Ball:
4884 } // switch ( baseType )
4887 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4889 if ( baseType != SMDSEntity_Polygon )
4891 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4892 SMDS_MeshCell::applyInterlace( ind, prevNod );
4893 SMDS_MeshCell::applyInterlace( ind, nextNod );
4894 SMDS_MeshCell::applyInterlace( ind, midlNod );
4895 SMDS_MeshCell::applyInterlace( ind, itNN );
4896 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4897 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4899 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4900 vector<int> quantities (nbNodes + 2);
4901 polyedre_nodes.clear();
4905 for (int inode = 0; inode < nbNodes; inode++)
4906 polyedre_nodes.push_back( prevNod[inode] );
4907 quantities.push_back( nbNodes );
4910 polyedre_nodes.push_back( nextNod[0] );
4911 for (int inode = nbNodes; inode-1; --inode )
4912 polyedre_nodes.push_back( nextNod[inode-1] );
4913 quantities.push_back( nbNodes );
4921 const int iQuad = elem->IsQuadratic();
4922 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4924 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4925 int inextface = (iface+1+iQuad) % nbNodes;
4926 int imid = (iface+1) % nbNodes;
4927 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4928 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4929 polyedre_nodes.push_back( prevNod[iface] ); // 1
4930 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4932 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4933 polyedre_nodes.push_back( nextNod[iface] ); // 2
4935 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4936 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4938 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4939 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4941 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4942 if ( nbFaceNodes > 2 )
4943 quantities.push_back( nbFaceNodes );
4944 else // degenerated face
4945 polyedre_nodes.resize( prevNbNodes );
4947 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4949 } // try to create a polyherdal prism
4952 newElems.push_back( aNewElem );
4953 myLastCreatedElems.Append(aNewElem);
4954 srcElements.Append( elem );
4957 // set new prev nodes
4958 for ( iNode = 0; iNode < nbNodes; iNode++ )
4959 prevNod[ iNode ] = nextNod[ iNode ];
4964 //=======================================================================
4966 * \brief Create 1D and 2D elements around swept elements
4967 * \param mapNewNodes - source nodes and ones generated from them
4968 * \param newElemsMap - source elements and ones generated from them
4969 * \param elemNewNodesMap - nodes generated from each node of each element
4970 * \param elemSet - all swept elements
4971 * \param nbSteps - number of sweeping steps
4972 * \param srcElements - to append elem for each generated element
4974 //=======================================================================
4976 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4977 TTElemOfElemListMap & newElemsMap,
4978 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4979 TIDSortedElemSet& elemSet,
4981 SMESH_SequenceOfElemPtr& srcElements)
4983 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4984 SMESHDS_Mesh* aMesh = GetMeshDS();
4986 // Find nodes belonging to only one initial element - sweep them into edges.
4988 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4989 for ( ; nList != mapNewNodes.end(); nList++ )
4991 const SMDS_MeshNode* node =
4992 static_cast<const SMDS_MeshNode*>( nList->first );
4993 if ( newElemsMap.count( node ))
4994 continue; // node was extruded into edge
4995 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4996 int nbInitElems = 0;
4997 const SMDS_MeshElement* el = 0;
4998 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4999 while ( eIt->more() && nbInitElems < 2 ) {
5000 const SMDS_MeshElement* e = eIt->next();
5001 SMDSAbs_ElementType type = e->GetType();
5002 if ( type == SMDSAbs_Volume ||
5006 if ( type > highType ) {
5013 if ( nbInitElems == 1 ) {
5014 bool NotCreateEdge = el && el->IsMediumNode(node);
5015 if(!NotCreateEdge) {
5016 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5017 list<const SMDS_MeshElement*> newEdges;
5018 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5023 // Make a ceiling for each element ie an equal element of last new nodes.
5024 // Find free links of faces - make edges and sweep them into faces.
5026 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5028 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
5029 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5030 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5032 const SMDS_MeshElement* elem = itElem->first;
5033 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5035 if(itElem->second.size()==0) continue;
5037 const bool isQuadratic = elem->IsQuadratic();
5039 if ( elem->GetType() == SMDSAbs_Edge ) {
5040 // create a ceiling edge
5041 if ( !isQuadratic ) {
5042 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5043 vecNewNodes[ 1 ]->second.back())) {
5044 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5045 vecNewNodes[ 1 ]->second.back()));
5046 srcElements.Append( elem );
5050 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5051 vecNewNodes[ 1 ]->second.back(),
5052 vecNewNodes[ 2 ]->second.back())) {
5053 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5054 vecNewNodes[ 1 ]->second.back(),
5055 vecNewNodes[ 2 ]->second.back()));
5056 srcElements.Append( elem );
5060 if ( elem->GetType() != SMDSAbs_Face )
5063 bool hasFreeLinks = false;
5065 TIDSortedElemSet avoidSet;
5066 avoidSet.insert( elem );
5068 set<const SMDS_MeshNode*> aFaceLastNodes;
5069 int iNode, nbNodes = vecNewNodes.size();
5070 if ( !isQuadratic ) {
5071 // loop on the face nodes
5072 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5073 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5074 // look for free links of the face
5075 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5076 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5077 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5078 // check if a link n1-n2 is free
5079 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5080 hasFreeLinks = true;
5081 // make a new edge and a ceiling for a new edge
5082 const SMDS_MeshElement* edge;
5083 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5084 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5085 srcElements.Append( myLastCreatedElems.Last() );
5087 n1 = vecNewNodes[ iNode ]->second.back();
5088 n2 = vecNewNodes[ iNext ]->second.back();
5089 if ( !aMesh->FindEdge( n1, n2 )) {
5090 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5091 srcElements.Append( edge );
5096 else { // elem is quadratic face
5097 int nbn = nbNodes/2;
5098 for ( iNode = 0; iNode < nbn; iNode++ ) {
5099 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5100 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5101 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5102 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5103 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5104 // check if a link is free
5105 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5106 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5107 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5108 hasFreeLinks = true;
5109 // make an edge and a ceiling for a new edge
5111 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5112 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5113 srcElements.Append( elem );
5115 n1 = vecNewNodes[ iNode ]->second.back();
5116 n2 = vecNewNodes[ iNext ]->second.back();
5117 n3 = vecNewNodes[ iNode+nbn ]->second.back();
5118 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5119 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5120 srcElements.Append( elem );
5124 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5125 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5129 // sweep free links into faces
5131 if ( hasFreeLinks ) {
5132 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5133 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5135 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5136 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5137 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5138 initNodeSet.insert( vecNewNodes[ iNode ]->first );
5139 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5141 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
5142 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5143 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5145 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5146 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5147 std::advance( v, volNb );
5148 // find indices of free faces of a volume and their source edges
5149 list< int > freeInd;
5150 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5151 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5152 int iF, nbF = vTool.NbFaces();
5153 for ( iF = 0; iF < nbF; iF ++ ) {
5154 if (vTool.IsFreeFace( iF ) &&
5155 vTool.GetFaceNodes( iF, faceNodeSet ) &&
5156 initNodeSet != faceNodeSet) // except an initial face
5158 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5160 if ( faceNodeSet == initNodeSetNoCenter )
5162 freeInd.push_back( iF );
5163 // find source edge of a free face iF
5164 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5165 vector<const SMDS_MeshNode*>::iterator lastCommom;
5166 commonNodes.resize( nbNodes, 0 );
5167 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5168 initNodeSet.begin(), initNodeSet.end(),
5169 commonNodes.begin());
5170 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5171 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5173 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5175 if ( !srcEdges.back() )
5177 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5178 << iF << " of volume #" << vTool.ID() << endl;
5183 if ( freeInd.empty() )
5186 // create wall faces for all steps;
5187 // if such a face has been already created by sweep of edge,
5188 // assure that its orientation is OK
5189 for ( int iStep = 0; iStep < nbSteps; iStep++ )
5191 vTool.Set( *v, /*ignoreCentralNodes=*/false );
5192 vTool.SetExternalNormal();
5193 const int nextShift = vTool.IsForward() ? +1 : -1;
5194 list< int >::iterator ind = freeInd.begin();
5195 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5196 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5198 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5199 int nbn = vTool.NbFaceNodes( *ind );
5200 const SMDS_MeshElement * f = 0;
5201 if ( nbn == 3 ) ///// triangle
5203 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5205 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5207 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5209 nodes[ 1 + nextShift ] };
5211 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5213 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5217 else if ( nbn == 4 ) ///// quadrangle
5219 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5221 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5223 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5224 nodes[ 2 ], nodes[ 2+nextShift ] };
5226 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5228 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5229 newOrder[ 2 ], newOrder[ 3 ]));
5232 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5234 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5236 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5238 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5240 nodes[2 + 2*nextShift],
5241 nodes[3 - 2*nextShift],
5243 nodes[3 + 2*nextShift]};
5245 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5247 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5255 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5257 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5258 nodes[1], nodes[3], nodes[5], nodes[7] );
5260 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5262 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5263 nodes[4 - 2*nextShift],
5265 nodes[4 + 2*nextShift],
5267 nodes[5 - 2*nextShift],
5269 nodes[5 + 2*nextShift] };
5271 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5273 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5274 newOrder[ 2 ], newOrder[ 3 ],
5275 newOrder[ 4 ], newOrder[ 5 ],
5276 newOrder[ 6 ], newOrder[ 7 ]));
5279 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5281 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5282 SMDSAbs_Face, /*noMedium=*/false);
5284 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5286 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5287 nodes[4 - 2*nextShift],
5289 nodes[4 + 2*nextShift],
5291 nodes[5 - 2*nextShift],
5293 nodes[5 + 2*nextShift],
5296 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5298 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5299 newOrder[ 2 ], newOrder[ 3 ],
5300 newOrder[ 4 ], newOrder[ 5 ],
5301 newOrder[ 6 ], newOrder[ 7 ],
5305 else //////// polygon
5307 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5308 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5310 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5312 if ( !vTool.IsForward() )
5313 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5315 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5317 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5321 while ( srcElements.Length() < myLastCreatedElems.Length() )
5322 srcElements.Append( *srcEdge );
5324 } // loop on free faces
5326 // go to the next volume
5328 while ( iVol++ < nbVolumesByStep ) v++;
5331 } // loop on volumes of one step
5332 } // sweep free links into faces
5334 // Make a ceiling face with a normal external to a volume
5336 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5337 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5338 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5340 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5341 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5342 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5346 lastVol.SetExternalNormal();
5347 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5348 const int nbn = lastVol.NbFaceNodes( iF );
5349 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5350 if ( !hasFreeLinks ||
5351 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5353 const vector<int>& interlace =
5354 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5355 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5357 AddElement( nodeVec, anyFace.Init( elem ));
5359 while ( srcElements.Length() < myLastCreatedElems.Length() )
5360 srcElements.Append( elem );
5363 } // loop on swept elements
5366 //=======================================================================
5367 //function : RotationSweep
5369 //=======================================================================
5371 SMESH_MeshEditor::PGroupIDs
5372 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5373 const gp_Ax1& theAxis,
5374 const double theAngle,
5375 const int theNbSteps,
5376 const double theTol,
5377 const bool theMakeGroups,
5378 const bool theMakeWalls)
5380 myLastCreatedElems.Clear();
5381 myLastCreatedNodes.Clear();
5383 // source elements for each generated one
5384 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5386 MESSAGE( "RotationSweep()");
5388 aTrsf.SetRotation( theAxis, theAngle );
5390 aTrsf2.SetRotation( theAxis, theAngle/2. );
5392 gp_Lin aLine( theAxis );
5393 double aSqTol = theTol * theTol;
5395 SMESHDS_Mesh* aMesh = GetMeshDS();
5397 TNodeOfNodeListMap mapNewNodes;
5398 TElemOfVecOfNnlmiMap mapElemNewNodes;
5399 TTElemOfElemListMap newElemsMap;
5401 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5402 myMesh->NbFaces(ORDER_QUADRATIC) +
5403 myMesh->NbVolumes(ORDER_QUADRATIC) );
5404 // loop on theElemSets
5405 setElemsFirst( theElemSets );
5406 TIDSortedElemSet::iterator itElem;
5407 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5409 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5410 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5411 const SMDS_MeshElement* elem = *itElem;
5412 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5414 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5415 newNodesItVec.reserve( elem->NbNodes() );
5417 // loop on elem nodes
5418 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5419 while ( itN->more() )
5421 const SMDS_MeshNode* node = cast2Node( itN->next() );
5423 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5425 aXYZ.Coord( coord[0], coord[1], coord[2] );
5426 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5428 // check if a node has been already sweeped
5429 TNodeOfNodeListMapItr nIt =
5430 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5431 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5432 if ( listNewNodes.empty() )
5434 // check if we are to create medium nodes between corner ones
5435 bool needMediumNodes = false;
5436 if ( isQuadraticMesh )
5438 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5439 while (it->more() && !needMediumNodes )
5441 const SMDS_MeshElement* invElem = it->next();
5442 if ( invElem != elem && !theElems.count( invElem )) continue;
5443 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5444 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5445 needMediumNodes = true;
5450 const SMDS_MeshNode * newNode = node;
5451 for ( int i = 0; i < theNbSteps; i++ ) {
5453 if ( needMediumNodes ) // create a medium node
5455 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5456 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5457 myLastCreatedNodes.Append(newNode);
5458 srcNodes.Append( node );
5459 listNewNodes.push_back( newNode );
5460 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5463 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5465 // create a corner node
5466 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5467 myLastCreatedNodes.Append(newNode);
5468 srcNodes.Append( node );
5469 listNewNodes.push_back( newNode );
5472 listNewNodes.push_back( newNode );
5473 // if ( needMediumNodes )
5474 // listNewNodes.push_back( newNode );
5478 newNodesItVec.push_back( nIt );
5480 // make new elements
5481 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5486 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5488 PGroupIDs newGroupIDs;
5489 if ( theMakeGroups )
5490 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5495 //=======================================================================
5496 //function : ExtrusParam
5497 //purpose : standard construction
5498 //=======================================================================
5500 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5501 const int theNbSteps,
5503 const double theTolerance):
5505 myFlags( theFlags ),
5506 myTolerance( theTolerance ),
5507 myElemsToUse( NULL )
5509 mySteps = new TColStd_HSequenceOfReal;
5510 const double stepSize = theStep.Magnitude();
5511 for (int i=1; i<=theNbSteps; i++ )
5512 mySteps->Append( stepSize );
5514 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5515 ( theTolerance > 0 ))
5517 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5521 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5525 //=======================================================================
5526 //function : ExtrusParam
5527 //purpose : steps are given explicitly
5528 //=======================================================================
5530 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5531 Handle(TColStd_HSequenceOfReal) theSteps,
5533 const double theTolerance):
5535 mySteps( theSteps ),
5536 myFlags( theFlags ),
5537 myTolerance( theTolerance ),
5538 myElemsToUse( NULL )
5540 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5541 ( theTolerance > 0 ))
5543 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5547 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5551 //=======================================================================
5552 //function : ExtrusParam
5553 //purpose : for extrusion by normal
5554 //=======================================================================
5556 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5557 const int theNbSteps,
5561 mySteps( new TColStd_HSequenceOfReal ),
5562 myFlags( theFlags ),
5564 myElemsToUse( NULL )
5566 for (int i = 0; i < theNbSteps; i++ )
5567 mySteps->Append( theStepSize );
5571 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5575 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5579 //=======================================================================
5580 //function : ExtrusParam::SetElementsToUse
5581 //purpose : stores elements to use for extrusion by normal, depending on
5582 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5583 //=======================================================================
5585 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5587 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5590 //=======================================================================
5591 //function : ExtrusParam::beginStepIter
5592 //purpose : prepare iteration on steps
5593 //=======================================================================
5595 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5597 myWithMediumNodes = withMediumNodes;
5601 //=======================================================================
5602 //function : ExtrusParam::moreSteps
5603 //purpose : are there more steps?
5604 //=======================================================================
5606 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5608 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5610 //=======================================================================
5611 //function : ExtrusParam::nextStep
5612 //purpose : returns the next step
5613 //=======================================================================
5615 double SMESH_MeshEditor::ExtrusParam::nextStep()
5618 if ( !myCurSteps.empty() )
5620 res = myCurSteps.back();
5621 myCurSteps.pop_back();
5623 else if ( myNextStep <= mySteps->Length() )
5625 myCurSteps.push_back( mySteps->Value( myNextStep ));
5627 if ( myWithMediumNodes )
5629 myCurSteps.back() /= 2.;
5630 myCurSteps.push_back( myCurSteps.back() );
5637 //=======================================================================
5638 //function : ExtrusParam::makeNodesByDir
5639 //purpose : create nodes for standard extrusion
5640 //=======================================================================
5642 int SMESH_MeshEditor::ExtrusParam::
5643 makeNodesByDir( SMESHDS_Mesh* mesh,
5644 const SMDS_MeshNode* srcNode,
5645 std::list<const SMDS_MeshNode*> & newNodes,
5646 const bool makeMediumNodes)
5648 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5651 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5653 p += myDir.XYZ() * nextStep();
5654 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5655 newNodes.push_back( newNode );
5660 //=======================================================================
5661 //function : ExtrusParam::makeNodesByDirAndSew
5662 //purpose : create nodes for standard extrusion with sewing
5663 //=======================================================================
5665 int SMESH_MeshEditor::ExtrusParam::
5666 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5667 const SMDS_MeshNode* srcNode,
5668 std::list<const SMDS_MeshNode*> & newNodes,
5669 const bool makeMediumNodes)
5671 gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5674 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5676 P1 += myDir.XYZ() * nextStep();
5678 // try to search in sequence of existing nodes
5679 // if myNodes.Length()>0 we 'nave to use given sequence
5680 // else - use all nodes of mesh
5681 const SMDS_MeshNode * node = 0;
5682 if ( myNodes.Length() > 0 ) {
5684 for(i=1; i<=myNodes.Length(); i++) {
5685 gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5686 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5688 node = myNodes.Value(i);
5694 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5695 while(itn->more()) {
5696 SMESH_TNodeXYZ P2( itn->next() );
5697 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5706 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5708 newNodes.push_back( node );
5715 //=======================================================================
5716 //function : ExtrusParam::makeNodesByNormal2D
5717 //purpose : create nodes for extrusion using normals of faces
5718 //=======================================================================
5720 int SMESH_MeshEditor::ExtrusParam::
5721 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5722 const SMDS_MeshNode* srcNode,
5723 std::list<const SMDS_MeshNode*> & newNodes,
5724 const bool makeMediumNodes)
5726 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5728 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5730 // get normals to faces sharing srcNode
5731 vector< gp_XYZ > norms, baryCenters;
5732 gp_XYZ norm, avgNorm( 0,0,0 );
5733 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5734 while ( faceIt->more() )
5736 const SMDS_MeshElement* face = faceIt->next();
5737 if ( myElemsToUse && !myElemsToUse->count( face ))
5739 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5741 norms.push_back( norm );
5743 if ( !alongAvgNorm )
5747 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5748 bc += SMESH_TNodeXYZ( nIt->next() );
5749 baryCenters.push_back( bc / nbN );
5754 if ( norms.empty() ) return 0;
5756 double normSize = avgNorm.Modulus();
5757 if ( normSize < std::numeric_limits<double>::min() )
5760 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5763 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5766 avgNorm /= normSize;
5769 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5772 double stepSize = nextStep();
5774 if ( norms.size() > 1 )
5776 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5778 // translate plane of a face
5779 baryCenters[ iF ] += norms[ iF ] * stepSize;
5781 // find point of intersection of the face plane located at baryCenters[ iF ]
5782 // and avgNorm located at pNew
5783 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5784 double dot = ( norms[ iF ] * avgNorm );
5785 if ( dot < std::numeric_limits<double>::min() )
5786 dot = stepSize * 1e-3;
5787 double step = -( norms[ iF ] * pNew + d ) / dot;
5788 pNew += step * avgNorm;
5793 pNew += stepSize * avgNorm;
5797 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5798 newNodes.push_back( newNode );
5803 //=======================================================================
5804 //function : ExtrusParam::makeNodesByNormal1D
5805 //purpose : create nodes for extrusion using normals of edges
5806 //=======================================================================
5808 int SMESH_MeshEditor::ExtrusParam::
5809 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5810 const SMDS_MeshNode* srcNode,
5811 std::list<const SMDS_MeshNode*> & newNodes,
5812 const bool makeMediumNodes)
5814 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5818 //=======================================================================
5819 //function : ExtrusionSweep
5821 //=======================================================================
5823 SMESH_MeshEditor::PGroupIDs
5824 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5825 const gp_Vec& theStep,
5826 const int theNbSteps,
5827 TTElemOfElemListMap& newElemsMap,
5829 const double theTolerance)
5831 ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5832 return ExtrusionSweep( theElems, aParams, newElemsMap );
5836 //=======================================================================
5837 //function : ExtrusionSweep
5839 //=======================================================================
5841 SMESH_MeshEditor::PGroupIDs
5842 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5843 ExtrusParam& theParams,
5844 TTElemOfElemListMap& newElemsMap)
5846 myLastCreatedElems.Clear();
5847 myLastCreatedNodes.Clear();
5849 // source elements for each generated one
5850 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5852 //SMESHDS_Mesh* aMesh = GetMeshDS();
5854 setElemsFirst( theElemSets );
5855 const int nbSteps = theParams.NbSteps();
5856 theParams.SetElementsToUse( theElemSets[0] );
5858 TNodeOfNodeListMap mapNewNodes;
5859 //TNodeOfNodeVecMap mapNewNodes;
5860 TElemOfVecOfNnlmiMap mapElemNewNodes;
5861 //TElemOfVecOfMapNodesMap mapElemNewNodes;
5863 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5864 myMesh->NbFaces(ORDER_QUADRATIC) +
5865 myMesh->NbVolumes(ORDER_QUADRATIC) );
5867 TIDSortedElemSet::iterator itElem;
5868 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5870 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5871 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5873 // check element type
5874 const SMDS_MeshElement* elem = *itElem;
5875 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5878 const size_t nbNodes = elem->NbNodes();
5879 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5880 newNodesItVec.reserve( nbNodes );
5882 // loop on elem nodes
5883 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5884 while ( itN->more() )
5886 // check if a node has been already sweeped
5887 const SMDS_MeshNode* node = cast2Node( itN->next() );
5888 TNodeOfNodeListMap::iterator nIt =
5889 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5890 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5891 if ( listNewNodes.empty() )
5895 // check if we are to create medium nodes between corner ones
5896 bool needMediumNodes = false;
5897 if ( isQuadraticMesh )
5899 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5900 while (it->more() && !needMediumNodes )
5902 const SMDS_MeshElement* invElem = it->next();
5903 if ( invElem != elem && !theElems.count( invElem )) continue;
5904 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5905 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5906 needMediumNodes = true;
5909 // create nodes for all steps
5910 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5912 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5913 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5915 myLastCreatedNodes.Append( *newNodesIt );
5916 srcNodes.Append( node );
5921 break; // newNodesItVec will be shorter than nbNodes
5924 newNodesItVec.push_back( nIt );
5926 // make new elements
5927 if ( newNodesItVec.size() == nbNodes )
5928 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5932 if ( theParams.ToMakeBoundary() ) {
5933 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5935 PGroupIDs newGroupIDs;
5936 if ( theParams.ToMakeGroups() )
5937 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5942 //=======================================================================
5943 //function : ExtrusionAlongTrack
5945 //=======================================================================
5946 SMESH_MeshEditor::Extrusion_Error
5947 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5948 SMESH_subMesh* theTrack,
5949 const SMDS_MeshNode* theN1,
5950 const bool theHasAngles,
5951 list<double>& theAngles,
5952 const bool theLinearVariation,
5953 const bool theHasRefPoint,
5954 const gp_Pnt& theRefPoint,
5955 const bool theMakeGroups)
5957 MESSAGE("ExtrusionAlongTrack");
5958 myLastCreatedElems.Clear();
5959 myLastCreatedNodes.Clear();
5962 std::list<double> aPrms;
5963 TIDSortedElemSet::iterator itElem;
5966 TopoDS_Edge aTrackEdge;
5967 TopoDS_Vertex aV1, aV2;
5969 SMDS_ElemIteratorPtr aItE;
5970 SMDS_NodeIteratorPtr aItN;
5971 SMDSAbs_ElementType aTypeE;
5973 TNodeOfNodeListMap mapNewNodes;
5976 aNbE = theElements[0].size() + theElements[1].size();
5979 return EXTR_NO_ELEMENTS;
5981 // 1.1 Track Pattern
5984 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5986 return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
5987 theHasAngles, theAngles, theLinearVariation,
5988 theHasRefPoint, theRefPoint, theMakeGroups );
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 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6244 int startNid = theN1->GetID();
6245 for ( size_t i = 1; i < aNodesList.size(); i++ )
6247 gp_Pnt p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6248 gp_Pnt p2 = SMESH_TNodeXYZ( aNodesList[i] );
6249 TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6250 list<SMESH_MeshEditor_PathPoint> LPP;
6252 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6253 LLPPs.push_back(LPP);
6254 if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i ]->GetID();
6255 else startNid = aNodesList[i-1]->GetID();
6258 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6259 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6260 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6261 for(; itPP!=firstList.end(); itPP++) {
6262 fullList.push_back( *itPP );
6265 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6266 SMESH_MeshEditor_PathPoint PP2;
6267 fullList.pop_back();
6269 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6270 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6271 itPP = currList.begin();
6272 PP2 = currList.front();
6273 gp_Dir D1 = PP1.Tangent();
6274 gp_Dir D2 = PP2.Tangent();
6275 gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6276 PP1.SetTangent(Dnew);
6277 fullList.push_back(PP1);
6279 for(; itPP!=currList.end(); itPP++) {
6280 fullList.push_back( *itPP );
6282 PP1 = fullList.back();
6283 fullList.pop_back();
6285 fullList.push_back(PP1);
6287 } // Sub-shape for the Pattern must be an Edge or Wire
6288 else if ( aS.ShapeType() == TopAbs_EDGE )
6290 aTrackEdge = TopoDS::Edge( aS );
6291 // the Edge must not be degenerated
6292 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6293 return EXTR_BAD_PATH_SHAPE;
6294 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6295 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6296 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6297 // starting node must be aN1 or aN2
6298 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6299 return EXTR_BAD_STARTING_NODE;
6300 aItN = pMeshDS->nodesIterator();
6301 while ( aItN->more() ) {
6302 const SMDS_MeshNode* pNode = aItN->next();
6303 if( pNode==aN1 || pNode==aN2 ) continue;
6304 const SMDS_EdgePosition* pEPos =
6305 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6306 double aT = pEPos->GetUParameter();
6307 aPrms.push_back( aT );
6309 //Extrusion_Error err =
6310 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6312 else if( aS.ShapeType() == TopAbs_WIRE ) {
6313 list< SMESH_subMesh* > LSM;
6314 TopTools_SequenceOfShape Edges;
6315 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6316 for(; eExp.More(); eExp.Next()) {
6317 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6318 if( SMESH_Algo::isDegenerated(E) ) continue;
6319 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6325 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6326 TopoDS_Vertex aVprev;
6327 TColStd_MapOfInteger UsedNums;
6328 int NbEdges = Edges.Length();
6330 for(; i<=NbEdges; i++) {
6332 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6333 for(; itLSM!=LSM.end(); itLSM++) {
6335 if(UsedNums.Contains(k)) continue;
6336 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6337 SMESH_subMesh* locTrack = *itLSM;
6338 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6339 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6340 bool aN1isOK = false, aN2isOK = false;
6341 if ( aVprev.IsNull() ) {
6342 // if previous vertex is not yet defined, it means that we in the beginning of wire
6343 // and we have to find initial vertex corresponding to starting node theN1
6344 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6345 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6346 // starting node must be aN1 or aN2
6347 aN1isOK = ( aN1 && aN1 == theN1 );
6348 aN2isOK = ( aN2 && aN2 == theN1 );
6351 // we have specified ending vertex of the previous edge on the previous iteration
6352 // and we have just to check that it corresponds to any vertex in current segment
6353 aN1isOK = aVprev.IsSame( aV1 );
6354 aN2isOK = aVprev.IsSame( aV2 );
6356 if ( !aN1isOK && !aN2isOK ) continue;
6357 // 2. Collect parameters on the track edge
6359 aItN = locMeshDS->GetNodes();
6360 while ( aItN->more() ) {
6361 const SMDS_MeshNode* pNode = aItN->next();
6362 const SMDS_EdgePosition* pEPos =
6363 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6364 double aT = pEPos->GetUParameter();
6365 aPrms.push_back( aT );
6367 list<SMESH_MeshEditor_PathPoint> LPP;
6368 //Extrusion_Error err =
6369 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6370 LLPPs.push_back(LPP);
6372 // update startN for search following egde
6373 if ( aN1isOK ) aVprev = aV2;
6378 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6379 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6380 fullList.splice( fullList.end(), firstList );
6382 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6383 fullList.pop_back();
6385 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6386 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6387 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6388 gp_Dir D1 = PP1.Tangent();
6389 gp_Dir D2 = PP2.Tangent();
6390 gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6391 PP1.SetTangent(Dnew);
6392 fullList.push_back(PP1);
6393 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6394 PP1 = fullList.back();
6395 fullList.pop_back();
6397 // if wire not closed
6398 fullList.push_back(PP1);
6402 return EXTR_BAD_PATH_SHAPE;
6405 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6406 theHasRefPoint, theRefPoint, theMakeGroups);
6410 //=======================================================================
6411 //function : MakeEdgePathPoints
6412 //purpose : auxilary for ExtrusionAlongTrack
6413 //=======================================================================
6414 SMESH_MeshEditor::Extrusion_Error
6415 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
6416 const TopoDS_Edge& aTrackEdge,
6418 list<SMESH_MeshEditor_PathPoint>& LPP)
6420 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6422 aTolVec2=aTolVec*aTolVec;
6424 TopoDS_Vertex aV1, aV2;
6425 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6426 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6427 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6428 // 2. Collect parameters on the track edge
6429 aPrms.push_front( aT1 );
6430 aPrms.push_back( aT2 );
6433 if( FirstIsStart ) {
6444 SMESH_MeshEditor_PathPoint aPP;
6445 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6446 std::list<double>::iterator aItD = aPrms.begin();
6447 for(; aItD != aPrms.end(); ++aItD) {
6451 aC3D->D1( aT, aP3D, aVec );
6452 aL2 = aVec.SquareMagnitude();
6453 if ( aL2 < aTolVec2 )
6454 return EXTR_CANT_GET_TANGENT;
6455 gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6457 aPP.SetTangent( aTgt );
6458 aPP.SetParameter( aT );
6465 //=======================================================================
6466 //function : MakeExtrElements
6467 //purpose : auxilary for ExtrusionAlongTrack
6468 //=======================================================================
6469 SMESH_MeshEditor::Extrusion_Error
6470 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet theElemSets[2],
6471 list<SMESH_MeshEditor_PathPoint>& fullList,
6472 const bool theHasAngles,
6473 list<double>& theAngles,
6474 const bool theLinearVariation,
6475 const bool theHasRefPoint,
6476 const gp_Pnt& theRefPoint,
6477 const bool theMakeGroups)
6479 const int aNbTP = fullList.size();
6482 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6483 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++ )
6516 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();
6531 list<const SMDS_MeshNode*> emptyList;
6533 setElemsFirst( theElemSets );
6534 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6536 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6537 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6539 const SMDS_MeshElement* elem = *itElem;
6541 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6542 newNodesItVec.reserve( elem->NbNodes() );
6544 // loop on elem nodes
6546 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6547 while ( itN->more() )
6550 // check if a node has been already processed
6551 const SMDS_MeshNode* node = cast2Node( itN->next() );
6552 TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6553 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6554 if ( listNewNodes.empty() )
6557 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6558 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6559 gp_Ax1 anAx1, anAxT1T0;
6560 gp_Dir aDT1x, aDT0x, aDT1T0;
6565 aPN0 = SMESH_TNodeXYZ( node );
6567 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6569 aDT0x= aPP0.Tangent();
6571 for ( int j = 1; j < aNbTP; ++j ) {
6572 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6574 aDT1x = aPP1.Tangent();
6575 aAngle1x = aPP1.Angle();
6577 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6579 gp_Vec aV01x( aP0x, aP1x );
6580 aTrsf.SetTranslation( aV01x );
6583 aV1x = aV0x.Transformed( aTrsf );
6584 aPN1 = aPN0.Transformed( aTrsf );
6586 // rotation 1 [ T1,T0 ]
6587 aAngleT1T0=-aDT1x.Angle( aDT0x );
6588 if (fabs(aAngleT1T0) > aTolAng)
6591 anAxT1T0.SetLocation( aV1x );
6592 anAxT1T0.SetDirection( aDT1T0 );
6593 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6595 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6599 if ( theHasAngles ) {
6600 anAx1.SetLocation( aV1x );
6601 anAx1.SetDirection( aDT1x );
6602 aTrsfRot.SetRotation( anAx1, aAngle1x );
6604 aPN1 = aPN1.Transformed( aTrsfRot );
6608 if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6610 // create additional node
6611 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6612 const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6613 myLastCreatedNodes.Append(newNode);
6614 srcNodes.Append( node );
6615 listNewNodes.push_back( newNode );
6617 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6618 myLastCreatedNodes.Append(newNode);
6619 srcNodes.Append( node );
6620 listNewNodes.push_back( newNode );
6628 else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6630 // if current elem is quadratic and current node is not medium
6631 // we have to check - may be it is needed to insert additional nodes
6632 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6633 if ((int) listNewNodes.size() == aNbTP-1 )
6635 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6636 gp_XYZ P(node->X(), node->Y(), node->Z());
6637 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6639 for(i=0; i<aNbTP-1; i++) {
6640 const SMDS_MeshNode* N = *it;
6641 double x = ( N->X() + P.X() )/2.;
6642 double y = ( N->Y() + P.Y() )/2.;
6643 double z = ( N->Z() + P.Z() )/2.;
6644 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6645 srcNodes.Append( node );
6646 myLastCreatedNodes.Append(newN);
6649 P = gp_XYZ(N->X(),N->Y(),N->Z());
6651 listNewNodes.clear();
6652 for(i=0; i<2*(aNbTP-1); i++) {
6653 listNewNodes.push_back(aNodes[i]);
6658 newNodesItVec.push_back( nIt );
6661 // make new elements
6662 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6666 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6668 if ( theMakeGroups )
6669 generateGroups( srcNodes, srcElems, "extruded");
6675 //=======================================================================
6676 //function : LinearAngleVariation
6677 //purpose : auxilary for ExtrusionAlongTrack
6678 //=======================================================================
6679 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6680 list<double>& Angles)
6682 int nbAngles = Angles.size();
6683 if( nbSteps > nbAngles ) {
6684 vector<double> theAngles(nbAngles);
6685 list<double>::iterator it = Angles.begin();
6687 for(; it!=Angles.end(); it++) {
6689 theAngles[i] = (*it);
6692 double rAn2St = double( nbAngles ) / double( nbSteps );
6693 double angPrev = 0, angle;
6694 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6695 double angCur = rAn2St * ( iSt+1 );
6696 double angCurFloor = floor( angCur );
6697 double angPrevFloor = floor( angPrev );
6698 if ( angPrevFloor == angCurFloor )
6699 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6701 int iP = int( angPrevFloor );
6702 double angPrevCeil = ceil(angPrev);
6703 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6705 int iC = int( angCurFloor );
6706 if ( iC < nbAngles )
6707 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6709 iP = int( angPrevCeil );
6711 angle += theAngles[ iC ];
6713 res.push_back(angle);
6718 for(; it!=res.end(); it++)
6719 Angles.push_back( *it );
6724 //================================================================================
6726 * \brief Move or copy theElements applying theTrsf to their nodes
6727 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6728 * \param theTrsf - transformation to apply
6729 * \param theCopy - if true, create translated copies of theElems
6730 * \param theMakeGroups - if true and theCopy, create translated groups
6731 * \param theTargetMesh - mesh to copy translated elements into
6732 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6734 //================================================================================
6736 SMESH_MeshEditor::PGroupIDs
6737 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6738 const gp_Trsf& theTrsf,
6740 const bool theMakeGroups,
6741 SMESH_Mesh* theTargetMesh)
6743 myLastCreatedElems.Clear();
6744 myLastCreatedNodes.Clear();
6746 bool needReverse = false;
6747 string groupPostfix;
6748 switch ( theTrsf.Form() ) {
6750 MESSAGE("gp_PntMirror");
6752 groupPostfix = "mirrored";
6755 MESSAGE("gp_Ax1Mirror");
6756 groupPostfix = "mirrored";
6759 MESSAGE("gp_Ax2Mirror");
6761 groupPostfix = "mirrored";
6764 MESSAGE("gp_Rotation");
6765 groupPostfix = "rotated";
6767 case gp_Translation:
6768 MESSAGE("gp_Translation");
6769 groupPostfix = "translated";
6772 MESSAGE("gp_Scale");
6773 groupPostfix = "scaled";
6775 case gp_CompoundTrsf: // different scale by axis
6776 MESSAGE("gp_CompoundTrsf");
6777 groupPostfix = "scaled";
6781 needReverse = false;
6782 groupPostfix = "transformed";
6785 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6786 SMESHDS_Mesh* aMesh = GetMeshDS();
6788 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6789 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6790 SMESH_MeshEditor::ElemFeatures elemType;
6792 // map old node to new one
6793 TNodeNodeMap nodeMap;
6795 // elements sharing moved nodes; those of them which have all
6796 // nodes mirrored but are not in theElems are to be reversed
6797 TIDSortedElemSet inverseElemSet;
6799 // source elements for each generated one
6800 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6802 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6803 TIDSortedElemSet orphanNode;
6805 if ( theElems.empty() ) // transform the whole mesh
6808 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6809 while ( eIt->more() ) theElems.insert( eIt->next() );
6811 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6812 while ( nIt->more() )
6814 const SMDS_MeshNode* node = nIt->next();
6815 if ( node->NbInverseElements() == 0)
6816 orphanNode.insert( node );
6820 // loop on elements to transform nodes : first orphan nodes then elems
6821 TIDSortedElemSet::iterator itElem;
6822 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6823 for (int i=0; i<2; i++)
6824 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6826 const SMDS_MeshElement* elem = *itElem;
6830 // loop on elem nodes
6832 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6833 while ( itN->more() )
6835 const SMDS_MeshNode* node = cast2Node( itN->next() );
6836 // check if a node has been already transformed
6837 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6838 nodeMap.insert( make_pair ( node, node ));
6839 if ( !n2n_isnew.second )
6842 node->GetXYZ( coord );
6843 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6844 if ( theTargetMesh ) {
6845 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6846 n2n_isnew.first->second = newNode;
6847 myLastCreatedNodes.Append(newNode);
6848 srcNodes.Append( node );
6850 else if ( theCopy ) {
6851 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6852 n2n_isnew.first->second = newNode;
6853 myLastCreatedNodes.Append(newNode);
6854 srcNodes.Append( node );
6857 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6858 // node position on shape becomes invalid
6859 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6860 ( SMDS_SpacePosition::originSpacePosition() );
6863 // keep inverse elements
6864 if ( !theCopy && !theTargetMesh && needReverse ) {
6865 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6866 while ( invElemIt->more() ) {
6867 const SMDS_MeshElement* iel = invElemIt->next();
6868 inverseElemSet.insert( iel );
6872 } // loop on elems in { &orphanNode, &theElems };
6874 // either create new elements or reverse mirrored ones
6875 if ( !theCopy && !needReverse && !theTargetMesh )
6878 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6880 // Replicate or reverse elements
6882 std::vector<int> iForw;
6883 vector<const SMDS_MeshNode*> nodes;
6884 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6886 const SMDS_MeshElement* elem = *itElem;
6887 if ( !elem ) continue;
6889 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6890 size_t nbNodes = elem->NbNodes();
6891 if ( geomType == SMDSGeom_NONE ) continue; // node
6893 nodes.resize( nbNodes );
6895 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6897 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6901 bool allTransformed = true;
6902 int nbFaces = aPolyedre->NbFaces();
6903 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6905 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6906 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6908 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6909 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6910 if ( nodeMapIt == nodeMap.end() )
6911 allTransformed = false; // not all nodes transformed
6913 nodes.push_back((*nodeMapIt).second);
6915 if ( needReverse && allTransformed )
6916 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6918 if ( !allTransformed )
6919 continue; // not all nodes transformed
6921 else // ----------------------- the rest element types
6923 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6924 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6925 const vector<int>& i = needReverse ? iRev : iForw;
6927 // find transformed nodes
6929 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6930 while ( itN->more() ) {
6931 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6932 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6933 if ( nodeMapIt == nodeMap.end() )
6934 break; // not all nodes transformed
6935 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6937 if ( iNode != nbNodes )
6938 continue; // not all nodes transformed
6942 // copy in this or a new mesh
6943 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6944 srcElems.Append( elem );
6947 // reverse element as it was reversed by transformation
6949 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6952 } // loop on elements
6954 if ( editor && editor != this )
6955 myLastCreatedElems = editor->myLastCreatedElems;
6957 PGroupIDs newGroupIDs;
6959 if ( ( theMakeGroups && theCopy ) ||
6960 ( theMakeGroups && theTargetMesh ) )
6961 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6966 //=======================================================================
6968 * \brief Create groups of elements made during transformation
6969 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6970 * \param elemGens - elements making corresponding myLastCreatedElems
6971 * \param postfix - to append to names of new groups
6972 * \param targetMesh - mesh to create groups in
6973 * \param topPresent - is there "top" elements that are created by sweeping
6975 //=======================================================================
6977 SMESH_MeshEditor::PGroupIDs
6978 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6979 const SMESH_SequenceOfElemPtr& elemGens,
6980 const std::string& postfix,
6981 SMESH_Mesh* targetMesh,
6982 const bool topPresent)
6984 PGroupIDs newGroupIDs( new list<int> );
6985 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6987 // Sort existing groups by types and collect their names
6989 // containers to store an old group and generated new ones;
6990 // 1st new group is for result elems of different type than a source one;
6991 // 2nd new group is for same type result elems ("top" group at extrusion)
6993 using boost::make_tuple;
6994 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6995 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6996 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6998 set< string > groupNames;
7000 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7001 if ( !groupIt->more() ) return newGroupIDs;
7003 int newGroupID = mesh->GetGroupIds().back()+1;
7004 while ( groupIt->more() )
7006 SMESH_Group * group = groupIt->next();
7007 if ( !group ) continue;
7008 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7009 if ( !groupDS || groupDS->IsEmpty() ) continue;
7010 groupNames.insert ( group->GetName() );
7011 groupDS->SetStoreName( group->GetName() );
7012 const SMDSAbs_ElementType type = groupDS->GetType();
7013 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7014 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7015 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7016 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7019 // Loop on nodes and elements to add them in new groups
7021 vector< const SMDS_MeshElement* > resultElems;
7022 for ( int isNodes = 0; isNodes < 2; ++isNodes )
7024 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
7025 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7026 if ( gens.Length() != elems.Length() )
7027 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7029 // loop on created elements
7030 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7032 const SMDS_MeshElement* sourceElem = gens( iElem );
7033 if ( !sourceElem ) {
7034 MESSAGE("generateGroups(): NULL source element");
7037 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7038 if ( groupsOldNew.empty() ) { // no groups of this type at all
7039 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7040 ++iElem; // skip all elements made by sourceElem
7043 // collect all elements made by the iElem-th sourceElem
7044 resultElems.clear();
7045 if ( const SMDS_MeshElement* resElem = elems( iElem ))
7046 if ( resElem != sourceElem )
7047 resultElems.push_back( resElem );
7048 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7049 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7050 if ( resElem != sourceElem )
7051 resultElems.push_back( resElem );
7053 const SMDS_MeshElement* topElem = 0;
7054 if ( isNodes ) // there must be a top element
7056 topElem = resultElems.back();
7057 resultElems.pop_back();
7061 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7062 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7063 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7065 topElem = *resElemIt;
7066 *resElemIt = 0; // erase *resElemIt
7070 // add resultElems to groups originted from ones the sourceElem belongs to
7071 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7072 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7074 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7075 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7077 // fill in a new group
7078 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7079 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7080 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7082 newGroup.Add( *resElemIt );
7084 // fill a "top" group
7087 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7088 newTopGroup.Add( topElem );
7092 } // loop on created elements
7093 }// loop on nodes and elements
7095 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7097 list<int> topGrouIds;
7098 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7100 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
7101 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7102 orderedOldNewGroups[i]->get<2>() };
7103 for ( int is2nd = 0; is2nd < 2; ++is2nd )
7105 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7106 if ( newGroupDS->IsEmpty() )
7108 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7113 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7116 const bool isTop = ( topPresent &&
7117 newGroupDS->GetType() == oldGroupDS->GetType() &&
7120 string name = oldGroupDS->GetStoreName();
7121 { // remove trailing whitespaces (issue 22599)
7122 size_t size = name.size();
7123 while ( size > 1 && isspace( name[ size-1 ]))
7125 if ( size != name.size() )
7127 name.resize( size );
7128 oldGroupDS->SetStoreName( name.c_str() );
7131 if ( !targetMesh ) {
7132 string suffix = ( isTop ? "top": postfix.c_str() );
7136 while ( !groupNames.insert( name ).second ) // name exists
7137 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7142 newGroupDS->SetStoreName( name.c_str() );
7144 // make a SMESH_Groups
7145 mesh->AddGroup( newGroupDS );
7147 topGrouIds.push_back( newGroupDS->GetID() );
7149 newGroupIDs->push_back( newGroupDS->GetID() );
7153 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7158 //================================================================================
7160 * * \brief Return list of group of nodes close to each other within theTolerance
7161 * * Search among theNodes or in the whole mesh if theNodes is empty using
7162 * * an Octree algorithm
7163 * \param [in,out] theNodes - the nodes to treat
7164 * \param [in] theTolerance - the tolerance
7165 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7166 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7167 * corner and medium nodes in separate groups
7169 //================================================================================
7171 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7172 const double theTolerance,
7173 TListOfListOfNodes & theGroupsOfNodes,
7174 bool theSeparateCornersAndMedium)
7176 myLastCreatedElems.Clear();
7177 myLastCreatedNodes.Clear();
7179 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7180 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7181 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7182 theSeparateCornersAndMedium = false;
7184 TIDSortedNodeSet& corners = theNodes;
7185 TIDSortedNodeSet medium;
7187 if ( theNodes.empty() ) // get all nodes in the mesh
7189 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7190 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7191 if ( theSeparateCornersAndMedium )
7192 while ( nIt->more() )
7194 const SMDS_MeshNode* n = nIt->next();
7195 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7196 nodeSet->insert( nodeSet->end(), n );
7199 while ( nIt->more() )
7200 theNodes.insert( theNodes.end(),nIt->next() );
7202 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7204 TIDSortedNodeSet::iterator nIt = corners.begin();
7205 while ( nIt != corners.end() )
7206 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7208 medium.insert( medium.end(), *nIt );
7209 corners.erase( nIt++ );
7217 if ( !corners.empty() )
7218 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7219 if ( !medium.empty() )
7220 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7223 //=======================================================================
7224 //function : SimplifyFace
7225 //purpose : split a chain of nodes into several closed chains
7226 //=======================================================================
7228 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7229 vector<const SMDS_MeshNode *>& poly_nodes,
7230 vector<int>& quantities) const
7232 int nbNodes = faceNodes.size();
7237 set<const SMDS_MeshNode*> nodeSet;
7239 // get simple seq of nodes
7240 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7243 simpleNodes[iSimple++] = faceNodes[0];
7244 for (int iCur = 1; iCur < nbNodes; iCur++) {
7245 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7246 simpleNodes[iSimple++] = faceNodes[iCur];
7247 nodeSet.insert( faceNodes[iCur] );
7250 int nbUnique = nodeSet.size();
7251 int nbSimple = iSimple;
7252 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7262 bool foundLoop = (nbSimple > nbUnique);
7265 set<const SMDS_MeshNode*> loopSet;
7266 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7267 const SMDS_MeshNode* n = simpleNodes[iSimple];
7268 if (!loopSet.insert( n ).second) {
7272 int iC = 0, curLast = iSimple;
7273 for (; iC < curLast; iC++) {
7274 if (simpleNodes[iC] == n) break;
7276 int loopLen = curLast - iC;
7278 // create sub-element
7280 quantities.push_back(loopLen);
7281 for (; iC < curLast; iC++) {
7282 poly_nodes.push_back(simpleNodes[iC]);
7285 // shift the rest nodes (place from the first loop position)
7286 for (iC = curLast + 1; iC < nbSimple; iC++) {
7287 simpleNodes[iC - loopLen] = simpleNodes[iC];
7289 nbSimple -= loopLen;
7292 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7293 } // while (foundLoop)
7297 quantities.push_back(iSimple);
7298 for (int i = 0; i < iSimple; i++)
7299 poly_nodes.push_back(simpleNodes[i]);
7305 //=======================================================================
7306 //function : MergeNodes
7307 //purpose : In each group, the cdr of nodes are substituted by the first one
7309 //=======================================================================
7311 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7313 MESSAGE("MergeNodes");
7314 myLastCreatedElems.Clear();
7315 myLastCreatedNodes.Clear();
7317 SMESHDS_Mesh* aMesh = GetMeshDS();
7319 TNodeNodeMap nodeNodeMap; // node to replace - new node
7320 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7321 list< int > rmElemIds, rmNodeIds;
7323 // Fill nodeNodeMap and elems
7325 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7326 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7328 list<const SMDS_MeshNode*>& nodes = *grIt;
7329 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7330 const SMDS_MeshNode* nToKeep = *nIt;
7331 for ( ++nIt; nIt != nodes.end(); nIt++ )
7333 const SMDS_MeshNode* nToRemove = *nIt;
7334 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7335 if ( nToRemove != nToKeep )
7337 rmNodeIds.push_back( nToRemove->GetID() );
7338 AddToSameGroups( nToKeep, nToRemove, aMesh );
7339 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7340 // after MergeNodes() w/o creating node in place of merged ones.
7341 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7342 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7343 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7344 sm->SetIsAlwaysComputed( true );
7346 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7347 while ( invElemIt->more() ) {
7348 const SMDS_MeshElement* elem = invElemIt->next();
7353 // Change element nodes or remove an element
7355 set<const SMDS_MeshNode*> nodeSet;
7356 vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7358 ElemFeatures elemType;
7360 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7361 for ( ; eIt != elems.end(); eIt++ )
7363 const SMDS_MeshElement* elem = *eIt;
7364 const int nbNodes = elem->NbNodes();
7365 const int aShapeId = FindShape( elem );
7368 curNodes.resize( nbNodes );
7369 uniqueNodes.resize( nbNodes );
7370 iRepl.resize( nbNodes );
7371 int iUnique = 0, iCur = 0, nbRepl = 0;
7373 // get new seq of nodes
7374 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7375 while ( itN->more() )
7377 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7379 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7380 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7382 { ////////// BUG 0020185: begin
7383 bool stopRecur = false;
7384 set<const SMDS_MeshNode*> nodesRecur;
7385 nodesRecur.insert(n);
7386 while (!stopRecur) {
7387 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7388 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7389 n = (*nnIt_i).second;
7390 if (!nodesRecur.insert(n).second) {
7391 // error: recursive dependancy
7398 } ////////// BUG 0020185: end
7400 curNodes[ iCur ] = n;
7401 bool isUnique = nodeSet.insert( n ).second;
7403 uniqueNodes[ iUnique++ ] = n;
7405 iRepl[ nbRepl++ ] = iCur;
7409 // Analyse element topology after replacement
7412 int nbUniqueNodes = nodeSet.size();
7413 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7415 if (elem->IsPoly()) // Polygons and Polyhedral volumes
7417 if (elem->GetType() == SMDSAbs_Face) // Polygon
7419 elemType.Init( elem );
7420 const bool isQuad = elemType.myIsQuad;
7422 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7423 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7425 // a polygon can divide into several elements
7426 vector<const SMDS_MeshNode *> polygons_nodes;
7427 vector<int> quantities;
7428 int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7431 vector<const SMDS_MeshNode *> face_nodes;
7433 for (int iface = 0; iface < nbNew; iface++)
7435 int nbNewNodes = quantities[iface];
7436 face_nodes.assign( polygons_nodes.begin() + inode,
7437 polygons_nodes.begin() + inode + nbNewNodes );
7438 inode += nbNewNodes;
7439 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7441 bool isValid = ( nbNewNodes % 2 == 0 );
7442 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7443 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7444 elemType.SetQuad( isValid );
7445 if ( isValid ) // put medium nodes after corners
7446 SMDS_MeshCell::applyInterlaceRev
7447 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7448 nbNewNodes ), face_nodes );
7450 elemType.SetPoly(( nbNewNodes / ( elemType.myIsQuad + 1 ) > 4 ));
7452 SMDS_MeshElement* newElem = AddElement( face_nodes, elemType );
7454 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7457 rmElemIds.push_back(elem->GetID());
7461 else if (elem->GetType() == SMDSAbs_Volume) // Polyhedral volume
7463 if (nbUniqueNodes < 4) {
7464 rmElemIds.push_back(elem->GetID());
7467 // each face has to be analyzed in order to check volume validity
7468 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
7471 int nbFaces = aPolyedre->NbFaces();
7473 vector<const SMDS_MeshNode *> poly_nodes;
7474 vector<int> quantities;
7476 for (int iface = 1; iface <= nbFaces; iface++) {
7477 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7478 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7480 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7481 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7482 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7483 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7484 faceNode = (*nnIt).second;
7486 faceNodes[inode - 1] = faceNode;
7489 SimplifyFace(faceNodes, poly_nodes, quantities);
7492 if (quantities.size() > 3) {
7493 // to be done: remove coincident faces
7496 if (quantities.size() > 3)
7498 const SMDS_MeshElement* newElem =
7499 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7500 myLastCreatedElems.Append(newElem);
7501 if ( aShapeId && newElem )
7502 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7503 rmElemIds.push_back(elem->GetID());
7507 rmElemIds.push_back(elem->GetID());
7518 // TODO not all the possible cases are solved. Find something more generic?
7519 switch ( nbNodes ) {
7520 case 2: ///////////////////////////////////// EDGE
7521 isOk = false; break;
7522 case 3: ///////////////////////////////////// TRIANGLE
7523 isOk = false; break;
7525 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7527 else { //////////////////////////////////// QUADRANGLE
7528 if ( nbUniqueNodes < 3 )
7530 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7531 isOk = false; // opposite nodes stick
7532 //MESSAGE("isOk " << isOk);
7535 case 6: ///////////////////////////////////// PENTAHEDRON
7536 if ( nbUniqueNodes == 4 ) {
7537 // ---------------------------------> tetrahedron
7539 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7540 // all top nodes stick: reverse a bottom
7541 uniqueNodes[ 0 ] = curNodes [ 1 ];
7542 uniqueNodes[ 1 ] = curNodes [ 0 ];
7544 else if (nbRepl == 3 &&
7545 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7546 // all bottom nodes stick: set a top before
7547 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7548 uniqueNodes[ 0 ] = curNodes [ 3 ];
7549 uniqueNodes[ 1 ] = curNodes [ 4 ];
7550 uniqueNodes[ 2 ] = curNodes [ 5 ];
7552 else if (nbRepl == 4 &&
7553 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7554 // a lateral face turns into a line: reverse a bottom
7555 uniqueNodes[ 0 ] = curNodes [ 1 ];
7556 uniqueNodes[ 1 ] = curNodes [ 0 ];
7561 else if ( nbUniqueNodes == 5 ) {
7562 // PENTAHEDRON --------------------> 2 tetrahedrons
7563 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7564 // a bottom node sticks with a linked top one
7566 SMDS_MeshElement* newElem =
7567 aMesh->AddVolume(curNodes[ 3 ],
7570 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7571 myLastCreatedElems.Append(newElem);
7573 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7574 // 2. : reverse a bottom
7575 uniqueNodes[ 0 ] = curNodes [ 1 ];
7576 uniqueNodes[ 1 ] = curNodes [ 0 ];
7586 if(elem->IsQuadratic()) { // Quadratic quadrangle
7598 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7601 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7603 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7604 uniqueNodes[0] = curNodes[0];
7605 uniqueNodes[1] = curNodes[2];
7606 uniqueNodes[2] = curNodes[3];
7607 uniqueNodes[3] = curNodes[5];
7608 uniqueNodes[4] = curNodes[6];
7609 uniqueNodes[5] = curNodes[7];
7612 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7613 uniqueNodes[0] = curNodes[0];
7614 uniqueNodes[1] = curNodes[1];
7615 uniqueNodes[2] = curNodes[2];
7616 uniqueNodes[3] = curNodes[4];
7617 uniqueNodes[4] = curNodes[5];
7618 uniqueNodes[5] = curNodes[6];
7621 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7622 uniqueNodes[0] = curNodes[1];
7623 uniqueNodes[1] = curNodes[2];
7624 uniqueNodes[2] = curNodes[3];
7625 uniqueNodes[3] = curNodes[5];
7626 uniqueNodes[4] = curNodes[6];
7627 uniqueNodes[5] = curNodes[0];
7630 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7631 uniqueNodes[0] = curNodes[0];
7632 uniqueNodes[1] = curNodes[1];
7633 uniqueNodes[2] = curNodes[3];
7634 uniqueNodes[3] = curNodes[4];
7635 uniqueNodes[4] = curNodes[6];
7636 uniqueNodes[5] = curNodes[7];
7639 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7640 uniqueNodes[0] = curNodes[0];
7641 uniqueNodes[1] = curNodes[2];
7642 uniqueNodes[2] = curNodes[3];
7643 uniqueNodes[3] = curNodes[1];
7644 uniqueNodes[4] = curNodes[6];
7645 uniqueNodes[5] = curNodes[7];
7648 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7649 uniqueNodes[0] = curNodes[0];
7650 uniqueNodes[1] = curNodes[1];
7651 uniqueNodes[2] = curNodes[2];
7652 uniqueNodes[3] = curNodes[4];
7653 uniqueNodes[4] = curNodes[5];
7654 uniqueNodes[5] = curNodes[7];
7657 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7658 uniqueNodes[0] = curNodes[0];
7659 uniqueNodes[1] = curNodes[1];
7660 uniqueNodes[2] = curNodes[3];
7661 uniqueNodes[3] = curNodes[4];
7662 uniqueNodes[4] = curNodes[2];
7663 uniqueNodes[5] = curNodes[7];
7666 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7667 uniqueNodes[0] = curNodes[0];
7668 uniqueNodes[1] = curNodes[1];
7669 uniqueNodes[2] = curNodes[2];
7670 uniqueNodes[3] = curNodes[4];
7671 uniqueNodes[4] = curNodes[5];
7672 uniqueNodes[5] = curNodes[3];
7677 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7680 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7684 //////////////////////////////////// HEXAHEDRON
7686 SMDS_VolumeTool hexa (elem);
7687 hexa.SetExternalNormal();
7688 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7689 //////////////////////// HEX ---> 1 tetrahedron
7690 for ( int iFace = 0; iFace < 6; iFace++ ) {
7691 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7692 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7693 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7694 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7695 // one face turns into a point ...
7696 int iOppFace = hexa.GetOppFaceIndex( iFace );
7697 ind = hexa.GetFaceNodesIndices( iOppFace );
7699 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7700 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7703 if ( nbStick == 1 ) {
7704 // ... and the opposite one - into a triangle.
7706 ind = hexa.GetFaceNodesIndices( iFace );
7707 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7714 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7715 //////////////////////// HEX ---> 1 prism
7716 int nbTria = 0, iTria[3];
7717 const int *ind; // indices of face nodes
7718 // look for triangular faces
7719 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7720 ind = hexa.GetFaceNodesIndices( iFace );
7721 TIDSortedNodeSet faceNodes;
7722 for ( iCur = 0; iCur < 4; iCur++ )
7723 faceNodes.insert( curNodes[ind[iCur]] );
7724 if ( faceNodes.size() == 3 )
7725 iTria[ nbTria++ ] = iFace;
7727 // check if triangles are opposite
7728 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7731 // set nodes of the bottom triangle
7732 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7734 for ( iCur = 0; iCur < 4; iCur++ )
7735 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7736 indB.push_back( ind[iCur] );
7737 if ( !hexa.IsForward() )
7738 std::swap( indB[0], indB[2] );
7739 for ( iCur = 0; iCur < 3; iCur++ )
7740 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7741 // set nodes of the top triangle
7742 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7743 for ( iCur = 0; iCur < 3; ++iCur )
7744 for ( int j = 0; j < 4; ++j )
7745 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7747 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7753 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7754 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7755 for ( int iFace = 0; iFace < 6; iFace++ ) {
7756 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7757 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7758 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7759 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7760 // one face turns into a point ...
7761 int iOppFace = hexa.GetOppFaceIndex( iFace );
7762 ind = hexa.GetFaceNodesIndices( iOppFace );
7764 iUnique = 2; // reverse a tetrahedron 1 bottom
7765 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7766 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7768 else if ( iUnique >= 0 )
7769 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7771 if ( nbStick == 0 ) {
7772 // ... and the opposite one is a quadrangle
7774 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7775 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7778 SMDS_MeshElement* newElem =
7779 aMesh->AddVolume(curNodes[ind[ 0 ]],
7782 curNodes[indTop[ 0 ]]);
7783 myLastCreatedElems.Append(newElem);
7785 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7792 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7793 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7794 // find indices of quad and tri faces
7795 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7796 for ( iFace = 0; iFace < 6; iFace++ ) {
7797 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7799 for ( iCur = 0; iCur < 4; iCur++ )
7800 nodeSet.insert( curNodes[ind[ iCur ]] );
7801 nbUniqueNodes = nodeSet.size();
7802 if ( nbUniqueNodes == 3 )
7803 iTriFace[ nbTri++ ] = iFace;
7804 else if ( nbUniqueNodes == 4 )
7805 iQuadFace[ nbQuad++ ] = iFace;
7807 if (nbQuad == 2 && nbTri == 4 &&
7808 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7809 // 2 opposite quadrangles stuck with a diagonal;
7810 // sample groups of merged indices: (0-4)(2-6)
7811 // --------------------------------------------> 2 tetrahedrons
7812 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7813 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7814 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7815 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7816 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7817 // stuck with 0-2 diagonal
7825 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7826 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7827 // stuck with 1-3 diagonal
7839 uniqueNodes[ 0 ] = curNodes [ i0 ];
7840 uniqueNodes[ 1 ] = curNodes [ i1d ];
7841 uniqueNodes[ 2 ] = curNodes [ i3d ];
7842 uniqueNodes[ 3 ] = curNodes [ i0t ];
7845 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7849 myLastCreatedElems.Append(newElem);
7851 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7854 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7855 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7856 // --------------------------------------------> prism
7857 // find 2 opposite triangles
7859 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7860 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7861 // find indices of kept and replaced nodes
7862 // and fill unique nodes of 2 opposite triangles
7863 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7864 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7865 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7866 // fill unique nodes
7869 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7870 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7871 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7873 // iCur of a linked node of the opposite face (make normals co-directed):
7874 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7875 // check that correspondent corners of triangles are linked
7876 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7879 uniqueNodes[ iUnique ] = n;
7880 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7889 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7892 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7899 } // switch ( nbNodes )
7901 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7903 if ( isOk ) // the non-poly elem remains valid after sticking nodes
7905 if ( nbNodes != nbUniqueNodes ||
7906 !aMesh->ChangeElementNodes( elem, & curNodes[0], nbNodes ))
7908 elemType.Init( elem ).SetID( elem->GetID() );
7910 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7911 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7913 uniqueNodes.resize(nbUniqueNodes);
7914 SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7915 if ( sm && newElem )
7916 sm->AddElement( newElem );
7917 if ( elem != newElem )
7918 ReplaceElemInGroups( elem, newElem, aMesh );
7922 // Remove invalid regular element or invalid polygon
7923 rmElemIds.push_back( elem->GetID() );
7926 } // loop on elements
7928 // Remove bad elements, then equal nodes (order important)
7930 Remove( rmElemIds, false );
7931 Remove( rmNodeIds, true );
7937 // ========================================================
7938 // class : SortableElement
7939 // purpose : allow sorting elements basing on their nodes
7940 // ========================================================
7941 class SortableElement : public set <const SMDS_MeshElement*>
7945 SortableElement( const SMDS_MeshElement* theElem )
7948 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7949 while ( nodeIt->more() )
7950 this->insert( nodeIt->next() );
7953 const SMDS_MeshElement* Get() const
7957 mutable const SMDS_MeshElement* myElem;
7960 //=======================================================================
7961 //function : FindEqualElements
7962 //purpose : Return list of group of elements built on the same nodes.
7963 // Search among theElements or in the whole mesh if theElements is empty
7964 //=======================================================================
7966 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7967 TListOfListOfElementsID & theGroupsOfElementsID)
7969 myLastCreatedElems.Clear();
7970 myLastCreatedNodes.Clear();
7972 typedef map< SortableElement, int > TMapOfNodeSet;
7973 typedef list<int> TGroupOfElems;
7975 if ( theElements.empty() )
7976 { // get all elements in the mesh
7977 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7978 while ( eIt->more() )
7979 theElements.insert( theElements.end(), eIt->next() );
7982 vector< TGroupOfElems > arrayOfGroups;
7983 TGroupOfElems groupOfElems;
7984 TMapOfNodeSet mapOfNodeSet;
7986 TIDSortedElemSet::iterator elemIt = theElements.begin();
7987 for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7989 const SMDS_MeshElement* curElem = *elemIt;
7990 SortableElement SE(curElem);
7992 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7993 if ( !pp.second ) { // one more coincident elem
7994 TMapOfNodeSet::iterator& itSE = pp.first;
7995 int ind = (*itSE).second;
7996 arrayOfGroups[ind].push_back( curElem->GetID() );
7999 arrayOfGroups.push_back( groupOfElems );
8000 arrayOfGroups.back().push_back( curElem->GetID() );
8005 groupOfElems.clear();
8006 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8007 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8009 if ( groupIt->size() > 1 ) {
8010 //groupOfElems.sort(); -- theElements is sorted already
8011 theGroupsOfElementsID.push_back( groupOfElems );
8012 theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8017 //=======================================================================
8018 //function : MergeElements
8019 //purpose : In each given group, substitute all elements by the first one.
8020 //=======================================================================
8022 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8024 myLastCreatedElems.Clear();
8025 myLastCreatedNodes.Clear();
8027 typedef list<int> TListOfIDs;
8028 TListOfIDs rmElemIds; // IDs of elems to remove
8030 SMESHDS_Mesh* aMesh = GetMeshDS();
8032 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8033 while ( groupsIt != theGroupsOfElementsID.end() ) {
8034 TListOfIDs& aGroupOfElemID = *groupsIt;
8035 aGroupOfElemID.sort();
8036 int elemIDToKeep = aGroupOfElemID.front();
8037 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8038 aGroupOfElemID.pop_front();
8039 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8040 while ( idIt != aGroupOfElemID.end() ) {
8041 int elemIDToRemove = *idIt;
8042 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8043 // add the kept element in groups of removed one (PAL15188)
8044 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8045 rmElemIds.push_back( elemIDToRemove );
8051 Remove( rmElemIds, false );
8054 //=======================================================================
8055 //function : MergeEqualElements
8056 //purpose : Remove all but one of elements built on the same nodes.
8057 //=======================================================================
8059 void SMESH_MeshEditor::MergeEqualElements()
8061 TIDSortedElemSet aMeshElements; /* empty input ==
8062 to merge equal elements in the whole mesh */
8063 TListOfListOfElementsID aGroupsOfElementsID;
8064 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8065 MergeElements(aGroupsOfElementsID);
8068 //=======================================================================
8069 //function : findAdjacentFace
8071 //=======================================================================
8073 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8074 const SMDS_MeshNode* n2,
8075 const SMDS_MeshElement* elem)
8077 TIDSortedElemSet elemSet, avoidSet;
8079 avoidSet.insert ( elem );
8080 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8083 //=======================================================================
8084 //function : findSegment
8085 //purpose : Return a mesh segment by two nodes one of which can be medium
8086 //=======================================================================
8088 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8089 const SMDS_MeshNode* n2)
8091 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8092 while ( it->more() )
8094 const SMDS_MeshElement* seg = it->next();
8095 if ( seg->GetNodeIndex( n2 ) >= 0 )
8101 //=======================================================================
8102 //function : FindFreeBorder
8104 //=======================================================================
8106 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8108 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8109 const SMDS_MeshNode* theSecondNode,
8110 const SMDS_MeshNode* theLastNode,
8111 list< const SMDS_MeshNode* > & theNodes,
8112 list< const SMDS_MeshElement* >& theFaces)
8114 if ( !theFirstNode || !theSecondNode )
8116 // find border face between theFirstNode and theSecondNode
8117 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8121 theFaces.push_back( curElem );
8122 theNodes.push_back( theFirstNode );
8123 theNodes.push_back( theSecondNode );
8125 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8126 TIDSortedElemSet foundElems;
8127 bool needTheLast = ( theLastNode != 0 );
8129 while ( nStart != theLastNode ) {
8130 if ( nStart == theFirstNode )
8131 return !needTheLast;
8133 // find all free border faces sharing form nStart
8135 list< const SMDS_MeshElement* > curElemList;
8136 list< const SMDS_MeshNode* > nStartList;
8137 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8138 while ( invElemIt->more() ) {
8139 const SMDS_MeshElement* e = invElemIt->next();
8140 if ( e == curElem || foundElems.insert( e ).second ) {
8142 int iNode = 0, nbNodes = e->NbNodes();
8143 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8145 if ( e->IsQuadratic() ) {
8146 const SMDS_VtkFace* F =
8147 dynamic_cast<const SMDS_VtkFace*>(e);
8148 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8149 // use special nodes iterator
8150 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8151 while( anIter->more() ) {
8152 nodes[ iNode++ ] = cast2Node(anIter->next());
8156 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8157 while ( nIt->more() )
8158 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8160 nodes[ iNode ] = nodes[ 0 ];
8162 for ( iNode = 0; iNode < nbNodes; iNode++ )
8163 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8164 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8165 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8167 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8168 curElemList.push_back( e );
8172 // analyse the found
8174 int nbNewBorders = curElemList.size();
8175 if ( nbNewBorders == 0 ) {
8176 // no free border furthermore
8177 return !needTheLast;
8179 else if ( nbNewBorders == 1 ) {
8180 // one more element found
8182 nStart = nStartList.front();
8183 curElem = curElemList.front();
8184 theFaces.push_back( curElem );
8185 theNodes.push_back( nStart );
8188 // several continuations found
8189 list< const SMDS_MeshElement* >::iterator curElemIt;
8190 list< const SMDS_MeshNode* >::iterator nStartIt;
8191 // check if one of them reached the last node
8192 if ( needTheLast ) {
8193 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8194 curElemIt!= curElemList.end();
8195 curElemIt++, nStartIt++ )
8196 if ( *nStartIt == theLastNode ) {
8197 theFaces.push_back( *curElemIt );
8198 theNodes.push_back( *nStartIt );
8202 // find the best free border by the continuations
8203 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8204 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8205 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8206 curElemIt!= curElemList.end();
8207 curElemIt++, nStartIt++ )
8209 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8210 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8211 // find one more free border
8212 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8216 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8217 // choice: clear a worse one
8218 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8219 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8220 contNodes[ iWorse ].clear();
8221 contFaces[ iWorse ].clear();
8224 if ( contNodes[0].empty() && contNodes[1].empty() )
8227 // append the best free border
8228 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8229 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8230 theNodes.pop_back(); // remove nIgnore
8231 theNodes.pop_back(); // remove nStart
8232 theFaces.pop_back(); // remove curElem
8233 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8234 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8235 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8236 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8239 } // several continuations found
8240 } // while ( nStart != theLastNode )
8245 //=======================================================================
8246 //function : CheckFreeBorderNodes
8247 //purpose : Return true if the tree nodes are on a free border
8248 //=======================================================================
8250 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8251 const SMDS_MeshNode* theNode2,
8252 const SMDS_MeshNode* theNode3)
8254 list< const SMDS_MeshNode* > nodes;
8255 list< const SMDS_MeshElement* > faces;
8256 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8259 //=======================================================================
8260 //function : SewFreeBorder
8262 //warning : for border-to-side sewing theSideSecondNode is considered as
8263 // the last side node and theSideThirdNode is not used
8264 //=======================================================================
8266 SMESH_MeshEditor::Sew_Error
8267 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8268 const SMDS_MeshNode* theBordSecondNode,
8269 const SMDS_MeshNode* theBordLastNode,
8270 const SMDS_MeshNode* theSideFirstNode,
8271 const SMDS_MeshNode* theSideSecondNode,
8272 const SMDS_MeshNode* theSideThirdNode,
8273 const bool theSideIsFreeBorder,
8274 const bool toCreatePolygons,
8275 const bool toCreatePolyedrs)
8277 myLastCreatedElems.Clear();
8278 myLastCreatedNodes.Clear();
8280 MESSAGE("::SewFreeBorder()");
8281 Sew_Error aResult = SEW_OK;
8283 // ====================================
8284 // find side nodes and elements
8285 // ====================================
8287 list< const SMDS_MeshNode* > nSide[ 2 ];
8288 list< const SMDS_MeshElement* > eSide[ 2 ];
8289 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8290 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8294 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8295 nSide[0], eSide[0])) {
8296 MESSAGE(" Free Border 1 not found " );
8297 aResult = SEW_BORDER1_NOT_FOUND;
8299 if (theSideIsFreeBorder) {
8302 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8303 nSide[1], eSide[1])) {
8304 MESSAGE(" Free Border 2 not found " );
8305 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8308 if ( aResult != SEW_OK )
8311 if (!theSideIsFreeBorder) {
8315 // -------------------------------------------------------------------------
8317 // 1. If nodes to merge are not coincident, move nodes of the free border
8318 // from the coord sys defined by the direction from the first to last
8319 // nodes of the border to the correspondent sys of the side 2
8320 // 2. On the side 2, find the links most co-directed with the correspondent
8321 // links of the free border
8322 // -------------------------------------------------------------------------
8324 // 1. Since sewing may break if there are volumes to split on the side 2,
8325 // we wont move nodes but just compute new coordinates for them
8326 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8327 TNodeXYZMap nBordXYZ;
8328 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8329 list< const SMDS_MeshNode* >::iterator nBordIt;
8331 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8332 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8333 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8334 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8335 double tol2 = 1.e-8;
8336 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8337 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8338 // Need node movement.
8340 // find X and Z axes to create trsf
8341 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8343 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8345 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8348 gp_Ax3 toBordAx( Pb1, Zb, X );
8349 gp_Ax3 fromSideAx( Ps1, Zs, X );
8350 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8352 gp_Trsf toBordSys, fromSide2Sys;
8353 toBordSys.SetTransformation( toBordAx );
8354 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8355 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8358 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8359 const SMDS_MeshNode* n = *nBordIt;
8360 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8361 toBordSys.Transforms( xyz );
8362 fromSide2Sys.Transforms( xyz );
8363 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8367 // just insert nodes XYZ in the nBordXYZ map
8368 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8369 const SMDS_MeshNode* n = *nBordIt;
8370 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8374 // 2. On the side 2, find the links most co-directed with the correspondent
8375 // links of the free border
8377 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8378 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8379 sideNodes.push_back( theSideFirstNode );
8381 bool hasVolumes = false;
8382 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8383 set<long> foundSideLinkIDs, checkedLinkIDs;
8384 SMDS_VolumeTool volume;
8385 //const SMDS_MeshNode* faceNodes[ 4 ];
8387 const SMDS_MeshNode* sideNode;
8388 const SMDS_MeshElement* sideElem;
8389 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8390 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8391 nBordIt = bordNodes.begin();
8393 // border node position and border link direction to compare with
8394 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8395 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8396 // choose next side node by link direction or by closeness to
8397 // the current border node:
8398 bool searchByDir = ( *nBordIt != theBordLastNode );
8400 // find the next node on the Side 2
8402 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8404 checkedLinkIDs.clear();
8405 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8407 // loop on inverse elements of current node (prevSideNode) on the Side 2
8408 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8409 while ( invElemIt->more() )
8411 const SMDS_MeshElement* elem = invElemIt->next();
8412 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8413 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8414 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8415 bool isVolume = volume.Set( elem );
8416 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8417 if ( isVolume ) // --volume
8419 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8420 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8421 if(elem->IsQuadratic()) {
8422 const SMDS_VtkFace* F =
8423 dynamic_cast<const SMDS_VtkFace*>(elem);
8424 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8425 // use special nodes iterator
8426 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8427 while( anIter->more() ) {
8428 nodes[ iNode ] = cast2Node(anIter->next());
8429 if ( nodes[ iNode++ ] == prevSideNode )
8430 iPrevNode = iNode - 1;
8434 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8435 while ( nIt->more() ) {
8436 nodes[ iNode ] = cast2Node( nIt->next() );
8437 if ( nodes[ iNode++ ] == prevSideNode )
8438 iPrevNode = iNode - 1;
8441 // there are 2 links to check
8446 // loop on links, to be precise, on the second node of links
8447 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8448 const SMDS_MeshNode* n = nodes[ iNode ];
8450 if ( !volume.IsLinked( n, prevSideNode ))
8454 if ( iNode ) // a node before prevSideNode
8455 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8456 else // a node after prevSideNode
8457 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8459 // check if this link was already used
8460 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8461 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8462 if (!isJustChecked &&
8463 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8465 // test a link geometrically
8466 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8467 bool linkIsBetter = false;
8468 double dot = 0.0, dist = 0.0;
8469 if ( searchByDir ) { // choose most co-directed link
8470 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8471 linkIsBetter = ( dot > maxDot );
8473 else { // choose link with the node closest to bordPos
8474 dist = ( nextXYZ - bordPos ).SquareModulus();
8475 linkIsBetter = ( dist < minDist );
8477 if ( linkIsBetter ) {
8486 } // loop on inverse elements of prevSideNode
8489 MESSAGE(" Cant find path by links of the Side 2 ");
8490 return SEW_BAD_SIDE_NODES;
8492 sideNodes.push_back( sideNode );
8493 sideElems.push_back( sideElem );
8494 foundSideLinkIDs.insert ( linkID );
8495 prevSideNode = sideNode;
8497 if ( *nBordIt == theBordLastNode )
8498 searchByDir = false;
8500 // find the next border link to compare with
8501 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8502 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8503 // move to next border node if sideNode is before forward border node (bordPos)
8504 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8505 prevBordNode = *nBordIt;
8507 bordPos = nBordXYZ[ *nBordIt ];
8508 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8509 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8513 while ( sideNode != theSideSecondNode );
8515 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8516 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8517 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8519 } // end nodes search on the side 2
8521 // ============================
8522 // sew the border to the side 2
8523 // ============================
8525 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8526 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8528 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8529 if ( toMergeConformal && toCreatePolygons )
8531 // do not merge quadrangles if polygons are OK (IPAL0052824)
8532 eIt[0] = eSide[0].begin();
8533 eIt[1] = eSide[1].begin();
8534 bool allQuads[2] = { true, true };
8535 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8536 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8537 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8539 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8542 TListOfListOfNodes nodeGroupsToMerge;
8543 if (( toMergeConformal ) ||
8544 ( theSideIsFreeBorder && !theSideThirdNode )) {
8546 // all nodes are to be merged
8548 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8549 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8550 nIt[0]++, nIt[1]++ )
8552 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8553 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8554 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8559 // insert new nodes into the border and the side to get equal nb of segments
8561 // get normalized parameters of nodes on the borders
8562 vector< double > param[ 2 ];
8563 param[0].resize( maxNbNodes );
8564 param[1].resize( maxNbNodes );
8566 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8567 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8568 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8569 const SMDS_MeshNode* nPrev = *nIt;
8570 double bordLength = 0;
8571 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8572 const SMDS_MeshNode* nCur = *nIt;
8573 gp_XYZ segment (nCur->X() - nPrev->X(),
8574 nCur->Y() - nPrev->Y(),
8575 nCur->Z() - nPrev->Z());
8576 double segmentLen = segment.Modulus();
8577 bordLength += segmentLen;
8578 param[ iBord ][ iNode ] = bordLength;
8581 // normalize within [0,1]
8582 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8583 param[ iBord ][ iNode ] /= bordLength;
8587 // loop on border segments
8588 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8589 int i[ 2 ] = { 0, 0 };
8590 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8591 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8593 TElemOfNodeListMap insertMap;
8594 TElemOfNodeListMap::iterator insertMapIt;
8596 // key: elem to insert nodes into
8597 // value: 2 nodes to insert between + nodes to be inserted
8599 bool next[ 2 ] = { false, false };
8601 // find min adjacent segment length after sewing
8602 double nextParam = 10., prevParam = 0;
8603 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8604 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8605 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8606 if ( i[ iBord ] > 0 )
8607 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8609 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8610 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8611 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8613 // choose to insert or to merge nodes
8614 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8615 if ( Abs( du ) <= minSegLen * 0.2 ) {
8618 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8619 const SMDS_MeshNode* n0 = *nIt[0];
8620 const SMDS_MeshNode* n1 = *nIt[1];
8621 nodeGroupsToMerge.back().push_back( n1 );
8622 nodeGroupsToMerge.back().push_back( n0 );
8623 // position of node of the border changes due to merge
8624 param[ 0 ][ i[0] ] += du;
8625 // move n1 for the sake of elem shape evaluation during insertion.
8626 // n1 will be removed by MergeNodes() anyway
8627 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8628 next[0] = next[1] = true;
8633 int intoBord = ( du < 0 ) ? 0 : 1;
8634 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8635 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8636 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8637 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8638 if ( intoBord == 1 ) {
8639 // move node of the border to be on a link of elem of the side
8640 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8641 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8642 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8643 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8644 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8646 insertMapIt = insertMap.find( elem );
8647 bool notFound = ( insertMapIt == insertMap.end() );
8648 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8650 // insert into another link of the same element:
8651 // 1. perform insertion into the other link of the elem
8652 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8653 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8654 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8655 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8656 // 2. perform insertion into the link of adjacent faces
8657 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8658 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8660 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8661 InsertNodesIntoLink( seg, n12, n22, nodeList );
8663 if (toCreatePolyedrs) {
8664 // perform insertion into the links of adjacent volumes
8665 UpdateVolumes(n12, n22, nodeList);
8667 // 3. find an element appeared on n1 and n2 after the insertion
8668 insertMap.erase( elem );
8669 elem = findAdjacentFace( n1, n2, 0 );
8671 if ( notFound || otherLink ) {
8672 // add element and nodes of the side into the insertMap
8673 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8674 (*insertMapIt).second.push_back( n1 );
8675 (*insertMapIt).second.push_back( n2 );
8677 // add node to be inserted into elem
8678 (*insertMapIt).second.push_back( nIns );
8679 next[ 1 - intoBord ] = true;
8682 // go to the next segment
8683 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8684 if ( next[ iBord ] ) {
8685 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8687 nPrev[ iBord ] = *nIt[ iBord ];
8688 nIt[ iBord ]++; i[ iBord ]++;
8692 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8694 // perform insertion of nodes into elements
8696 for (insertMapIt = insertMap.begin();
8697 insertMapIt != insertMap.end();
8700 const SMDS_MeshElement* elem = (*insertMapIt).first;
8701 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8702 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8703 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8705 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8707 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8708 InsertNodesIntoLink( seg, n1, n2, nodeList );
8711 if ( !theSideIsFreeBorder ) {
8712 // look for and insert nodes into the faces adjacent to elem
8713 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8714 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8717 if (toCreatePolyedrs) {
8718 // perform insertion into the links of adjacent volumes
8719 UpdateVolumes(n1, n2, nodeList);
8722 } // end: insert new nodes
8724 MergeNodes ( nodeGroupsToMerge );
8727 // Remove coincident segments
8730 TIDSortedElemSet segments;
8731 SMESH_SequenceOfElemPtr newFaces;
8732 for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8734 if ( !myLastCreatedElems(i) ) continue;
8735 if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8736 segments.insert( segments.end(), myLastCreatedElems(i) );
8738 newFaces.Append( myLastCreatedElems(i) );
8740 // get segments adjacent to merged nodes
8741 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8742 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8744 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8745 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8746 while ( segIt->more() )
8747 segments.insert( segIt->next() );
8751 TListOfListOfElementsID equalGroups;
8752 if ( !segments.empty() )
8753 FindEqualElements( segments, equalGroups );
8754 if ( !equalGroups.empty() )
8756 // remove from segments those that will be removed
8757 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8758 for ( ; itGroups != equalGroups.end(); ++itGroups )
8760 list< int >& group = *itGroups;
8761 list< int >::iterator id = group.begin();
8762 for ( ++id; id != group.end(); ++id )
8763 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8764 segments.erase( seg );
8766 // remove equal segments
8767 MergeElements( equalGroups );
8769 // restore myLastCreatedElems
8770 myLastCreatedElems = newFaces;
8771 TIDSortedElemSet::iterator seg = segments.begin();
8772 for ( ; seg != segments.end(); ++seg )
8773 myLastCreatedElems.Append( *seg );
8779 //=======================================================================
8780 //function : InsertNodesIntoLink
8781 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8782 // and theBetweenNode2 and split theElement
8783 //=======================================================================
8785 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8786 const SMDS_MeshNode* theBetweenNode1,
8787 const SMDS_MeshNode* theBetweenNode2,
8788 list<const SMDS_MeshNode*>& theNodesToInsert,
8789 const bool toCreatePoly)
8791 if ( !theElement ) return;
8793 SMESHDS_Mesh *aMesh = GetMeshDS();
8794 vector<const SMDS_MeshElement*> newElems;
8796 if ( theElement->GetType() == SMDSAbs_Edge )
8798 theNodesToInsert.push_front( theBetweenNode1 );
8799 theNodesToInsert.push_back ( theBetweenNode2 );
8800 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8801 const SMDS_MeshNode* n1 = *n;
8802 for ( ++n; n != theNodesToInsert.end(); ++n )
8804 const SMDS_MeshNode* n2 = *n;
8805 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8806 AddToSameGroups( seg, theElement, aMesh );
8808 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8811 theNodesToInsert.pop_front();
8812 theNodesToInsert.pop_back();
8814 if ( theElement->IsQuadratic() ) // add a not split part
8816 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8817 theElement->end_nodes() );
8818 int iOther = 0, nbN = nodes.size();
8819 for ( ; iOther < nbN; ++iOther )
8820 if ( nodes[iOther] != theBetweenNode1 &&
8821 nodes[iOther] != theBetweenNode2 )
8825 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8826 AddToSameGroups( seg, theElement, aMesh );
8828 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8830 else if ( iOther == 2 )
8832 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8833 AddToSameGroups( seg, theElement, aMesh );
8835 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8838 // treat new elements
8839 for ( size_t i = 0; i < newElems.size(); ++i )
8842 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8843 myLastCreatedElems.Append( newElems[i] );
8845 ReplaceElemInGroups( theElement, newElems, aMesh );
8846 aMesh->RemoveElement( theElement );
8849 } // if ( theElement->GetType() == SMDSAbs_Edge )
8851 const SMDS_MeshElement* theFace = theElement;
8852 if ( theFace->GetType() != SMDSAbs_Face ) return;
8854 // find indices of 2 link nodes and of the rest nodes
8855 int iNode = 0, il1, il2, i3, i4;
8856 il1 = il2 = i3 = i4 = -1;
8857 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8859 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8860 while ( nodeIt->more() ) {
8861 const SMDS_MeshNode* n = nodeIt->next();
8862 if ( n == theBetweenNode1 )
8864 else if ( n == theBetweenNode2 )
8870 nodes[ iNode++ ] = n;
8872 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8875 // arrange link nodes to go one after another regarding the face orientation
8876 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8877 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8882 aNodesToInsert.reverse();
8884 // check that not link nodes of a quadrangles are in good order
8885 int nbFaceNodes = theFace->NbNodes();
8886 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8892 if (toCreatePoly || theFace->IsPoly()) {
8895 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8897 // add nodes of face up to first node of link
8900 if ( theFace->IsQuadratic() ) {
8901 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8902 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8903 // use special nodes iterator
8904 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8905 while( anIter->more() && !isFLN ) {
8906 const SMDS_MeshNode* n = cast2Node(anIter->next());
8907 poly_nodes[iNode++] = n;
8908 if (n == nodes[il1]) {
8912 // add nodes to insert
8913 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8914 for (; nIt != aNodesToInsert.end(); nIt++) {
8915 poly_nodes[iNode++] = *nIt;
8917 // add nodes of face starting from last node of link
8918 while ( anIter->more() ) {
8919 poly_nodes[iNode++] = cast2Node(anIter->next());
8923 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8924 while ( nodeIt->more() && !isFLN ) {
8925 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8926 poly_nodes[iNode++] = n;
8927 if (n == nodes[il1]) {
8931 // add nodes to insert
8932 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8933 for (; nIt != aNodesToInsert.end(); nIt++) {
8934 poly_nodes[iNode++] = *nIt;
8936 // add nodes of face starting from last node of link
8937 while ( nodeIt->more() ) {
8938 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8939 poly_nodes[iNode++] = n;
8944 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8947 else if ( !theFace->IsQuadratic() )
8949 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8950 int nbLinkNodes = 2 + aNodesToInsert.size();
8951 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8952 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8953 linkNodes[ 0 ] = nodes[ il1 ];
8954 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8955 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8956 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8957 linkNodes[ iNode++ ] = *nIt;
8959 // decide how to split a quadrangle: compare possible variants
8960 // and choose which of splits to be a quadrangle
8961 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8962 if ( nbFaceNodes == 3 ) {
8963 iBestQuad = nbSplits;
8966 else if ( nbFaceNodes == 4 ) {
8967 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8968 double aBestRate = DBL_MAX;
8969 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8971 double aBadRate = 0;
8972 // evaluate elements quality
8973 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8974 if ( iSplit == iQuad ) {
8975 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8979 aBadRate += getBadRate( &quad, aCrit );
8982 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8984 nodes[ iSplit < iQuad ? i4 : i3 ]);
8985 aBadRate += getBadRate( &tria, aCrit );
8989 if ( aBadRate < aBestRate ) {
8991 aBestRate = aBadRate;
8996 // create new elements
8998 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
9000 if ( iSplit == iBestQuad )
9001 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9006 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9008 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
9011 const SMDS_MeshNode* newNodes[ 4 ];
9012 newNodes[ 0 ] = linkNodes[ i1 ];
9013 newNodes[ 1 ] = linkNodes[ i2 ];
9014 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9015 newNodes[ 3 ] = nodes[ i4 ];
9016 if (iSplit == iBestQuad)
9017 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
9019 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
9021 } // end if(!theFace->IsQuadratic())
9023 else { // theFace is quadratic
9024 // we have to split theFace on simple triangles and one simple quadrangle
9026 int nbshift = tmp*2;
9027 // shift nodes in nodes[] by nbshift
9029 for(i=0; i<nbshift; i++) {
9030 const SMDS_MeshNode* n = nodes[0];
9031 for(j=0; j<nbFaceNodes-1; j++) {
9032 nodes[j] = nodes[j+1];
9034 nodes[nbFaceNodes-1] = n;
9036 il1 = il1 - nbshift;
9037 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9038 // n0 n1 n2 n0 n1 n2
9039 // +-----+-----+ +-----+-----+
9048 // create new elements
9050 if ( nbFaceNodes == 6 ) { // quadratic triangle
9051 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9052 if ( theFace->IsMediumNode(nodes[il1]) ) {
9053 // create quadrangle
9054 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9060 // create quadrangle
9061 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9067 else { // nbFaceNodes==8 - quadratic quadrangle
9068 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9069 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9070 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9071 if ( theFace->IsMediumNode( nodes[ il1 ])) {
9072 // create quadrangle
9073 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9079 // create quadrangle
9080 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9086 // create needed triangles using n1,n2,n3 and inserted nodes
9087 int nbn = 2 + aNodesToInsert.size();
9088 vector<const SMDS_MeshNode*> aNodes(nbn);
9089 aNodes[0 ] = nodes[n1];
9090 aNodes[nbn-1] = nodes[n2];
9091 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9092 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9093 aNodes[iNode++] = *nIt;
9095 for ( i = 1; i < nbn; i++ )
9096 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9099 // remove the old face
9100 for ( size_t i = 0; i < newElems.size(); ++i )
9103 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9104 myLastCreatedElems.Append( newElems[i] );
9106 ReplaceElemInGroups( theFace, newElems, aMesh );
9107 aMesh->RemoveElement(theFace);
9109 } // InsertNodesIntoLink()
9111 //=======================================================================
9112 //function : UpdateVolumes
9114 //=======================================================================
9116 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9117 const SMDS_MeshNode* theBetweenNode2,
9118 list<const SMDS_MeshNode*>& theNodesToInsert)
9120 myLastCreatedElems.Clear();
9121 myLastCreatedNodes.Clear();
9123 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9124 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9125 const SMDS_MeshElement* elem = invElemIt->next();
9127 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9128 SMDS_VolumeTool aVolume (elem);
9129 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9132 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9133 int iface, nbFaces = aVolume.NbFaces();
9134 vector<const SMDS_MeshNode *> poly_nodes;
9135 vector<int> quantities (nbFaces);
9137 for (iface = 0; iface < nbFaces; iface++) {
9138 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9139 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9140 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9142 for (int inode = 0; inode < nbFaceNodes; inode++) {
9143 poly_nodes.push_back(faceNodes[inode]);
9145 if (nbInserted == 0) {
9146 if (faceNodes[inode] == theBetweenNode1) {
9147 if (faceNodes[inode + 1] == theBetweenNode2) {
9148 nbInserted = theNodesToInsert.size();
9150 // add nodes to insert
9151 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9152 for (; nIt != theNodesToInsert.end(); nIt++) {
9153 poly_nodes.push_back(*nIt);
9157 else if (faceNodes[inode] == theBetweenNode2) {
9158 if (faceNodes[inode + 1] == theBetweenNode1) {
9159 nbInserted = theNodesToInsert.size();
9161 // add nodes to insert in reversed order
9162 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9164 for (; nIt != theNodesToInsert.begin(); nIt--) {
9165 poly_nodes.push_back(*nIt);
9167 poly_nodes.push_back(*nIt);
9174 quantities[iface] = nbFaceNodes + nbInserted;
9177 // Replace the volume
9178 SMESHDS_Mesh *aMesh = GetMeshDS();
9180 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9182 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9183 myLastCreatedElems.Append( newElem );
9184 ReplaceElemInGroups( elem, newElem, aMesh );
9186 aMesh->RemoveElement( elem );
9192 //================================================================================
9194 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9196 //================================================================================
9198 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9199 vector<const SMDS_MeshNode *> & nodes,
9200 vector<int> & nbNodeInFaces )
9203 nbNodeInFaces.clear();
9204 SMDS_VolumeTool vTool ( elem );
9205 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9207 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9208 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9209 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9214 //=======================================================================
9216 * \brief Convert elements contained in a sub-mesh to quadratic
9217 * \return int - nb of checked elements
9219 //=======================================================================
9221 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9222 SMESH_MesherHelper& theHelper,
9223 const bool theForce3d)
9226 if( !theSm ) return nbElem;
9228 vector<int> nbNodeInFaces;
9229 vector<const SMDS_MeshNode *> nodes;
9230 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9231 while(ElemItr->more())
9234 const SMDS_MeshElement* elem = ElemItr->next();
9235 if( !elem ) continue;
9237 // analyse a necessity of conversion
9238 const SMDSAbs_ElementType aType = elem->GetType();
9239 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9241 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9242 bool hasCentralNodes = false;
9243 if ( elem->IsQuadratic() )
9246 switch ( aGeomType ) {
9247 case SMDSEntity_Quad_Triangle:
9248 case SMDSEntity_Quad_Quadrangle:
9249 case SMDSEntity_Quad_Hexa:
9250 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9252 case SMDSEntity_BiQuad_Triangle:
9253 case SMDSEntity_BiQuad_Quadrangle:
9254 case SMDSEntity_TriQuad_Hexa:
9255 alreadyOK = theHelper.GetIsBiQuadratic();
9256 hasCentralNodes = true;
9261 // take into account already present modium nodes
9263 case SMDSAbs_Volume:
9264 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9266 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9268 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9274 // get elem data needed to re-create it
9276 const int id = elem->GetID();
9277 const int nbNodes = elem->NbCornerNodes();
9278 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9279 if ( aGeomType == SMDSEntity_Polyhedra )
9280 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9281 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9282 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9284 // remove a linear element
9285 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9287 // remove central nodes of biquadratic elements (biquad->quad convertion)
9288 if ( hasCentralNodes )
9289 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9290 if ( nodes[i]->NbInverseElements() == 0 )
9291 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9293 const SMDS_MeshElement* NewElem = 0;
9299 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9307 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9310 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9313 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9317 case SMDSAbs_Volume :
9321 case SMDSEntity_Tetra:
9322 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9324 case SMDSEntity_Pyramid:
9325 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9327 case SMDSEntity_Penta:
9328 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9330 case SMDSEntity_Hexa:
9331 case SMDSEntity_Quad_Hexa:
9332 case SMDSEntity_TriQuad_Hexa:
9333 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9334 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9336 case SMDSEntity_Hexagonal_Prism:
9338 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9345 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9346 if( NewElem && NewElem->getshapeId() < 1 )
9347 theSm->AddElement( NewElem );
9351 //=======================================================================
9352 //function : ConvertToQuadratic
9354 //=======================================================================
9356 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9358 SMESHDS_Mesh* meshDS = GetMeshDS();
9360 SMESH_MesherHelper aHelper(*myMesh);
9362 aHelper.SetIsQuadratic( true );
9363 aHelper.SetIsBiQuadratic( theToBiQuad );
9364 aHelper.SetElementsOnShape(true);
9365 aHelper.ToFixNodeParameters( true );
9367 // convert elements assigned to sub-meshes
9368 int nbCheckedElems = 0;
9369 if ( myMesh->HasShapeToMesh() )
9371 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9373 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9374 while ( smIt->more() ) {
9375 SMESH_subMesh* sm = smIt->next();
9376 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9377 aHelper.SetSubShape( sm->GetSubShape() );
9378 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9384 // convert elements NOT assigned to sub-meshes
9385 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9386 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9388 aHelper.SetElementsOnShape(false);
9389 SMESHDS_SubMesh *smDS = 0;
9392 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9393 while( aEdgeItr->more() )
9395 const SMDS_MeshEdge* edge = aEdgeItr->next();
9396 if ( !edge->IsQuadratic() )
9398 int id = edge->GetID();
9399 const SMDS_MeshNode* n1 = edge->GetNode(0);
9400 const SMDS_MeshNode* n2 = edge->GetNode(1);
9402 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9404 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9405 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9409 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9414 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9415 while( aFaceItr->more() )
9417 const SMDS_MeshFace* face = aFaceItr->next();
9418 if ( !face ) continue;
9420 const SMDSAbs_EntityType type = face->GetEntityType();
9424 case SMDSEntity_Quad_Triangle:
9425 case SMDSEntity_Quad_Quadrangle:
9426 alreadyOK = !theToBiQuad;
9427 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9429 case SMDSEntity_BiQuad_Triangle:
9430 case SMDSEntity_BiQuad_Quadrangle:
9431 alreadyOK = theToBiQuad;
9432 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9434 default: alreadyOK = false;
9439 const int id = face->GetID();
9440 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9442 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9444 SMDS_MeshFace * NewFace = 0;
9447 case SMDSEntity_Triangle:
9448 case SMDSEntity_Quad_Triangle:
9449 case SMDSEntity_BiQuad_Triangle:
9450 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9451 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9452 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9455 case SMDSEntity_Quadrangle:
9456 case SMDSEntity_Quad_Quadrangle:
9457 case SMDSEntity_BiQuad_Quadrangle:
9458 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9459 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9460 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9464 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9466 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9470 vector<int> nbNodeInFaces;
9471 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9472 while(aVolumeItr->more())
9474 const SMDS_MeshVolume* volume = aVolumeItr->next();
9475 if ( !volume ) continue;
9477 const SMDSAbs_EntityType type = volume->GetEntityType();
9478 if ( volume->IsQuadratic() )
9483 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9484 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9485 default: alreadyOK = true;
9489 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9493 const int id = volume->GetID();
9494 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9495 if ( type == SMDSEntity_Polyhedra )
9496 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9497 else if ( type == SMDSEntity_Hexagonal_Prism )
9498 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9500 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9502 SMDS_MeshVolume * NewVolume = 0;
9505 case SMDSEntity_Tetra:
9506 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9508 case SMDSEntity_Hexa:
9509 case SMDSEntity_Quad_Hexa:
9510 case SMDSEntity_TriQuad_Hexa:
9511 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9512 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9513 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9514 if ( nodes[i]->NbInverseElements() == 0 )
9515 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9517 case SMDSEntity_Pyramid:
9518 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9519 nodes[3], nodes[4], id, theForce3d);
9521 case SMDSEntity_Penta:
9522 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9523 nodes[3], nodes[4], nodes[5], id, theForce3d);
9525 case SMDSEntity_Hexagonal_Prism:
9527 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9529 ReplaceElemInGroups(volume, NewVolume, meshDS);
9534 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9535 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9536 // aHelper.FixQuadraticElements(myError);
9537 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9541 //================================================================================
9543 * \brief Makes given elements quadratic
9544 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9545 * \param theElements - elements to make quadratic
9547 //================================================================================
9549 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9550 TIDSortedElemSet& theElements,
9551 const bool theToBiQuad)
9553 if ( theElements.empty() ) return;
9555 // we believe that all theElements are of the same type
9556 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9558 // get all nodes shared by theElements
9559 TIDSortedNodeSet allNodes;
9560 TIDSortedElemSet::iterator eIt = theElements.begin();
9561 for ( ; eIt != theElements.end(); ++eIt )
9562 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9564 // complete theElements with elements of lower dim whose all nodes are in allNodes
9566 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9567 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9568 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9569 for ( ; nIt != allNodes.end(); ++nIt )
9571 const SMDS_MeshNode* n = *nIt;
9572 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9573 while ( invIt->more() )
9575 const SMDS_MeshElement* e = invIt->next();
9576 const SMDSAbs_ElementType type = e->GetType();
9577 if ( e->IsQuadratic() )
9579 quadAdjacentElems[ type ].insert( e );
9582 switch ( e->GetEntityType() ) {
9583 case SMDSEntity_Quad_Triangle:
9584 case SMDSEntity_Quad_Quadrangle:
9585 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9586 case SMDSEntity_BiQuad_Triangle:
9587 case SMDSEntity_BiQuad_Quadrangle:
9588 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9589 default: alreadyOK = true;
9594 if ( type >= elemType )
9595 continue; // same type or more complex linear element
9597 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9598 continue; // e is already checked
9602 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9603 while ( nodeIt->more() && allIn )
9604 allIn = allNodes.count( nodeIt->next() );
9606 theElements.insert(e );
9610 SMESH_MesherHelper helper(*myMesh);
9611 helper.SetIsQuadratic( true );
9612 helper.SetIsBiQuadratic( theToBiQuad );
9614 // add links of quadratic adjacent elements to the helper
9616 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9617 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9618 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9620 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9622 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9623 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9624 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9626 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9628 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9629 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9630 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9632 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9635 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9637 SMESHDS_Mesh* meshDS = GetMeshDS();
9638 SMESHDS_SubMesh* smDS = 0;
9639 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9641 const SMDS_MeshElement* elem = *eIt;
9644 int nbCentralNodes = 0;
9645 switch ( elem->GetEntityType() ) {
9646 // linear convertible
9647 case SMDSEntity_Edge:
9648 case SMDSEntity_Triangle:
9649 case SMDSEntity_Quadrangle:
9650 case SMDSEntity_Tetra:
9651 case SMDSEntity_Pyramid:
9652 case SMDSEntity_Hexa:
9653 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9654 // quadratic that can become bi-quadratic
9655 case SMDSEntity_Quad_Triangle:
9656 case SMDSEntity_Quad_Quadrangle:
9657 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9659 case SMDSEntity_BiQuad_Triangle:
9660 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9661 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9663 default: alreadyOK = true;
9665 if ( alreadyOK ) continue;
9667 const SMDSAbs_ElementType type = elem->GetType();
9668 const int id = elem->GetID();
9669 const int nbNodes = elem->NbCornerNodes();
9670 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9672 helper.SetSubShape( elem->getshapeId() );
9674 if ( !smDS || !smDS->Contains( elem ))
9675 smDS = meshDS->MeshElements( elem->getshapeId() );
9676 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9678 SMDS_MeshElement * newElem = 0;
9681 case 4: // cases for most frequently used element types go first (for optimization)
9682 if ( type == SMDSAbs_Volume )
9683 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9685 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9688 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9689 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9692 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9695 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9698 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9699 nodes[4], id, theForce3d);
9702 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9703 nodes[4], nodes[5], id, theForce3d);
9707 ReplaceElemInGroups( elem, newElem, meshDS);
9708 if( newElem && smDS )
9709 smDS->AddElement( newElem );
9711 // remove central nodes
9712 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9713 if ( nodes[i]->NbInverseElements() == 0 )
9714 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9716 } // loop on theElements
9719 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9720 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9721 // helper.FixQuadraticElements( myError );
9722 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9726 //=======================================================================
9728 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9729 * \return int - nb of checked elements
9731 //=======================================================================
9733 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9734 SMDS_ElemIteratorPtr theItr,
9735 const int theShapeID)
9738 SMESHDS_Mesh* meshDS = GetMeshDS();
9739 ElemFeatures elemType;
9740 vector<const SMDS_MeshNode *> nodes;
9742 while( theItr->more() )
9744 const SMDS_MeshElement* elem = theItr->next();
9746 if( elem && elem->IsQuadratic())
9749 int nbCornerNodes = elem->NbCornerNodes();
9750 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9752 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9754 //remove a quadratic element
9755 if ( !theSm || !theSm->Contains( elem ))
9756 theSm = meshDS->MeshElements( elem->getshapeId() );
9757 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9759 // remove medium nodes
9760 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9761 if ( nodes[i]->NbInverseElements() == 0 )
9762 meshDS->RemoveFreeNode( nodes[i], theSm );
9764 // add a linear element
9765 nodes.resize( nbCornerNodes );
9766 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9767 ReplaceElemInGroups(elem, newElem, meshDS);
9768 if( theSm && newElem )
9769 theSm->AddElement( newElem );
9775 //=======================================================================
9776 //function : ConvertFromQuadratic
9778 //=======================================================================
9780 bool SMESH_MeshEditor::ConvertFromQuadratic()
9782 int nbCheckedElems = 0;
9783 if ( myMesh->HasShapeToMesh() )
9785 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9787 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9788 while ( smIt->more() ) {
9789 SMESH_subMesh* sm = smIt->next();
9790 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9791 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9797 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9798 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9800 SMESHDS_SubMesh *aSM = 0;
9801 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9809 //================================================================================
9811 * \brief Return true if all medium nodes of the element are in the node set
9813 //================================================================================
9815 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9817 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9818 if ( !nodeSet.count( elem->GetNode(i) ))
9824 //================================================================================
9826 * \brief Makes given elements linear
9828 //================================================================================
9830 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9832 if ( theElements.empty() ) return;
9834 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9835 set<int> mediumNodeIDs;
9836 TIDSortedElemSet::iterator eIt = theElements.begin();
9837 for ( ; eIt != theElements.end(); ++eIt )
9839 const SMDS_MeshElement* e = *eIt;
9840 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9841 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9844 // replace given elements by linear ones
9845 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9846 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9848 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9849 // except those elements sharing medium nodes of quadratic element whose medium nodes
9850 // are not all in mediumNodeIDs
9852 // get remaining medium nodes
9853 TIDSortedNodeSet mediumNodes;
9854 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9855 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9856 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9857 mediumNodes.insert( mediumNodes.end(), n );
9859 // find more quadratic elements to convert
9860 TIDSortedElemSet moreElemsToConvert;
9861 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9862 for ( ; nIt != mediumNodes.end(); ++nIt )
9864 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9865 while ( invIt->more() )
9867 const SMDS_MeshElement* e = invIt->next();
9868 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9870 // find a more complex element including e and
9871 // whose medium nodes are not in mediumNodes
9872 bool complexFound = false;
9873 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9875 SMDS_ElemIteratorPtr invIt2 =
9876 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9877 while ( invIt2->more() )
9879 const SMDS_MeshElement* eComplex = invIt2->next();
9880 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9882 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9883 if ( nbCommonNodes == e->NbNodes())
9885 complexFound = true;
9886 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9892 if ( !complexFound )
9893 moreElemsToConvert.insert( e );
9897 elemIt = elemSetIterator( moreElemsToConvert );
9898 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9901 //=======================================================================
9902 //function : SewSideElements
9904 //=======================================================================
9906 SMESH_MeshEditor::Sew_Error
9907 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9908 TIDSortedElemSet& theSide2,
9909 const SMDS_MeshNode* theFirstNode1,
9910 const SMDS_MeshNode* theFirstNode2,
9911 const SMDS_MeshNode* theSecondNode1,
9912 const SMDS_MeshNode* theSecondNode2)
9914 myLastCreatedElems.Clear();
9915 myLastCreatedNodes.Clear();
9917 MESSAGE ("::::SewSideElements()");
9918 if ( theSide1.size() != theSide2.size() )
9919 return SEW_DIFF_NB_OF_ELEMENTS;
9921 Sew_Error aResult = SEW_OK;
9923 // 1. Build set of faces representing each side
9924 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9925 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9927 // =======================================================================
9928 // 1. Build set of faces representing each side:
9929 // =======================================================================
9930 // a. build set of nodes belonging to faces
9931 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9932 // c. create temporary faces representing side of volumes if correspondent
9933 // face does not exist
9935 SMESHDS_Mesh* aMesh = GetMeshDS();
9936 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9937 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9938 TIDSortedElemSet faceSet1, faceSet2;
9939 set<const SMDS_MeshElement*> volSet1, volSet2;
9940 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9941 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9942 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9943 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9944 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9945 int iSide, iFace, iNode;
9947 list<const SMDS_MeshElement* > tempFaceList;
9948 for ( iSide = 0; iSide < 2; iSide++ ) {
9949 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9950 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9951 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9952 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9953 set<const SMDS_MeshElement*>::iterator vIt;
9954 TIDSortedElemSet::iterator eIt;
9955 set<const SMDS_MeshNode*>::iterator nIt;
9957 // check that given nodes belong to given elements
9958 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9959 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9960 int firstIndex = -1, secondIndex = -1;
9961 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9962 const SMDS_MeshElement* elem = *eIt;
9963 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9964 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9965 if ( firstIndex > -1 && secondIndex > -1 ) break;
9967 if ( firstIndex < 0 || secondIndex < 0 ) {
9968 // we can simply return until temporary faces created
9969 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9972 // -----------------------------------------------------------
9973 // 1a. Collect nodes of existing faces
9974 // and build set of face nodes in order to detect missing
9975 // faces corresponding to sides of volumes
9976 // -----------------------------------------------------------
9978 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9980 // loop on the given element of a side
9981 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9982 //const SMDS_MeshElement* elem = *eIt;
9983 const SMDS_MeshElement* elem = *eIt;
9984 if ( elem->GetType() == SMDSAbs_Face ) {
9985 faceSet->insert( elem );
9986 set <const SMDS_MeshNode*> faceNodeSet;
9987 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9988 while ( nodeIt->more() ) {
9989 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9990 nodeSet->insert( n );
9991 faceNodeSet.insert( n );
9993 setOfFaceNodeSet.insert( faceNodeSet );
9995 else if ( elem->GetType() == SMDSAbs_Volume )
9996 volSet->insert( elem );
9998 // ------------------------------------------------------------------------------
9999 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10000 // ------------------------------------------------------------------------------
10002 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10003 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10004 while ( fIt->more() ) { // loop on faces sharing a node
10005 const SMDS_MeshElement* f = fIt->next();
10006 if ( faceSet->find( f ) == faceSet->end() ) {
10007 // check if all nodes are in nodeSet and
10008 // complete setOfFaceNodeSet if they are
10009 set <const SMDS_MeshNode*> faceNodeSet;
10010 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10011 bool allInSet = true;
10012 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10013 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10014 if ( nodeSet->find( n ) == nodeSet->end() )
10017 faceNodeSet.insert( n );
10020 faceSet->insert( f );
10021 setOfFaceNodeSet.insert( faceNodeSet );
10027 // -------------------------------------------------------------------------
10028 // 1c. Create temporary faces representing sides of volumes if correspondent
10029 // face does not exist
10030 // -------------------------------------------------------------------------
10032 if ( !volSet->empty() ) {
10033 //int nodeSetSize = nodeSet->size();
10035 // loop on given volumes
10036 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10037 SMDS_VolumeTool vol (*vIt);
10038 // loop on volume faces: find free faces
10039 // --------------------------------------
10040 list<const SMDS_MeshElement* > freeFaceList;
10041 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10042 if ( !vol.IsFreeFace( iFace ))
10044 // check if there is already a face with same nodes in a face set
10045 const SMDS_MeshElement* aFreeFace = 0;
10046 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10047 int nbNodes = vol.NbFaceNodes( iFace );
10048 set <const SMDS_MeshNode*> faceNodeSet;
10049 vol.GetFaceNodes( iFace, faceNodeSet );
10050 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10052 // no such a face is given but it still can exist, check it
10053 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10054 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10056 if ( !aFreeFace ) {
10057 // create a temporary face
10058 if ( nbNodes == 3 ) {
10059 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10060 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10062 else if ( nbNodes == 4 ) {
10063 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10064 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10067 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10068 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10069 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10072 tempFaceList.push_back( aFreeFace );
10076 freeFaceList.push_back( aFreeFace );
10078 } // loop on faces of a volume
10080 // choose one of several free faces of a volume
10081 // --------------------------------------------
10082 if ( freeFaceList.size() > 1 ) {
10083 // choose a face having max nb of nodes shared by other elems of a side
10084 int maxNbNodes = -1;
10085 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10086 while ( fIt != freeFaceList.end() ) { // loop on free faces
10087 int nbSharedNodes = 0;
10088 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10089 while ( nodeIt->more() ) { // loop on free face nodes
10090 const SMDS_MeshNode* n =
10091 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10092 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10093 while ( invElemIt->more() ) {
10094 const SMDS_MeshElement* e = invElemIt->next();
10095 nbSharedNodes += faceSet->count( e );
10096 nbSharedNodes += elemSet->count( e );
10099 if ( nbSharedNodes > maxNbNodes ) {
10100 maxNbNodes = nbSharedNodes;
10101 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10103 else if ( nbSharedNodes == maxNbNodes ) {
10107 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10110 if ( freeFaceList.size() > 1 )
10112 // could not choose one face, use another way
10113 // choose a face most close to the bary center of the opposite side
10114 gp_XYZ aBC( 0., 0., 0. );
10115 set <const SMDS_MeshNode*> addedNodes;
10116 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10117 eIt = elemSet2->begin();
10118 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10119 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10120 while ( nodeIt->more() ) { // loop on free face nodes
10121 const SMDS_MeshNode* n =
10122 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10123 if ( addedNodes.insert( n ).second )
10124 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10127 aBC /= addedNodes.size();
10128 double minDist = DBL_MAX;
10129 fIt = freeFaceList.begin();
10130 while ( fIt != freeFaceList.end() ) { // loop on free faces
10132 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10133 while ( nodeIt->more() ) { // loop on free face nodes
10134 const SMDS_MeshNode* n =
10135 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10136 gp_XYZ p( n->X(),n->Y(),n->Z() );
10137 dist += ( aBC - p ).SquareModulus();
10139 if ( dist < minDist ) {
10141 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10144 fIt = freeFaceList.erase( fIt++ );
10147 } // choose one of several free faces of a volume
10149 if ( freeFaceList.size() == 1 ) {
10150 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10151 faceSet->insert( aFreeFace );
10152 // complete a node set with nodes of a found free face
10153 // for ( iNode = 0; iNode < ; iNode++ )
10154 // nodeSet->insert( fNodes[ iNode ] );
10157 } // loop on volumes of a side
10159 // // complete a set of faces if new nodes in a nodeSet appeared
10160 // // ----------------------------------------------------------
10161 // if ( nodeSetSize != nodeSet->size() ) {
10162 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10163 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10164 // while ( fIt->more() ) { // loop on faces sharing a node
10165 // const SMDS_MeshElement* f = fIt->next();
10166 // if ( faceSet->find( f ) == faceSet->end() ) {
10167 // // check if all nodes are in nodeSet and
10168 // // complete setOfFaceNodeSet if they are
10169 // set <const SMDS_MeshNode*> faceNodeSet;
10170 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10171 // bool allInSet = true;
10172 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10173 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10174 // if ( nodeSet->find( n ) == nodeSet->end() )
10175 // allInSet = false;
10177 // faceNodeSet.insert( n );
10179 // if ( allInSet ) {
10180 // faceSet->insert( f );
10181 // setOfFaceNodeSet.insert( faceNodeSet );
10187 } // Create temporary faces, if there are volumes given
10190 if ( faceSet1.size() != faceSet2.size() ) {
10191 // delete temporary faces: they are in reverseElements of actual nodes
10192 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10193 // while ( tmpFaceIt->more() )
10194 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10195 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10196 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10197 // aMesh->RemoveElement(*tmpFaceIt);
10198 MESSAGE("Diff nb of faces");
10199 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10202 // ============================================================
10203 // 2. Find nodes to merge:
10204 // bind a node to remove to a node to put instead
10205 // ============================================================
10207 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10208 if ( theFirstNode1 != theFirstNode2 )
10209 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10210 if ( theSecondNode1 != theSecondNode2 )
10211 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10213 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10214 set< long > linkIdSet; // links to process
10215 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10217 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10218 list< NLink > linkList[2];
10219 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10220 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10221 // loop on links in linkList; find faces by links and append links
10222 // of the found faces to linkList
10223 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10224 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10226 NLink link[] = { *linkIt[0], *linkIt[1] };
10227 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10228 if ( !linkIdSet.count( linkID ) )
10231 // by links, find faces in the face sets,
10232 // and find indices of link nodes in the found faces;
10233 // in a face set, there is only one or no face sharing a link
10234 // ---------------------------------------------------------------
10236 const SMDS_MeshElement* face[] = { 0, 0 };
10237 vector<const SMDS_MeshNode*> fnodes[2];
10238 int iLinkNode[2][2];
10239 TIDSortedElemSet avoidSet;
10240 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10241 const SMDS_MeshNode* n1 = link[iSide].first;
10242 const SMDS_MeshNode* n2 = link[iSide].second;
10243 //cout << "Side " << iSide << " ";
10244 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10245 // find a face by two link nodes
10246 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10247 *faceSetPtr[ iSide ], avoidSet,
10248 &iLinkNode[iSide][0],
10249 &iLinkNode[iSide][1] );
10250 if ( face[ iSide ])
10252 //cout << " F " << face[ iSide]->GetID() <<endl;
10253 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10254 // put face nodes to fnodes
10255 if ( face[ iSide ]->IsQuadratic() )
10257 // use interlaced nodes iterator
10258 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10259 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10260 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10261 while ( nIter->more() )
10262 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10266 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10267 face[ iSide ]->end_nodes() );
10269 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10273 // check similarity of elements of the sides
10274 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10275 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10276 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10277 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10280 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10282 break; // do not return because it's necessary to remove tmp faces
10285 // set nodes to merge
10286 // -------------------
10288 if ( face[0] && face[1] ) {
10289 const int nbNodes = face[0]->NbNodes();
10290 if ( nbNodes != face[1]->NbNodes() ) {
10291 MESSAGE("Diff nb of face nodes");
10292 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10293 break; // do not return because it s necessary to remove tmp faces
10295 bool reverse[] = { false, false }; // order of nodes in the link
10296 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10297 // analyse link orientation in faces
10298 int i1 = iLinkNode[ iSide ][ 0 ];
10299 int i2 = iLinkNode[ iSide ][ 1 ];
10300 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10302 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10303 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10304 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10306 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10307 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10310 // add other links of the faces to linkList
10311 // -----------------------------------------
10313 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10314 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10315 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10316 if ( !iter_isnew.second ) { // already in a set: no need to process
10317 linkIdSet.erase( iter_isnew.first );
10319 else // new in set == encountered for the first time: add
10321 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10322 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10323 linkList[0].push_back ( NLink( n1, n2 ));
10324 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10329 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10332 } // loop on link lists
10334 if ( aResult == SEW_OK &&
10335 ( //linkIt[0] != linkList[0].end() ||
10336 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10337 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10338 " " << (faceSetPtr[1]->empty()));
10339 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10342 // ====================================================================
10343 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10344 // ====================================================================
10346 // delete temporary faces
10347 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10348 // while ( tmpFaceIt->more() )
10349 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10350 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10351 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10352 aMesh->RemoveElement(*tmpFaceIt);
10354 if ( aResult != SEW_OK)
10357 list< int > nodeIDsToRemove;
10358 vector< const SMDS_MeshNode*> nodes;
10359 ElemFeatures elemType;
10361 // loop on nodes replacement map
10362 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10363 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10364 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10366 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10367 nodeIDsToRemove.push_back( nToRemove->GetID() );
10368 // loop on elements sharing nToRemove
10369 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10370 while ( invElemIt->more() ) {
10371 const SMDS_MeshElement* e = invElemIt->next();
10372 // get a new suite of nodes: make replacement
10373 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10374 nodes.resize( nbNodes );
10375 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10376 while ( nIt->more() ) {
10377 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10378 nnIt = nReplaceMap.find( n );
10379 if ( nnIt != nReplaceMap.end() ) {
10381 n = (*nnIt).second;
10385 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10386 // elemIDsToRemove.push_back( e->GetID() );
10390 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10391 aMesh->RemoveElement( e );
10393 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10395 AddToSameGroups( newElem, e, aMesh );
10396 if ( int aShapeId = e->getshapeId() )
10397 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10403 Remove( nodeIDsToRemove, true );
10408 //================================================================================
10410 * \brief Find corresponding nodes in two sets of faces
10411 * \param theSide1 - first face set
10412 * \param theSide2 - second first face
10413 * \param theFirstNode1 - a boundary node of set 1
10414 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10415 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10416 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10417 * \param nReplaceMap - output map of corresponding nodes
10418 * \return bool - is a success or not
10420 //================================================================================
10423 //#define DEBUG_MATCHING_NODES
10426 SMESH_MeshEditor::Sew_Error
10427 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10428 set<const SMDS_MeshElement*>& theSide2,
10429 const SMDS_MeshNode* theFirstNode1,
10430 const SMDS_MeshNode* theFirstNode2,
10431 const SMDS_MeshNode* theSecondNode1,
10432 const SMDS_MeshNode* theSecondNode2,
10433 TNodeNodeMap & nReplaceMap)
10435 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10437 nReplaceMap.clear();
10438 if ( theFirstNode1 != theFirstNode2 )
10439 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10440 if ( theSecondNode1 != theSecondNode2 )
10441 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10443 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10444 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10446 list< NLink > linkList[2];
10447 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10448 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10450 // loop on links in linkList; find faces by links and append links
10451 // of the found faces to linkList
10452 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10453 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10454 NLink link[] = { *linkIt[0], *linkIt[1] };
10455 if ( linkSet.find( link[0] ) == linkSet.end() )
10458 // by links, find faces in the face sets,
10459 // and find indices of link nodes in the found faces;
10460 // in a face set, there is only one or no face sharing a link
10461 // ---------------------------------------------------------------
10463 const SMDS_MeshElement* face[] = { 0, 0 };
10464 list<const SMDS_MeshNode*> notLinkNodes[2];
10465 //bool reverse[] = { false, false }; // order of notLinkNodes
10467 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10469 const SMDS_MeshNode* n1 = link[iSide].first;
10470 const SMDS_MeshNode* n2 = link[iSide].second;
10471 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10472 set< const SMDS_MeshElement* > facesOfNode1;
10473 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10475 // during a loop of the first node, we find all faces around n1,
10476 // during a loop of the second node, we find one face sharing both n1 and n2
10477 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10478 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10479 while ( fIt->more() ) { // loop on faces sharing a node
10480 const SMDS_MeshElement* f = fIt->next();
10481 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10482 ! facesOfNode1.insert( f ).second ) // f encounters twice
10484 if ( face[ iSide ] ) {
10485 MESSAGE( "2 faces per link " );
10486 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10489 faceSet->erase( f );
10491 // get not link nodes
10492 int nbN = f->NbNodes();
10493 if ( f->IsQuadratic() )
10495 nbNodes[ iSide ] = nbN;
10496 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10497 int i1 = f->GetNodeIndex( n1 );
10498 int i2 = f->GetNodeIndex( n2 );
10499 int iEnd = nbN, iBeg = -1, iDelta = 1;
10500 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10502 std::swap( iEnd, iBeg ); iDelta = -1;
10507 if ( i == iEnd ) i = iBeg + iDelta;
10508 if ( i == i1 ) break;
10509 nodes.push_back ( f->GetNode( i ) );
10515 // check similarity of elements of the sides
10516 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10517 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10518 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10519 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10522 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10526 // set nodes to merge
10527 // -------------------
10529 if ( face[0] && face[1] ) {
10530 if ( nbNodes[0] != nbNodes[1] ) {
10531 MESSAGE("Diff nb of face nodes");
10532 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10534 #ifdef DEBUG_MATCHING_NODES
10535 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10536 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10537 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10539 int nbN = nbNodes[0];
10541 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10542 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10543 for ( int i = 0 ; i < nbN - 2; ++i ) {
10544 #ifdef DEBUG_MATCHING_NODES
10545 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10547 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10551 // add other links of the face 1 to linkList
10552 // -----------------------------------------
10554 const SMDS_MeshElement* f0 = face[0];
10555 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10556 for ( int i = 0; i < nbN; i++ )
10558 const SMDS_MeshNode* n2 = f0->GetNode( i );
10559 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10560 linkSet.insert( SMESH_TLink( n1, n2 ));
10561 if ( !iter_isnew.second ) { // already in a set: no need to process
10562 linkSet.erase( iter_isnew.first );
10564 else // new in set == encountered for the first time: add
10566 #ifdef DEBUG_MATCHING_NODES
10567 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10568 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10570 linkList[0].push_back ( NLink( n1, n2 ));
10571 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10576 } // loop on link lists
10581 //================================================================================
10583 * \brief Create elements equal (on same nodes) to given ones
10584 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10585 * elements of the uppest dimension are duplicated.
10587 //================================================================================
10589 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10591 ClearLastCreated();
10592 SMESHDS_Mesh* mesh = GetMeshDS();
10594 // get an element type and an iterator over elements
10596 SMDSAbs_ElementType type;
10597 SMDS_ElemIteratorPtr elemIt;
10598 vector< const SMDS_MeshElement* > allElems;
10599 if ( theElements.empty() )
10601 if ( mesh->NbNodes() == 0 )
10603 // get most complex type
10604 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10605 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10606 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10608 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10609 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10614 // put all elements in the vector <allElems>
10615 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10616 elemIt = mesh->elementsIterator( type );
10617 while ( elemIt->more() )
10618 allElems.push_back( elemIt->next());
10619 elemIt = elemSetIterator( allElems );
10623 type = (*theElements.begin())->GetType();
10624 elemIt = elemSetIterator( theElements );
10627 // duplicate elements
10629 ElemFeatures elemType;
10631 vector< const SMDS_MeshNode* > nodes;
10632 while ( elemIt->more() )
10634 const SMDS_MeshElement* elem = elemIt->next();
10635 if ( elem->GetType() != type )
10638 elemType.Init( elem, /*basicOnly=*/false );
10639 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10641 AddElement( nodes, elemType );
10645 //================================================================================
10647 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10648 \param theElems - the list of elements (edges or faces) to be replicated
10649 The nodes for duplication could be found from these elements
10650 \param theNodesNot - list of nodes to NOT replicate
10651 \param theAffectedElems - the list of elements (cells and edges) to which the
10652 replicated nodes should be associated to.
10653 \return TRUE if operation has been completed successfully, FALSE otherwise
10655 //================================================================================
10657 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10658 const TIDSortedElemSet& theNodesNot,
10659 const TIDSortedElemSet& theAffectedElems )
10661 myLastCreatedElems.Clear();
10662 myLastCreatedNodes.Clear();
10664 if ( theElems.size() == 0 )
10667 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10672 TNodeNodeMap anOldNodeToNewNode;
10673 // duplicate elements and nodes
10674 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10675 // replce nodes by duplications
10676 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10680 //================================================================================
10682 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10683 \param theMeshDS - mesh instance
10684 \param theElems - the elements replicated or modified (nodes should be changed)
10685 \param theNodesNot - nodes to NOT replicate
10686 \param theNodeNodeMap - relation of old node to new created node
10687 \param theIsDoubleElem - flag os to replicate element or modify
10688 \return TRUE if operation has been completed successfully, FALSE otherwise
10690 //================================================================================
10692 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10693 const TIDSortedElemSet& theElems,
10694 const TIDSortedElemSet& theNodesNot,
10695 TNodeNodeMap& theNodeNodeMap,
10696 const bool theIsDoubleElem )
10698 MESSAGE("doubleNodes");
10699 // iterate through element and duplicate them (by nodes duplication)
10701 std::vector<const SMDS_MeshNode*> newNodes;
10702 ElemFeatures elemType;
10704 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10705 for ( ; elemItr != theElems.end(); ++elemItr )
10707 const SMDS_MeshElement* anElem = *elemItr;
10711 // duplicate nodes to duplicate element
10712 bool isDuplicate = false;
10713 newNodes.resize( anElem->NbNodes() );
10714 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10716 while ( anIter->more() )
10718 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10719 const SMDS_MeshNode* aNewNode = aCurrNode;
10720 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10721 if ( n2n != theNodeNodeMap.end() )
10723 aNewNode = n2n->second;
10725 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10728 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10729 copyPosition( aCurrNode, aNewNode );
10730 theNodeNodeMap[ aCurrNode ] = aNewNode;
10731 myLastCreatedNodes.Append( aNewNode );
10733 isDuplicate |= (aCurrNode != aNewNode);
10734 newNodes[ ind++ ] = aNewNode;
10736 if ( !isDuplicate )
10739 if ( theIsDoubleElem )
10740 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10742 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10749 //================================================================================
10751 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10752 \param theNodes - identifiers of nodes to be doubled
10753 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10754 nodes. If list of element identifiers is empty then nodes are doubled but
10755 they not assigned to elements
10756 \return TRUE if operation has been completed successfully, FALSE otherwise
10758 //================================================================================
10760 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10761 const std::list< int >& theListOfModifiedElems )
10763 MESSAGE("DoubleNodes");
10764 myLastCreatedElems.Clear();
10765 myLastCreatedNodes.Clear();
10767 if ( theListOfNodes.size() == 0 )
10770 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10774 // iterate through nodes and duplicate them
10776 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10778 std::list< int >::const_iterator aNodeIter;
10779 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10781 int aCurr = *aNodeIter;
10782 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10788 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10791 copyPosition( aNode, aNewNode );
10792 anOldNodeToNewNode[ aNode ] = aNewNode;
10793 myLastCreatedNodes.Append( aNewNode );
10797 // Create map of new nodes for modified elements
10799 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10801 std::list< int >::const_iterator anElemIter;
10802 for ( anElemIter = theListOfModifiedElems.begin();
10803 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10805 int aCurr = *anElemIter;
10806 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10810 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10812 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10814 while ( anIter->more() )
10816 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10817 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10819 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10820 aNodeArr[ ind++ ] = aNewNode;
10823 aNodeArr[ ind++ ] = aCurrNode;
10825 anElemToNodes[ anElem ] = aNodeArr;
10828 // Change nodes of elements
10830 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10831 anElemToNodesIter = anElemToNodes.begin();
10832 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10834 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10835 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10838 MESSAGE("ChangeElementNodes");
10839 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10848 //================================================================================
10850 \brief Check if element located inside shape
10851 \return TRUE if IN or ON shape, FALSE otherwise
10853 //================================================================================
10855 template<class Classifier>
10856 bool isInside(const SMDS_MeshElement* theElem,
10857 Classifier& theClassifier,
10858 const double theTol)
10860 gp_XYZ centerXYZ (0, 0, 0);
10861 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10862 while (aNodeItr->more())
10863 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10865 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10866 theClassifier.Perform(aPnt, theTol);
10867 TopAbs_State aState = theClassifier.State();
10868 return (aState == TopAbs_IN || aState == TopAbs_ON );
10871 //================================================================================
10873 * \brief Classifier of the 3D point on the TopoDS_Face
10874 * with interaface suitable for isInside()
10876 //================================================================================
10878 struct _FaceClassifier
10880 Extrema_ExtPS _extremum;
10881 BRepAdaptor_Surface _surface;
10882 TopAbs_State _state;
10884 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10886 _extremum.Initialize( _surface,
10887 _surface.FirstUParameter(), _surface.LastUParameter(),
10888 _surface.FirstVParameter(), _surface.LastVParameter(),
10889 _surface.Tolerance(), _surface.Tolerance() );
10891 void Perform(const gp_Pnt& aPnt, double theTol)
10894 _state = TopAbs_OUT;
10895 _extremum.Perform(aPnt);
10896 if ( _extremum.IsDone() )
10897 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10898 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10900 TopAbs_State State() const
10907 //================================================================================
10909 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10910 This method is the first step of DoubleNodeElemGroupsInRegion.
10911 \param theElems - list of groups of elements (edges or faces) to be replicated
10912 \param theNodesNot - list of groups of nodes not to replicated
10913 \param theShape - shape to detect affected elements (element which geometric center
10914 located on or inside shape). If the shape is null, detection is done on faces orientations
10915 (select elements with a gravity center on the side given by faces normals).
10916 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10917 The replicated nodes should be associated to affected elements.
10918 \return groups of affected elements
10919 \sa DoubleNodeElemGroupsInRegion()
10921 //================================================================================
10923 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10924 const TIDSortedElemSet& theNodesNot,
10925 const TopoDS_Shape& theShape,
10926 TIDSortedElemSet& theAffectedElems)
10928 if ( theShape.IsNull() )
10930 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10931 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10932 std::set<const SMDS_MeshElement*> edgesToCheck;
10933 alreadyCheckedNodes.clear();
10934 alreadyCheckedElems.clear();
10935 edgesToCheck.clear();
10937 // --- iterates on elements to be replicated and get elements by back references from their nodes
10939 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10941 for ( ielem=1; elemItr != theElems.end(); ++elemItr )
10943 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10944 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10947 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10948 MESSAGE("element " << ielem++ << " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10949 std::set<const SMDS_MeshNode*> nodesElem;
10951 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10952 while ( nodeItr->more() )
10954 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10955 nodesElem.insert(aNode);
10957 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10958 for (; nodit != nodesElem.end(); nodit++)
10960 MESSAGE(" noeud ");
10961 const SMDS_MeshNode* aNode = *nodit;
10962 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10964 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10966 alreadyCheckedNodes.insert(aNode);
10967 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10968 while ( backElemItr->more() )
10970 MESSAGE(" backelem ");
10971 const SMDS_MeshElement* curElem = backElemItr->next();
10972 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10974 if (theElems.find(curElem) != theElems.end())
10976 alreadyCheckedElems.insert(curElem);
10977 double x=0, y=0, z=0;
10979 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10980 while ( nodeItr2->more() )
10982 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10983 x += anotherNode->X();
10984 y += anotherNode->Y();
10985 z += anotherNode->Z();
10989 p.SetCoord( x/nb -aNode->X(),
10991 z/nb -aNode->Z() );
10992 MESSAGE(" check " << p.X() << " " << p.Y() << " " << p.Z());
10995 MESSAGE(" --- inserted")
10996 theAffectedElems.insert( curElem );
10998 else if (curElem->GetType() == SMDSAbs_Edge)
10999 edgesToCheck.insert(curElem);
11003 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
11004 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
11005 for( ; eit != edgesToCheck.end(); eit++)
11007 bool onside = true;
11008 const SMDS_MeshElement* anEdge = *eit;
11009 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
11010 while ( nodeItr->more() )
11012 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11013 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
11021 MESSAGE(" --- edge onside inserted")
11022 theAffectedElems.insert(anEdge);
11028 const double aTol = Precision::Confusion();
11029 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11030 auto_ptr<_FaceClassifier> aFaceClassifier;
11031 if ( theShape.ShapeType() == TopAbs_SOLID )
11033 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11034 bsc3d->PerformInfinitePoint(aTol);
11036 else if (theShape.ShapeType() == TopAbs_FACE )
11038 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11041 // iterates on indicated elements and get elements by back references from their nodes
11042 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11044 for ( ielem = 1; elemItr != theElems.end(); ++elemItr )
11046 MESSAGE("element " << ielem++);
11047 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11050 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11051 while ( nodeItr->more() )
11053 MESSAGE(" noeud ");
11054 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11055 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11057 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11058 while ( backElemItr->more() )
11060 MESSAGE(" backelem ");
11061 const SMDS_MeshElement* curElem = backElemItr->next();
11062 if ( curElem && theElems.find(curElem) == theElems.end() &&
11064 isInside( curElem, *bsc3d, aTol ) :
11065 isInside( curElem, *aFaceClassifier, aTol )))
11066 theAffectedElems.insert( curElem );
11074 //================================================================================
11076 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11077 \param theElems - group of of elements (edges or faces) to be replicated
11078 \param theNodesNot - group of nodes not to replicate
11079 \param theShape - shape to detect affected elements (element which geometric center
11080 located on or inside shape).
11081 The replicated nodes should be associated to affected elements.
11082 \return TRUE if operation has been completed successfully, FALSE otherwise
11084 //================================================================================
11086 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11087 const TIDSortedElemSet& theNodesNot,
11088 const TopoDS_Shape& theShape )
11090 if ( theShape.IsNull() )
11093 const double aTol = Precision::Confusion();
11094 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11095 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
11096 if ( theShape.ShapeType() == TopAbs_SOLID )
11098 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11099 bsc3d->PerformInfinitePoint(aTol);
11101 else if (theShape.ShapeType() == TopAbs_FACE )
11103 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11106 // iterates on indicated elements and get elements by back references from their nodes
11107 TIDSortedElemSet anAffected;
11108 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11109 for ( ; elemItr != theElems.end(); ++elemItr )
11111 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11115 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11116 while ( nodeItr->more() )
11118 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11119 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11121 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11122 while ( backElemItr->more() )
11124 const SMDS_MeshElement* curElem = backElemItr->next();
11125 if ( curElem && theElems.find(curElem) == theElems.end() &&
11127 isInside( curElem, *bsc3d, aTol ) :
11128 isInside( curElem, *aFaceClassifier, aTol )))
11129 anAffected.insert( curElem );
11133 return DoubleNodes( theElems, theNodesNot, anAffected );
11137 * \brief compute an oriented angle between two planes defined by four points.
11138 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11139 * @param p0 base of the rotation axe
11140 * @param p1 extremity of the rotation axe
11141 * @param g1 belongs to the first plane
11142 * @param g2 belongs to the second plane
11144 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11146 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11147 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11148 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11149 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11150 gp_Vec vref(p0, p1);
11153 gp_Vec n1 = vref.Crossed(v1);
11154 gp_Vec n2 = vref.Crossed(v2);
11156 return n2.AngleWithRef(n1, vref);
11158 catch ( Standard_Failure ) {
11160 return Max( v1.Magnitude(), v2.Magnitude() );
11164 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11165 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11166 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11167 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11168 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11169 * 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.
11170 * 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.
11171 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11172 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11173 * \param theElems - list of groups of volumes, where a group of volume is a set of
11174 * SMDS_MeshElements sorted by Id.
11175 * \param createJointElems - if TRUE, create the elements
11176 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11177 * the boundary between \a theDomains and the rest mesh
11178 * \return TRUE if operation has been completed successfully, FALSE otherwise
11180 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11181 bool createJointElems,
11182 bool onAllBoundaries)
11184 MESSAGE("----------------------------------------------");
11185 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11186 MESSAGE("----------------------------------------------");
11188 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11189 meshDS->BuildDownWardConnectivity(true);
11191 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11193 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11194 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11195 // build the list of nodes shared by 2 or more domains, with their domain indexes
11197 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11198 std::map<int,int>celldom; // cell vtkId --> domain
11199 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11200 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11201 faceDomains.clear();
11203 cellDomains.clear();
11204 nodeDomains.clear();
11205 std::map<int,int> emptyMap;
11206 std::set<int> emptySet;
11209 MESSAGE(".. Number of domains :"<<theElems.size());
11211 TIDSortedElemSet theRestDomElems;
11212 const int iRestDom = -1;
11213 const int idom0 = onAllBoundaries ? iRestDom : 0;
11214 const int nbDomains = theElems.size();
11216 // Check if the domains do not share an element
11217 for (int idom = 0; idom < nbDomains-1; idom++)
11219 // MESSAGE("... Check of domain #" << idom);
11220 const TIDSortedElemSet& domain = theElems[idom];
11221 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11222 for (; elemItr != domain.end(); ++elemItr)
11224 const SMDS_MeshElement* anElem = *elemItr;
11225 int idombisdeb = idom + 1 ;
11226 // check if the element belongs to a domain further in the list
11227 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11229 const TIDSortedElemSet& domainbis = theElems[idombis];
11230 if ( domainbis.count( anElem ))
11232 MESSAGE(".... Domain #" << idom);
11233 MESSAGE(".... Domain #" << idombis);
11234 throw SALOME_Exception("The domains are not disjoint.");
11241 for (int idom = 0; idom < nbDomains; idom++)
11244 // --- build a map (face to duplicate --> volume to modify)
11245 // with all the faces shared by 2 domains (group of elements)
11246 // and corresponding volume of this domain, for each shared face.
11247 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11249 MESSAGE("... Neighbors of domain #" << idom);
11250 const TIDSortedElemSet& domain = theElems[idom];
11251 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11252 for (; elemItr != domain.end(); ++elemItr)
11254 const SMDS_MeshElement* anElem = *elemItr;
11257 int vtkId = anElem->getVtkId();
11258 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11259 int neighborsVtkIds[NBMAXNEIGHBORS];
11260 int downIds[NBMAXNEIGHBORS];
11261 unsigned char downTypes[NBMAXNEIGHBORS];
11262 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11263 for (int n = 0; n < nbNeighbors; n++)
11265 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11266 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11267 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11270 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11272 // MESSAGE("Domain " << idombis);
11273 const TIDSortedElemSet& domainbis = theElems[idombis];
11274 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11276 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11278 DownIdType face(downIds[n], downTypes[n]);
11279 if (!faceDomains[face].count(idom))
11281 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11282 celldom[vtkId] = idom;
11283 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11287 theRestDomElems.insert( elem );
11288 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11289 celldom[neighborsVtkIds[n]] = iRestDom;
11297 //MESSAGE("Number of shared faces " << faceDomains.size());
11298 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11300 // --- explore the shared faces domain by domain,
11301 // explore the nodes of the face and see if they belong to a cell in the domain,
11302 // which has only a node or an edge on the border (not a shared face)
11304 for (int idomain = idom0; idomain < nbDomains; idomain++)
11306 //MESSAGE("Domain " << idomain);
11307 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11308 itface = faceDomains.begin();
11309 for (; itface != faceDomains.end(); ++itface)
11311 const std::map<int, int>& domvol = itface->second;
11312 if (!domvol.count(idomain))
11314 DownIdType face = itface->first;
11315 //MESSAGE(" --- face " << face.cellId);
11316 std::set<int> oldNodes;
11318 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11319 std::set<int>::iterator itn = oldNodes.begin();
11320 for (; itn != oldNodes.end(); ++itn)
11323 //MESSAGE(" node " << oldId);
11324 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11325 for (int i=0; i<l.ncells; i++)
11327 int vtkId = l.cells[i];
11328 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11329 if (!domain.count(anElem))
11331 int vtkType = grid->GetCellType(vtkId);
11332 int downId = grid->CellIdToDownId(vtkId);
11335 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11336 continue; // not OK at this stage of the algorithm:
11337 //no cells created after BuildDownWardConnectivity
11339 DownIdType aCell(downId, vtkType);
11340 cellDomains[aCell][idomain] = vtkId;
11341 celldom[vtkId] = idomain;
11342 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11348 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11349 // for each shared face, get the nodes
11350 // for each node, for each domain of the face, create a clone of the node
11352 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11353 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11354 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11356 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11357 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11358 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11360 MESSAGE(".. Duplication of the nodes");
11361 for (int idomain = idom0; idomain < nbDomains; idomain++)
11363 itface = faceDomains.begin();
11364 for (; itface != faceDomains.end(); ++itface)
11366 const std::map<int, int>& domvol = itface->second;
11367 if (!domvol.count(idomain))
11369 DownIdType face = itface->first;
11370 //MESSAGE(" --- face " << face.cellId);
11371 std::set<int> oldNodes;
11373 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11374 std::set<int>::iterator itn = oldNodes.begin();
11375 for (; itn != oldNodes.end(); ++itn)
11378 if (nodeDomains[oldId].empty())
11380 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11381 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11383 std::map<int, int>::const_iterator itdom = domvol.begin();
11384 for (; itdom != domvol.end(); ++itdom)
11386 int idom = itdom->first;
11387 //MESSAGE(" domain " << idom);
11388 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11390 if (nodeDomains[oldId].size() >= 2) // a multiple node
11392 vector<int> orderedDoms;
11393 //MESSAGE("multiple node " << oldId);
11394 if (mutipleNodes.count(oldId))
11395 orderedDoms = mutipleNodes[oldId];
11398 map<int,int>::iterator it = nodeDomains[oldId].begin();
11399 for (; it != nodeDomains[oldId].end(); ++it)
11400 orderedDoms.push_back(it->first);
11402 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11403 //stringstream txt;
11404 //for (int i=0; i<orderedDoms.size(); i++)
11405 // txt << orderedDoms[i] << " ";
11406 //MESSAGE("orderedDoms " << txt.str());
11407 mutipleNodes[oldId] = orderedDoms;
11409 double *coords = grid->GetPoint(oldId);
11410 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11411 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11412 int newId = newNode->getVtkId();
11413 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11414 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11421 MESSAGE(".. Creation of elements");
11422 for (int idomain = idom0; idomain < nbDomains; idomain++)
11424 itface = faceDomains.begin();
11425 for (; itface != faceDomains.end(); ++itface)
11427 std::map<int, int> domvol = itface->second;
11428 if (!domvol.count(idomain))
11430 DownIdType face = itface->first;
11431 //MESSAGE(" --- face " << face.cellId);
11432 std::set<int> oldNodes;
11434 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11435 int nbMultipleNodes = 0;
11436 std::set<int>::iterator itn = oldNodes.begin();
11437 for (; itn != oldNodes.end(); ++itn)
11440 if (mutipleNodes.count(oldId))
11443 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11445 //MESSAGE("multiple Nodes detected on a shared face");
11446 int downId = itface->first.cellId;
11447 unsigned char cellType = itface->first.cellType;
11448 // --- shared edge or shared face ?
11449 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11452 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11453 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11454 if (mutipleNodes.count(nodes[i]))
11455 if (!mutipleNodesToFace.count(nodes[i]))
11456 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11458 else // shared face (between two volumes)
11460 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11461 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11462 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11463 for (int ie =0; ie < nbEdges; ie++)
11466 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11467 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11469 vector<int> vn0 = mutipleNodes[nodes[0]];
11470 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11472 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11473 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11474 if ( vn0[i0] == vn1[i1] )
11475 doms.push_back( vn0[ i0 ]);
11476 if ( doms.size() > 2 )
11478 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11479 double *coords = grid->GetPoint(nodes[0]);
11480 gp_Pnt p0(coords[0], coords[1], coords[2]);
11481 coords = grid->GetPoint(nodes[nbNodes - 1]);
11482 gp_Pnt p1(coords[0], coords[1], coords[2]);
11484 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11485 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11486 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11487 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11488 for ( size_t id = 0; id < doms.size(); id++ )
11490 int idom = doms[id];
11491 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11492 for ( int ivol = 0; ivol < nbvol; ivol++ )
11494 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11495 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11496 if (domain.count(elem))
11498 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11499 domvol[idom] = svol;
11500 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11502 vtkIdType npts = 0;
11503 vtkIdType* pts = 0;
11504 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11505 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11508 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11509 angleDom[idom] = 0;
11513 gp_Pnt g(values[0], values[1], values[2]);
11514 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11515 //MESSAGE(" angle=" << angleDom[idom]);
11521 map<double, int> sortedDom; // sort domains by angle
11522 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11523 sortedDom[ia->second] = ia->first;
11524 vector<int> vnodes;
11526 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11528 vdom.push_back(ib->second);
11529 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11531 for (int ino = 0; ino < nbNodes; ino++)
11532 vnodes.push_back(nodes[ino]);
11533 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11542 // --- iterate on shared faces (volumes to modify, face to extrude)
11543 // get node id's of the face (id SMDS = id VTK)
11544 // create flat element with old and new nodes if requested
11546 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11547 // (domain1 X domain2) = domain1 + MAXINT*domain2
11549 std::map<int, std::map<long,int> > nodeQuadDomains;
11550 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11552 MESSAGE(".. Creation of elements: simple junction");
11553 if (createJointElems)
11556 string joints2DName = "joints2D";
11557 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11558 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11559 string joints3DName = "joints3D";
11560 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11561 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11563 itface = faceDomains.begin();
11564 for (; itface != faceDomains.end(); ++itface)
11566 DownIdType face = itface->first;
11567 std::set<int> oldNodes;
11568 std::set<int>::iterator itn;
11570 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11572 std::map<int, int> domvol = itface->second;
11573 std::map<int, int>::iterator itdom = domvol.begin();
11574 int dom1 = itdom->first;
11575 int vtkVolId = itdom->second;
11577 int dom2 = itdom->first;
11578 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11580 stringstream grpname;
11583 grpname << dom1 << "_" << dom2;
11585 grpname << dom2 << "_" << dom1;
11586 string namegrp = grpname.str();
11587 if (!mapOfJunctionGroups.count(namegrp))
11588 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11589 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11591 sgrp->Add(vol->GetID());
11592 if (vol->GetType() == SMDSAbs_Volume)
11593 joints3DGrp->Add(vol->GetID());
11594 else if (vol->GetType() == SMDSAbs_Face)
11595 joints2DGrp->Add(vol->GetID());
11599 // --- create volumes on multiple domain intersection if requested
11600 // iterate on mutipleNodesToFace
11601 // iterate on edgesMultiDomains
11603 MESSAGE(".. Creation of elements: multiple junction");
11604 if (createJointElems)
11606 // --- iterate on mutipleNodesToFace
11608 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11609 for (; itn != mutipleNodesToFace.end(); ++itn)
11611 int node = itn->first;
11612 vector<int> orderDom = itn->second;
11613 vector<vtkIdType> orderedNodes;
11614 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11615 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11616 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11618 stringstream grpname;
11620 grpname << 0 << "_" << 0;
11622 string namegrp = grpname.str();
11623 if (!mapOfJunctionGroups.count(namegrp))
11624 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11625 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11627 sgrp->Add(face->GetID());
11630 // --- iterate on edgesMultiDomains
11632 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11633 for (; ite != edgesMultiDomains.end(); ++ite)
11635 vector<int> nodes = ite->first;
11636 vector<int> orderDom = ite->second;
11637 vector<vtkIdType> orderedNodes;
11638 if (nodes.size() == 2)
11640 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11641 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11642 if ( orderDom.size() == 3 )
11643 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11644 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11646 for (int idom = orderDom.size()-1; idom >=0; idom--)
11647 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11648 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11651 string namegrp = "jointsMultiples";
11652 if (!mapOfJunctionGroups.count(namegrp))
11653 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11654 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11656 sgrp->Add(vol->GetID());
11660 //INFOS("Quadratic multiple joints not implemented");
11661 // TODO quadratic nodes
11666 // --- list the explicit faces and edges of the mesh that need to be modified,
11667 // i.e. faces and edges built with one or more duplicated nodes.
11668 // associate these faces or edges to their corresponding domain.
11669 // only the first domain found is kept when a face or edge is shared
11671 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11672 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11673 faceOrEdgeDom.clear();
11676 MESSAGE(".. Modification of elements");
11677 for (int idomain = idom0; idomain < nbDomains; idomain++)
11679 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11680 for (; itnod != nodeDomains.end(); ++itnod)
11682 int oldId = itnod->first;
11683 //MESSAGE(" node " << oldId);
11684 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11685 for (int i = 0; i < l.ncells; i++)
11687 int vtkId = l.cells[i];
11688 int vtkType = grid->GetCellType(vtkId);
11689 int downId = grid->CellIdToDownId(vtkId);
11691 continue; // new cells: not to be modified
11692 DownIdType aCell(downId, vtkType);
11693 int volParents[1000];
11694 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11695 for (int j = 0; j < nbvol; j++)
11696 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11697 if (!feDom.count(vtkId))
11699 feDom[vtkId] = idomain;
11700 faceOrEdgeDom[aCell] = emptyMap;
11701 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11702 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11703 // << " type " << vtkType << " downId " << downId);
11709 // --- iterate on shared faces (volumes to modify, face to extrude)
11710 // get node id's of the face
11711 // replace old nodes by new nodes in volumes, and update inverse connectivity
11713 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11714 for (int m=0; m<3; m++)
11716 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11717 itface = (*amap).begin();
11718 for (; itface != (*amap).end(); ++itface)
11720 DownIdType face = itface->first;
11721 std::set<int> oldNodes;
11722 std::set<int>::iterator itn;
11724 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11725 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11726 std::map<int, int> localClonedNodeIds;
11728 std::map<int, int> domvol = itface->second;
11729 std::map<int, int>::iterator itdom = domvol.begin();
11730 for (; itdom != domvol.end(); ++itdom)
11732 int idom = itdom->first;
11733 int vtkVolId = itdom->second;
11734 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11735 localClonedNodeIds.clear();
11736 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11739 if (nodeDomains[oldId].count(idom))
11741 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11742 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11745 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11750 // Remove empty groups (issue 0022812)
11751 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11752 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11754 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11755 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11758 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11759 grid->BuildLinks();
11767 * \brief Double nodes on some external faces and create flat elements.
11768 * Flat elements are mainly used by some types of mechanic calculations.
11770 * Each group of the list must be constituted of faces.
11771 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11772 * @param theElems - list of groups of faces, where a group of faces is a set of
11773 * SMDS_MeshElements sorted by Id.
11774 * @return TRUE if operation has been completed successfully, FALSE otherwise
11776 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11778 MESSAGE("-------------------------------------------------");
11779 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11780 MESSAGE("-------------------------------------------------");
11782 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11784 // --- For each group of faces
11785 // duplicate the nodes, create a flat element based on the face
11786 // replace the nodes of the faces by their clones
11788 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11789 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11790 clonedNodes.clear();
11791 intermediateNodes.clear();
11792 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11793 mapOfJunctionGroups.clear();
11795 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11797 const TIDSortedElemSet& domain = theElems[idom];
11798 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11799 for ( ; elemItr != domain.end(); ++elemItr )
11801 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11802 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11805 // MESSAGE("aFace=" << aFace->GetID());
11806 bool isQuad = aFace->IsQuadratic();
11807 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11809 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11811 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11812 while (nodeIt->more())
11814 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11815 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11817 ln2.push_back(node);
11819 ln0.push_back(node);
11821 const SMDS_MeshNode* clone = 0;
11822 if (!clonedNodes.count(node))
11824 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11825 copyPosition( node, clone );
11826 clonedNodes[node] = clone;
11829 clone = clonedNodes[node];
11832 ln3.push_back(clone);
11834 ln1.push_back(clone);
11836 const SMDS_MeshNode* inter = 0;
11837 if (isQuad && (!isMedium))
11839 if (!intermediateNodes.count(node))
11841 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11842 copyPosition( node, inter );
11843 intermediateNodes[node] = inter;
11846 inter = intermediateNodes[node];
11847 ln4.push_back(inter);
11851 // --- extrude the face
11853 vector<const SMDS_MeshNode*> ln;
11854 SMDS_MeshVolume* vol = 0;
11855 vtkIdType aType = aFace->GetVtkType();
11859 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11860 // MESSAGE("vol prism " << vol->GetID());
11861 ln.push_back(ln1[0]);
11862 ln.push_back(ln1[1]);
11863 ln.push_back(ln1[2]);
11866 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11867 // MESSAGE("vol hexa " << vol->GetID());
11868 ln.push_back(ln1[0]);
11869 ln.push_back(ln1[1]);
11870 ln.push_back(ln1[2]);
11871 ln.push_back(ln1[3]);
11873 case VTK_QUADRATIC_TRIANGLE:
11874 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11875 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11876 // MESSAGE("vol quad prism " << vol->GetID());
11877 ln.push_back(ln1[0]);
11878 ln.push_back(ln1[1]);
11879 ln.push_back(ln1[2]);
11880 ln.push_back(ln3[0]);
11881 ln.push_back(ln3[1]);
11882 ln.push_back(ln3[2]);
11884 case VTK_QUADRATIC_QUAD:
11885 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11886 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11887 // ln4[0], ln4[1], ln4[2], ln4[3]);
11888 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11889 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11890 ln4[0], ln4[1], ln4[2], ln4[3]);
11891 // MESSAGE("vol quad hexa " << vol->GetID());
11892 ln.push_back(ln1[0]);
11893 ln.push_back(ln1[1]);
11894 ln.push_back(ln1[2]);
11895 ln.push_back(ln1[3]);
11896 ln.push_back(ln3[0]);
11897 ln.push_back(ln3[1]);
11898 ln.push_back(ln3[2]);
11899 ln.push_back(ln3[3]);
11909 stringstream grpname;
11913 string namegrp = grpname.str();
11914 if (!mapOfJunctionGroups.count(namegrp))
11915 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11916 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11918 sgrp->Add(vol->GetID());
11921 // --- modify the face
11923 aFace->ChangeNodes(&ln[0], ln.size());
11930 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11931 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11932 * groups of faces to remove inside the object, (idem edges).
11933 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11935 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11936 const TopoDS_Shape& theShape,
11937 SMESH_NodeSearcher* theNodeSearcher,
11938 const char* groupName,
11939 std::vector<double>& nodesCoords,
11940 std::vector<std::vector<int> >& listOfListOfNodes)
11942 MESSAGE("--------------------------------");
11943 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11944 MESSAGE("--------------------------------");
11946 // --- zone of volumes to remove is given :
11947 // 1 either by a geom shape (one or more vertices) and a radius,
11948 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11949 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11950 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11951 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11952 // defined by it's name.
11954 SMESHDS_GroupBase* groupDS = 0;
11955 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11956 while ( groupIt->more() )
11959 SMESH_Group * group = groupIt->next();
11960 if ( !group ) continue;
11961 groupDS = group->GetGroupDS();
11962 if ( !groupDS || groupDS->IsEmpty() ) continue;
11963 std::string grpName = group->GetName();
11964 //MESSAGE("grpName=" << grpName);
11965 if (grpName == groupName)
11971 bool isNodeGroup = false;
11972 bool isNodeCoords = false;
11975 if (groupDS->GetType() != SMDSAbs_Node)
11977 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11980 if (nodesCoords.size() > 0)
11981 isNodeCoords = true; // a list o nodes given by their coordinates
11982 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11984 // --- define groups to build
11986 int idg; // --- group of SMDS volumes
11987 string grpvName = groupName;
11988 grpvName += "_vol";
11989 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11992 MESSAGE("group not created " << grpvName);
11995 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11997 int idgs; // --- group of SMDS faces on the skin
11998 string grpsName = groupName;
11999 grpsName += "_skin";
12000 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12003 MESSAGE("group not created " << grpsName);
12006 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12008 int idgi; // --- group of SMDS faces internal (several shapes)
12009 string grpiName = groupName;
12010 grpiName += "_internalFaces";
12011 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12014 MESSAGE("group not created " << grpiName);
12017 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12019 int idgei; // --- group of SMDS faces internal (several shapes)
12020 string grpeiName = groupName;
12021 grpeiName += "_internalEdges";
12022 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12025 MESSAGE("group not created " << grpeiName);
12028 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12030 // --- build downward connectivity
12032 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12033 meshDS->BuildDownWardConnectivity(true);
12034 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12036 // --- set of volumes detected inside
12038 std::set<int> setOfInsideVol;
12039 std::set<int> setOfVolToCheck;
12041 std::vector<gp_Pnt> gpnts;
12044 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12046 MESSAGE("group of nodes provided");
12047 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12048 while ( elemIt->more() )
12050 const SMDS_MeshElement* elem = elemIt->next();
12053 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12056 SMDS_MeshElement* vol = 0;
12057 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12058 while (volItr->more())
12060 vol = (SMDS_MeshElement*)volItr->next();
12061 setOfInsideVol.insert(vol->getVtkId());
12062 sgrp->Add(vol->GetID());
12066 else if (isNodeCoords)
12068 MESSAGE("list of nodes coordinates provided");
12071 while ( i < nodesCoords.size()-2 )
12073 double x = nodesCoords[i++];
12074 double y = nodesCoords[i++];
12075 double z = nodesCoords[i++];
12076 gp_Pnt p = gp_Pnt(x, y ,z);
12077 gpnts.push_back(p);
12078 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12082 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12084 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12085 TopTools_IndexedMapOfShape vertexMap;
12086 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12087 gp_Pnt p = gp_Pnt(0,0,0);
12088 if (vertexMap.Extent() < 1)
12091 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12093 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12094 p = BRep_Tool::Pnt(vertex);
12095 gpnts.push_back(p);
12096 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12100 if (gpnts.size() > 0)
12103 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12105 nodeId = startNode->GetID();
12106 MESSAGE("nodeId " << nodeId);
12108 double radius2 = radius*radius;
12109 MESSAGE("radius2 " << radius2);
12111 // --- volumes on start node
12113 setOfVolToCheck.clear();
12114 SMDS_MeshElement* startVol = 0;
12115 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12116 while (volItr->more())
12118 startVol = (SMDS_MeshElement*)volItr->next();
12119 setOfVolToCheck.insert(startVol->getVtkId());
12121 if (setOfVolToCheck.empty())
12123 MESSAGE("No volumes found");
12127 // --- starting with central volumes then their neighbors, check if they are inside
12128 // or outside the domain, until no more new neighbor volume is inside.
12129 // Fill the group of inside volumes
12131 std::map<int, double> mapOfNodeDistance2;
12132 mapOfNodeDistance2.clear();
12133 std::set<int> setOfOutsideVol;
12134 while (!setOfVolToCheck.empty())
12136 std::set<int>::iterator it = setOfVolToCheck.begin();
12138 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12139 bool volInside = false;
12140 vtkIdType npts = 0;
12141 vtkIdType* pts = 0;
12142 grid->GetCellPoints(vtkId, npts, pts);
12143 for (int i=0; i<npts; i++)
12145 double distance2 = 0;
12146 if (mapOfNodeDistance2.count(pts[i]))
12148 distance2 = mapOfNodeDistance2[pts[i]];
12149 MESSAGE("point " << pts[i] << " distance2 " << distance2);
12153 double *coords = grid->GetPoint(pts[i]);
12154 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12156 for ( size_t j = 0; j < gpnts.size(); j++ )
12158 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12159 if (d2 < distance2)
12162 if (distance2 < radius2)
12166 mapOfNodeDistance2[pts[i]] = distance2;
12167 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12169 if (distance2 < radius2)
12171 volInside = true; // one or more nodes inside the domain
12172 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12178 setOfInsideVol.insert(vtkId);
12179 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12180 int neighborsVtkIds[NBMAXNEIGHBORS];
12181 int downIds[NBMAXNEIGHBORS];
12182 unsigned char downTypes[NBMAXNEIGHBORS];
12183 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12184 for (int n = 0; n < nbNeighbors; n++)
12185 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12186 setOfVolToCheck.insert(neighborsVtkIds[n]);
12190 setOfOutsideVol.insert(vtkId);
12191 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12193 setOfVolToCheck.erase(vtkId);
12197 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12198 // If yes, add the volume to the inside set
12200 bool addedInside = true;
12201 std::set<int> setOfVolToReCheck;
12202 while (addedInside)
12204 MESSAGE(" --------------------------- re check");
12205 addedInside = false;
12206 std::set<int>::iterator itv = setOfInsideVol.begin();
12207 for (; itv != setOfInsideVol.end(); ++itv)
12210 int neighborsVtkIds[NBMAXNEIGHBORS];
12211 int downIds[NBMAXNEIGHBORS];
12212 unsigned char downTypes[NBMAXNEIGHBORS];
12213 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12214 for (int n = 0; n < nbNeighbors; n++)
12215 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12216 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12218 setOfVolToCheck = setOfVolToReCheck;
12219 setOfVolToReCheck.clear();
12220 while (!setOfVolToCheck.empty())
12222 std::set<int>::iterator it = setOfVolToCheck.begin();
12224 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12226 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12227 int countInside = 0;
12228 int neighborsVtkIds[NBMAXNEIGHBORS];
12229 int downIds[NBMAXNEIGHBORS];
12230 unsigned char downTypes[NBMAXNEIGHBORS];
12231 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12232 for (int n = 0; n < nbNeighbors; n++)
12233 if (setOfInsideVol.count(neighborsVtkIds[n]))
12235 MESSAGE("countInside " << countInside);
12236 if (countInside > 1)
12238 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12239 setOfInsideVol.insert(vtkId);
12240 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12241 addedInside = true;
12244 setOfVolToReCheck.insert(vtkId);
12246 setOfVolToCheck.erase(vtkId);
12250 // --- map of Downward faces at the boundary, inside the global volume
12251 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12252 // fill group of SMDS faces inside the volume (when several volume shapes)
12253 // fill group of SMDS faces on the skin of the global volume (if skin)
12255 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12256 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12257 std::set<int>::iterator it = setOfInsideVol.begin();
12258 for (; it != setOfInsideVol.end(); ++it)
12261 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12262 int neighborsVtkIds[NBMAXNEIGHBORS];
12263 int downIds[NBMAXNEIGHBORS];
12264 unsigned char downTypes[NBMAXNEIGHBORS];
12265 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12266 for (int n = 0; n < nbNeighbors; n++)
12268 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12269 if (neighborDim == 3)
12271 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12273 DownIdType face(downIds[n], downTypes[n]);
12274 boundaryFaces[face] = vtkId;
12276 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12277 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12278 if (vtkFaceId >= 0)
12280 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12281 // find also the smds edges on this face
12282 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12283 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12284 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12285 for (int i = 0; i < nbEdges; i++)
12287 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12288 if (vtkEdgeId >= 0)
12289 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12293 else if (neighborDim == 2) // skin of the volume
12295 DownIdType face(downIds[n], downTypes[n]);
12296 skinFaces[face] = vtkId;
12297 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12298 if (vtkFaceId >= 0)
12299 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12304 // --- identify the edges constituting the wire of each subshape on the skin
12305 // define polylines with the nodes of edges, equivalent to wires
12306 // project polylines on subshapes, and partition, to get geom faces
12308 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12309 std::set<int> emptySet;
12311 std::set<int> shapeIds;
12313 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12314 while (itelem->more())
12316 const SMDS_MeshElement *elem = itelem->next();
12317 int shapeId = elem->getshapeId();
12318 int vtkId = elem->getVtkId();
12319 if (!shapeIdToVtkIdSet.count(shapeId))
12321 shapeIdToVtkIdSet[shapeId] = emptySet;
12322 shapeIds.insert(shapeId);
12324 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12327 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12328 std::set<DownIdType, DownIdCompare> emptyEdges;
12329 emptyEdges.clear();
12331 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12332 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12334 int shapeId = itShape->first;
12335 MESSAGE(" --- Shape ID --- "<< shapeId);
12336 shapeIdToEdges[shapeId] = emptyEdges;
12338 std::vector<int> nodesEdges;
12340 std::set<int>::iterator its = itShape->second.begin();
12341 for (; its != itShape->second.end(); ++its)
12344 MESSAGE(" " << vtkId);
12345 int neighborsVtkIds[NBMAXNEIGHBORS];
12346 int downIds[NBMAXNEIGHBORS];
12347 unsigned char downTypes[NBMAXNEIGHBORS];
12348 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12349 for (int n = 0; n < nbNeighbors; n++)
12351 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12353 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12354 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12355 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12357 DownIdType edge(downIds[n], downTypes[n]);
12358 if (!shapeIdToEdges[shapeId].count(edge))
12360 shapeIdToEdges[shapeId].insert(edge);
12362 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12363 nodesEdges.push_back(vtkNodeId[0]);
12364 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12365 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12371 std::list<int> order;
12373 if (nodesEdges.size() > 0)
12375 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12376 nodesEdges[0] = -1;
12377 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12378 nodesEdges[1] = -1; // do not reuse this edge
12382 int nodeTofind = order.back(); // try first to push back
12384 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12385 if (nodesEdges[i] == nodeTofind)
12387 if ( i == (int) nodesEdges.size() )
12388 found = false; // no follower found on back
12391 if (i%2) // odd ==> use the previous one
12392 if (nodesEdges[i-1] < 0)
12396 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12397 nodesEdges[i-1] = -1;
12399 else // even ==> use the next one
12400 if (nodesEdges[i+1] < 0)
12404 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12405 nodesEdges[i+1] = -1;
12410 // try to push front
12412 nodeTofind = order.front(); // try to push front
12413 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12414 if ( nodesEdges[i] == nodeTofind )
12416 if ( i == (int)nodesEdges.size() )
12418 found = false; // no predecessor found on front
12421 if (i%2) // odd ==> use the previous one
12422 if (nodesEdges[i-1] < 0)
12426 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12427 nodesEdges[i-1] = -1;
12429 else // even ==> use the next one
12430 if (nodesEdges[i+1] < 0)
12434 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12435 nodesEdges[i+1] = -1;
12441 std::vector<int> nodes;
12442 nodes.push_back(shapeId);
12443 std::list<int>::iterator itl = order.begin();
12444 for (; itl != order.end(); itl++)
12446 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12447 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12449 listOfListOfNodes.push_back(nodes);
12452 // partition geom faces with blocFissure
12453 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12454 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12460 //================================================================================
12462 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12463 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12464 * \return TRUE if operation has been completed successfully, FALSE otherwise
12466 //================================================================================
12468 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12470 // iterates on volume elements and detect all free faces on them
12471 SMESHDS_Mesh* aMesh = GetMeshDS();
12475 ElemFeatures faceType( SMDSAbs_Face );
12476 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12477 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12480 const SMDS_MeshVolume* volume = vIt->next();
12481 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12482 vTool.SetExternalNormal();
12483 const int iQuad = volume->IsQuadratic();
12484 faceType.SetQuad( iQuad );
12485 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12487 if (!vTool.IsFreeFace(iface))
12490 vector<const SMDS_MeshNode *> nodes;
12491 int nbFaceNodes = vTool.NbFaceNodes(iface);
12492 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12494 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12495 nodes.push_back(faceNodes[inode]);
12497 if (iQuad) // add medium nodes
12499 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12500 nodes.push_back(faceNodes[inode]);
12501 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12502 nodes.push_back(faceNodes[8]);
12504 // add new face based on volume nodes
12505 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12507 nbExisted++; // face already exsist
12511 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12516 return ( nbFree == ( nbExisted + nbCreated ));
12521 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12523 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12525 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12528 //================================================================================
12530 * \brief Creates missing boundary elements
12531 * \param elements - elements whose boundary is to be checked
12532 * \param dimension - defines type of boundary elements to create
12533 * \param group - a group to store created boundary elements in
12534 * \param targetMesh - a mesh to store created boundary elements in
12535 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12536 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12537 * boundary elements will be copied into the targetMesh
12538 * \param toAddExistingBondary - if true, not only new but also pre-existing
12539 * boundary elements will be added into the new group
12540 * \param aroundElements - if true, elements will be created on boundary of given
12541 * elements else, on boundary of the whole mesh.
12542 * \return nb of added boundary elements
12544 //================================================================================
12546 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12547 Bnd_Dimension dimension,
12548 SMESH_Group* group/*=0*/,
12549 SMESH_Mesh* targetMesh/*=0*/,
12550 bool toCopyElements/*=false*/,
12551 bool toCopyExistingBoundary/*=false*/,
12552 bool toAddExistingBondary/*= false*/,
12553 bool aroundElements/*= false*/)
12555 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12556 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12557 // hope that all elements are of the same type, do not check them all
12558 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12559 throw SALOME_Exception(LOCALIZED("wrong element type"));
12562 toCopyElements = toCopyExistingBoundary = false;
12564 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12565 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12566 int nbAddedBnd = 0;
12568 // editor adding present bnd elements and optionally holding elements to add to the group
12569 SMESH_MeshEditor* presentEditor;
12570 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12571 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12573 SMESH_MesherHelper helper( *myMesh );
12574 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12575 SMDS_VolumeTool vTool;
12576 TIDSortedElemSet avoidSet;
12577 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12580 typedef vector<const SMDS_MeshNode*> TConnectivity;
12581 TConnectivity tgtNodes;
12582 ElemFeatures elemKind( missType ), elemToCopy;
12584 vector<const SMDS_MeshElement*> presentBndElems;
12585 vector<TConnectivity> missingBndElems;
12586 vector<int> freeFacets;
12587 TConnectivity nodes, elemNodes;
12589 SMDS_ElemIteratorPtr eIt;
12590 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12591 else eIt = elemSetIterator( elements );
12593 while (eIt->more())
12595 const SMDS_MeshElement* elem = eIt->next();
12596 const int iQuad = elem->IsQuadratic();
12597 elemKind.SetQuad( iQuad );
12599 // ------------------------------------------------------------------------------------
12600 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12601 // ------------------------------------------------------------------------------------
12602 presentBndElems.clear();
12603 missingBndElems.clear();
12604 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12605 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12607 const SMDS_MeshElement* otherVol = 0;
12608 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12610 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12611 ( !aroundElements || elements.count( otherVol )))
12613 freeFacets.push_back( iface );
12615 if ( missType == SMDSAbs_Face )
12616 vTool.SetExternalNormal();
12617 for ( size_t i = 0; i < freeFacets.size(); ++i )
12619 int iface = freeFacets[i];
12620 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12621 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12622 if ( missType == SMDSAbs_Edge ) // boundary edges
12624 nodes.resize( 2+iQuad );
12625 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12627 for ( size_t j = 0; j < nodes.size(); ++j )
12628 nodes[ j ] = nn[ i+j ];
12629 if ( const SMDS_MeshElement* edge =
12630 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12631 presentBndElems.push_back( edge );
12633 missingBndElems.push_back( nodes );
12636 else // boundary face
12639 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12640 nodes.push_back( nn[inode] ); // add corner nodes
12642 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12643 nodes.push_back( nn[inode] ); // add medium nodes
12644 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12646 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12648 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12649 SMDSAbs_Face, /*noMedium=*/false ))
12650 presentBndElems.push_back( f );
12652 missingBndElems.push_back( nodes );
12654 if ( targetMesh != myMesh )
12656 // add 1D elements on face boundary to be added to a new mesh
12657 const SMDS_MeshElement* edge;
12658 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12661 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12663 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12664 if ( edge && avoidSet.insert( edge ).second )
12665 presentBndElems.push_back( edge );
12671 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12673 avoidSet.clear(), avoidSet.insert( elem );
12674 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12675 SMDS_MeshElement::iterator() );
12676 elemNodes.push_back( elemNodes[0] );
12677 nodes.resize( 2 + iQuad );
12678 const int nbLinks = elem->NbCornerNodes();
12679 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12681 nodes[0] = elemNodes[iN];
12682 nodes[1] = elemNodes[iN+1+iQuad];
12683 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12684 continue; // not free link
12686 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12687 if ( const SMDS_MeshElement* edge =
12688 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12689 presentBndElems.push_back( edge );
12691 missingBndElems.push_back( nodes );
12695 // ---------------------------------
12696 // 2. Add missing boundary elements
12697 // ---------------------------------
12698 if ( targetMesh != myMesh )
12699 // instead of making a map of nodes in this mesh and targetMesh,
12700 // we create nodes with same IDs.
12701 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12703 TConnectivity& srcNodes = missingBndElems[i];
12704 tgtNodes.resize( srcNodes.size() );
12705 for ( inode = 0; inode < srcNodes.size(); ++inode )
12706 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12707 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12709 /*noMedium=*/false))
12711 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12715 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12717 TConnectivity& nodes = missingBndElems[ i ];
12718 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12720 /*noMedium=*/false))
12722 SMDS_MeshElement* newElem =
12723 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12724 nbAddedBnd += bool( newElem );
12726 // try to set a new element to a shape
12727 if ( myMesh->HasShapeToMesh() )
12730 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12731 const size_t nbN = nodes.size() / (iQuad+1 );
12732 for ( inode = 0; inode < nbN && ok; ++inode )
12734 pair<int, TopAbs_ShapeEnum> i_stype =
12735 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12736 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12737 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12739 if ( ok && mediumShapes.size() > 1 )
12741 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12742 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12743 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12745 if (( ok = ( stype_i->first != stype_i_0.first )))
12746 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12747 aMesh->IndexToShape( stype_i_0.second ));
12750 if ( ok && mediumShapes.begin()->first == missShapeType )
12751 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12755 // ----------------------------------
12756 // 3. Copy present boundary elements
12757 // ----------------------------------
12758 if ( toCopyExistingBoundary )
12759 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12761 const SMDS_MeshElement* e = presentBndElems[i];
12762 tgtNodes.resize( e->NbNodes() );
12763 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12764 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12765 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12767 else // store present elements to add them to a group
12768 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12770 presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
12773 } // loop on given elements
12775 // ---------------------------------------------
12776 // 4. Fill group with boundary elements
12777 // ---------------------------------------------
12780 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12781 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12782 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12784 tgtEditor.myLastCreatedElems.Clear();
12785 tgtEditor2.myLastCreatedElems.Clear();
12787 // -----------------------
12788 // 5. Copy given elements
12789 // -----------------------
12790 if ( toCopyElements && targetMesh != myMesh )
12792 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12793 else eIt = elemSetIterator( elements );
12794 while (eIt->more())
12796 const SMDS_MeshElement* elem = eIt->next();
12797 tgtNodes.resize( elem->NbNodes() );
12798 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12799 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12800 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12802 tgtEditor.myLastCreatedElems.Clear();
12808 //================================================================================
12810 * \brief Copy node position and set \a to node on the same geometry
12812 //================================================================================
12814 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12815 const SMDS_MeshNode* to )
12817 if ( !from || !to ) return;
12819 SMDS_PositionPtr pos = from->GetPosition();
12820 if ( !pos || from->getshapeId() < 1 ) return;
12822 switch ( pos->GetTypeOfPosition() )
12824 case SMDS_TOP_3DSPACE: break;
12826 case SMDS_TOP_FACE:
12828 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12829 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12830 fPos->GetUParameter(), fPos->GetVParameter() );
12833 case SMDS_TOP_EDGE:
12835 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12836 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12837 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12840 case SMDS_TOP_VERTEX:
12842 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12845 case SMDS_TOP_UNSPEC: