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 [9]; aNodes[8] = 0;
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* centrNode = aNodes[8];
3005 if ( centrNode == 0 )
3007 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3008 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3010 myLastCreatedNodes.Append(centrNode);
3013 // create a new element
3014 const SMDS_MeshElement* newElem1 = 0;
3015 const SMDS_MeshElement* newElem2 = 0;
3017 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3018 aNodes[6], aNodes[7], centrNode );
3019 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3020 centrNode, aNodes[4], aNodes[5] );
3023 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3024 aNodes[7], aNodes[4], centrNode );
3025 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3026 centrNode, aNodes[5], aNodes[6] );
3028 myLastCreatedElems.Append(newElem1);
3029 myLastCreatedElems.Append(newElem2);
3030 // put a new triangle on the same shape and add to the same groups
3033 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3034 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3036 AddToSameGroups( newElem1, elem, aMesh );
3037 AddToSameGroups( newElem2, elem, aMesh );
3038 aMesh->RemoveElement( elem );
3045 //=======================================================================
3046 //function : getAngle
3048 //=======================================================================
3050 double getAngle(const SMDS_MeshElement * tr1,
3051 const SMDS_MeshElement * tr2,
3052 const SMDS_MeshNode * n1,
3053 const SMDS_MeshNode * n2)
3055 double angle = 2. * M_PI; // bad angle
3058 SMESH::Controls::TSequenceOfXYZ P1, P2;
3059 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3060 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3063 if(!tr1->IsQuadratic())
3064 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3066 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3067 if ( N1.SquareMagnitude() <= gp::Resolution() )
3069 if(!tr2->IsQuadratic())
3070 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3072 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3073 if ( N2.SquareMagnitude() <= gp::Resolution() )
3076 // find the first diagonal node n1 in the triangles:
3077 // take in account a diagonal link orientation
3078 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3079 for ( int t = 0; t < 2; t++ ) {
3080 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3081 int i = 0, iDiag = -1;
3082 while ( it->more()) {
3083 const SMDS_MeshElement *n = it->next();
3084 if ( n == n1 || n == n2 ) {
3088 if ( i - iDiag == 1 )
3089 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3098 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3101 angle = N1.Angle( N2 );
3106 // =================================================
3107 // class generating a unique ID for a pair of nodes
3108 // and able to return nodes by that ID
3109 // =================================================
3113 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3114 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3117 long GetLinkID (const SMDS_MeshNode * n1,
3118 const SMDS_MeshNode * n2) const
3120 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3123 bool GetNodes (const long theLinkID,
3124 const SMDS_MeshNode* & theNode1,
3125 const SMDS_MeshNode* & theNode2) const
3127 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3128 if ( !theNode1 ) return false;
3129 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3130 if ( !theNode2 ) return false;
3136 const SMESHDS_Mesh* myMesh;
3141 //=======================================================================
3142 //function : TriToQuad
3143 //purpose : Fuse neighbour triangles into quadrangles.
3144 // theCrit is used to select a neighbour to fuse with.
3145 // theMaxAngle is a max angle between element normals at which
3146 // fusion is still performed.
3147 //=======================================================================
3149 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3150 SMESH::Controls::NumericalFunctorPtr theCrit,
3151 const double theMaxAngle)
3153 myLastCreatedElems.Clear();
3154 myLastCreatedNodes.Clear();
3156 MESSAGE( "::TriToQuad()" );
3158 if ( !theCrit.get() )
3161 SMESHDS_Mesh * aMesh = GetMeshDS();
3163 // Prepare data for algo: build
3164 // 1. map of elements with their linkIDs
3165 // 2. map of linkIDs with their elements
3167 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3168 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3169 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3170 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3172 TIDSortedElemSet::iterator itElem;
3173 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3175 const SMDS_MeshElement* elem = *itElem;
3176 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3177 bool IsTria = ( elem->NbCornerNodes()==3 );
3178 if (!IsTria) continue;
3180 // retrieve element nodes
3181 const SMDS_MeshNode* aNodes [4];
3182 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3185 aNodes[ i++ ] = itN->next();
3186 aNodes[ 3 ] = aNodes[ 0 ];
3189 for ( i = 0; i < 3; i++ ) {
3190 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3191 // check if elements sharing a link can be fused
3192 itLE = mapLi_listEl.find( link );
3193 if ( itLE != mapLi_listEl.end() ) {
3194 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3196 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3197 //if ( FindShape( elem ) != FindShape( elem2 ))
3198 // continue; // do not fuse triangles laying on different shapes
3199 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3200 continue; // avoid making badly shaped quads
3201 (*itLE).second.push_back( elem );
3204 mapLi_listEl[ link ].push_back( elem );
3206 mapEl_setLi [ elem ].insert( link );
3209 // Clean the maps from the links shared by a sole element, ie
3210 // links to which only one element is bound in mapLi_listEl
3212 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3213 int nbElems = (*itLE).second.size();
3214 if ( nbElems < 2 ) {
3215 const SMDS_MeshElement* elem = (*itLE).second.front();
3216 SMESH_TLink link = (*itLE).first;
3217 mapEl_setLi[ elem ].erase( link );
3218 if ( mapEl_setLi[ elem ].empty() )
3219 mapEl_setLi.erase( elem );
3223 // Algo: fuse triangles into quadrangles
3225 while ( ! mapEl_setLi.empty() ) {
3226 // Look for the start element:
3227 // the element having the least nb of shared links
3228 const SMDS_MeshElement* startElem = 0;
3230 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3231 int nbLinks = (*itEL).second.size();
3232 if ( nbLinks < minNbLinks ) {
3233 startElem = (*itEL).first;
3234 minNbLinks = nbLinks;
3235 if ( minNbLinks == 1 )
3240 // search elements to fuse starting from startElem or links of elements
3241 // fused earlyer - startLinks
3242 list< SMESH_TLink > startLinks;
3243 while ( startElem || !startLinks.empty() ) {
3244 while ( !startElem && !startLinks.empty() ) {
3245 // Get an element to start, by a link
3246 SMESH_TLink linkId = startLinks.front();
3247 startLinks.pop_front();
3248 itLE = mapLi_listEl.find( linkId );
3249 if ( itLE != mapLi_listEl.end() ) {
3250 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3251 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3252 for ( ; itE != listElem.end() ; itE++ )
3253 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3255 mapLi_listEl.erase( itLE );
3260 // Get candidates to be fused
3261 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3262 const SMESH_TLink *link12, *link13;
3264 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3265 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3266 ASSERT( !setLi.empty() );
3267 set< SMESH_TLink >::iterator itLi;
3268 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3270 const SMESH_TLink & link = (*itLi);
3271 itLE = mapLi_listEl.find( link );
3272 if ( itLE == mapLi_listEl.end() )
3275 const SMDS_MeshElement* elem = (*itLE).second.front();
3277 elem = (*itLE).second.back();
3278 mapLi_listEl.erase( itLE );
3279 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3290 // add other links of elem to list of links to re-start from
3291 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3292 set< SMESH_TLink >::iterator it;
3293 for ( it = links.begin(); it != links.end(); it++ ) {
3294 const SMESH_TLink& link2 = (*it);
3295 if ( link2 != link )
3296 startLinks.push_back( link2 );
3300 // Get nodes of possible quadrangles
3301 const SMDS_MeshNode *n12 [4], *n13 [4];
3302 bool Ok12 = false, Ok13 = false;
3303 const SMDS_MeshNode *linkNode1, *linkNode2;
3305 linkNode1 = link12->first;
3306 linkNode2 = link12->second;
3307 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3311 linkNode1 = link13->first;
3312 linkNode2 = link13->second;
3313 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3317 // Choose a pair to fuse
3318 if ( Ok12 && Ok13 ) {
3319 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3320 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3321 double aBadRate12 = getBadRate( &quad12, theCrit );
3322 double aBadRate13 = getBadRate( &quad13, theCrit );
3323 if ( aBadRate13 < aBadRate12 )
3330 // and remove fused elems and remove links from the maps
3331 mapEl_setLi.erase( tr1 );
3334 mapEl_setLi.erase( tr2 );
3335 mapLi_listEl.erase( *link12 );
3336 if ( tr1->NbNodes() == 3 )
3338 const SMDS_MeshElement* newElem = 0;
3339 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3340 myLastCreatedElems.Append(newElem);
3341 AddToSameGroups( newElem, tr1, aMesh );
3342 int aShapeId = tr1->getshapeId();
3344 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3345 aMesh->RemoveElement( tr1 );
3346 aMesh->RemoveElement( tr2 );
3349 vector< const SMDS_MeshNode* > N1;
3350 vector< const SMDS_MeshNode* > N2;
3351 getNodesFromTwoTria(tr1,tr2,N1,N2);
3352 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3353 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3354 // i.e. first nodes from both arrays form a new diagonal
3355 const SMDS_MeshNode* aNodes[8];
3364 const SMDS_MeshElement* newElem = 0;
3365 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3366 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3367 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3369 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3370 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3371 myLastCreatedElems.Append(newElem);
3372 AddToSameGroups( newElem, tr1, aMesh );
3373 int aShapeId = tr1->getshapeId();
3375 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3376 aMesh->RemoveElement( tr1 );
3377 aMesh->RemoveElement( tr2 );
3378 // remove middle node (9)
3379 if ( N1[4]->NbInverseElements() == 0 )
3380 aMesh->RemoveNode( N1[4] );
3381 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3382 aMesh->RemoveNode( N1[6] );
3383 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3384 aMesh->RemoveNode( N2[6] );
3389 mapEl_setLi.erase( tr3 );
3390 mapLi_listEl.erase( *link13 );
3391 if ( tr1->NbNodes() == 3 ) {
3392 const SMDS_MeshElement* newElem = 0;
3393 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3394 myLastCreatedElems.Append(newElem);
3395 AddToSameGroups( newElem, tr1, aMesh );
3396 int aShapeId = tr1->getshapeId();
3398 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3399 aMesh->RemoveElement( tr1 );
3400 aMesh->RemoveElement( tr3 );
3403 vector< const SMDS_MeshNode* > N1;
3404 vector< const SMDS_MeshNode* > N2;
3405 getNodesFromTwoTria(tr1,tr3,N1,N2);
3406 // now we receive following N1 and N2 (using numeration as above image)
3407 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3408 // i.e. first nodes from both arrays form a new diagonal
3409 const SMDS_MeshNode* aNodes[8];
3418 const SMDS_MeshElement* newElem = 0;
3419 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3420 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3421 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3423 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3424 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3425 myLastCreatedElems.Append(newElem);
3426 AddToSameGroups( newElem, tr1, aMesh );
3427 int aShapeId = tr1->getshapeId();
3429 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3430 aMesh->RemoveElement( tr1 );
3431 aMesh->RemoveElement( tr3 );
3432 // remove middle node (9)
3433 if ( N1[4]->NbInverseElements() == 0 )
3434 aMesh->RemoveNode( N1[4] );
3435 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3436 aMesh->RemoveNode( N1[6] );
3437 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3438 aMesh->RemoveNode( N2[6] );
3442 // Next element to fuse: the rejected one
3444 startElem = Ok12 ? tr3 : tr2;
3446 } // if ( startElem )
3447 } // while ( startElem || !startLinks.empty() )
3448 } // while ( ! mapEl_setLi.empty() )
3454 /*#define DUMPSO(txt) \
3455 // cout << txt << endl;
3456 //=============================================================================
3460 //=============================================================================
3461 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3465 int tmp = idNodes[ i1 ];
3466 idNodes[ i1 ] = idNodes[ i2 ];
3467 idNodes[ i2 ] = tmp;
3468 gp_Pnt Ptmp = P[ i1 ];
3471 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3474 //=======================================================================
3475 //function : SortQuadNodes
3476 //purpose : Set 4 nodes of a quadrangle face in a good order.
3477 // Swap 1<->2 or 2<->3 nodes and correspondingly return
3479 //=======================================================================
3481 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3486 for ( i = 0; i < 4; i++ ) {
3487 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3489 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3492 gp_Vec V1(P[0], P[1]);
3493 gp_Vec V2(P[0], P[2]);
3494 gp_Vec V3(P[0], P[3]);
3496 gp_Vec Cross1 = V1 ^ V2;
3497 gp_Vec Cross2 = V2 ^ V3;
3500 if (Cross1.Dot(Cross2) < 0)
3505 if (Cross1.Dot(Cross2) < 0)
3509 swap ( i, i + 1, idNodes, P );
3511 // for ( int ii = 0; ii < 4; ii++ ) {
3512 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3513 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3519 //=======================================================================
3520 //function : SortHexaNodes
3521 //purpose : Set 8 nodes of a hexahedron in a good order.
3522 // Return success status
3523 //=======================================================================
3525 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3530 DUMPSO( "INPUT: ========================================");
3531 for ( i = 0; i < 8; i++ ) {
3532 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3533 if ( !n ) return false;
3534 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3535 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3537 DUMPSO( "========================================");
3540 set<int> faceNodes; // ids of bottom face nodes, to be found
3541 set<int> checkedId1; // ids of tried 2-nd nodes
3542 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3543 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
3544 int iMin, iLoop1 = 0;
3546 // Loop to try the 2-nd nodes
3548 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3550 // Find not checked 2-nd node
3551 for ( i = 1; i < 8; i++ )
3552 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3553 int id1 = idNodes[i];
3554 swap ( 1, i, idNodes, P );
3555 checkedId1.insert ( id1 );
3559 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3560 // ie that all but meybe one (id3 which is on the same face) nodes
3561 // lay on the same side from the triangle plane.
3563 bool manyInPlane = false; // more than 4 nodes lay in plane
3565 while ( ++iLoop2 < 6 ) {
3567 // get 1-2-3 plane coeffs
3568 Standard_Real A, B, C, D;
3569 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3570 if ( N.SquareMagnitude() > gp::Resolution() )
3572 gp_Pln pln ( P[0], N );
3573 pln.Coefficients( A, B, C, D );
3575 // find the node (iMin) closest to pln
3576 Standard_Real dist[ 8 ], minDist = DBL_MAX;
3578 for ( i = 3; i < 8; i++ ) {
3579 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3580 if ( fabs( dist[i] ) < minDist ) {
3581 minDist = fabs( dist[i] );
3584 if ( fabs( dist[i] ) <= tol )
3585 idInPln.insert( idNodes[i] );
3588 // there should not be more than 4 nodes in bottom plane
3589 if ( idInPln.size() > 1 )
3591 DUMPSO( "### idInPln.size() = " << idInPln.size());
3592 // idInPlane does not contain the first 3 nodes
3593 if ( manyInPlane || idInPln.size() == 5)
3594 return false; // all nodes in one plane
3597 // set the 1-st node to be not in plane
3598 for ( i = 3; i < 8; i++ ) {
3599 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3600 DUMPSO( "### Reset 0-th node");
3601 swap( 0, i, idNodes, P );
3606 // reset to re-check second nodes
3607 leastDist = DBL_MAX;
3611 break; // from iLoop2;
3614 // check that the other 4 nodes are on the same side
3615 bool sameSide = true;
3616 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3617 for ( i = 3; sameSide && i < 8; i++ ) {
3619 sameSide = ( isNeg == dist[i] <= 0.);
3622 // keep best solution
3623 if ( sameSide && minDist < leastDist ) {
3624 leastDist = minDist;
3626 faceNodes.insert( idNodes[ 1 ] );
3627 faceNodes.insert( idNodes[ 2 ] );
3628 faceNodes.insert( idNodes[ iMin ] );
3629 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3630 << " leastDist = " << leastDist);
3631 if ( leastDist <= DBL_MIN )
3636 // set next 3-d node to check
3637 int iNext = 2 + iLoop2;
3639 DUMPSO( "Try 2-nd");
3640 swap ( 2, iNext, idNodes, P );
3642 } // while ( iLoop2 < 6 )
3645 if ( faceNodes.empty() ) return false;
3647 // Put the faceNodes in proper places
3648 for ( i = 4; i < 8; i++ ) {
3649 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3650 // find a place to put
3652 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3654 DUMPSO( "Set faceNodes");
3655 swap ( iTo, i, idNodes, P );
3660 // Set nodes of the found bottom face in good order
3661 DUMPSO( " Found bottom face: ");
3662 i = SortQuadNodes( theMesh, idNodes );
3664 gp_Pnt Ptmp = P[ i ];
3669 // for ( int ii = 0; ii < 4; ii++ ) {
3670 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3671 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3674 // Gravity center of the top and bottom faces
3675 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3676 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3678 // Get direction from the bottom to the top face
3679 gp_Vec upDir ( aGCb, aGCt );
3680 Standard_Real upDirSize = upDir.Magnitude();
3681 if ( upDirSize <= gp::Resolution() ) return false;
3684 // Assure that the bottom face normal points up
3685 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3686 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3687 if ( Nb.Dot( upDir ) < 0 ) {
3688 DUMPSO( "Reverse bottom face");
3689 swap( 1, 3, idNodes, P );
3692 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3693 Standard_Real minDist = DBL_MAX;
3694 for ( i = 4; i < 8; i++ ) {
3695 // projection of P[i] to the plane defined by P[0] and upDir
3696 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3697 Standard_Real sqDist = P[0].SquareDistance( Pp );
3698 if ( sqDist < minDist ) {
3703 DUMPSO( "Set 4-th");
3704 swap ( 4, iMin, idNodes, P );
3706 // Set nodes of the top face in good order
3707 DUMPSO( "Sort top face");
3708 i = SortQuadNodes( theMesh, &idNodes[4] );
3711 gp_Pnt Ptmp = P[ i ];
3716 // Assure that direction of the top face normal is from the bottom face
3717 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3718 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3719 if ( Nt.Dot( upDir ) < 0 ) {
3720 DUMPSO( "Reverse top face");
3721 swap( 5, 7, idNodes, P );
3724 // DUMPSO( "OUTPUT: ========================================");
3725 // for ( i = 0; i < 8; i++ ) {
3726 // float *p = ugrid->GetPoint(idNodes[i]);
3727 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3733 //================================================================================
3735 * \brief Return nodes linked to the given one
3736 * \param theNode - the node
3737 * \param linkedNodes - the found nodes
3738 * \param type - the type of elements to check
3740 * Medium nodes are ignored
3742 //================================================================================
3744 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3745 TIDSortedElemSet & linkedNodes,
3746 SMDSAbs_ElementType type )
3748 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3749 while ( elemIt->more() )
3751 const SMDS_MeshElement* elem = elemIt->next();
3752 if(elem->GetType() == SMDSAbs_0DElement)
3755 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3756 if ( elem->GetType() == SMDSAbs_Volume )
3758 SMDS_VolumeTool vol( elem );
3759 while ( nodeIt->more() ) {
3760 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3761 if ( theNode != n && vol.IsLinked( theNode, n ))
3762 linkedNodes.insert( n );
3767 for ( int i = 0; nodeIt->more(); ++i ) {
3768 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3769 if ( n == theNode ) {
3770 int iBefore = i - 1;
3772 if ( elem->IsQuadratic() ) {
3773 int nb = elem->NbNodes() / 2;
3774 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3775 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3777 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3778 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3785 //=======================================================================
3786 //function : laplacianSmooth
3787 //purpose : pulls theNode toward the center of surrounding nodes directly
3788 // connected to that node along an element edge
3789 //=======================================================================
3791 void laplacianSmooth(const SMDS_MeshNode* theNode,
3792 const Handle(Geom_Surface)& theSurface,
3793 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3795 // find surrounding nodes
3797 TIDSortedElemSet nodeSet;
3798 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3800 // compute new coodrs
3802 double coord[] = { 0., 0., 0. };
3803 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3804 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3805 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3806 if ( theSurface.IsNull() ) { // smooth in 3D
3807 coord[0] += node->X();
3808 coord[1] += node->Y();
3809 coord[2] += node->Z();
3811 else { // smooth in 2D
3812 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3813 gp_XY* uv = theUVMap[ node ];
3814 coord[0] += uv->X();
3815 coord[1] += uv->Y();
3818 int nbNodes = nodeSet.size();
3821 coord[0] /= nbNodes;
3822 coord[1] /= nbNodes;
3824 if ( !theSurface.IsNull() ) {
3825 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3826 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3827 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3833 coord[2] /= nbNodes;
3837 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3840 //=======================================================================
3841 //function : centroidalSmooth
3842 //purpose : pulls theNode toward the element-area-weighted centroid of the
3843 // surrounding elements
3844 //=======================================================================
3846 void centroidalSmooth(const SMDS_MeshNode* theNode,
3847 const Handle(Geom_Surface)& theSurface,
3848 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3850 gp_XYZ aNewXYZ(0.,0.,0.);
3851 SMESH::Controls::Area anAreaFunc;
3852 double totalArea = 0.;
3857 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3858 while ( elemIt->more() )
3860 const SMDS_MeshElement* elem = elemIt->next();
3863 gp_XYZ elemCenter(0.,0.,0.);
3864 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3865 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3866 int nn = elem->NbNodes();
3867 if(elem->IsQuadratic()) nn = nn/2;
3869 //while ( itN->more() ) {
3871 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3873 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3874 aNodePoints.push_back( aP );
3875 if ( !theSurface.IsNull() ) { // smooth in 2D
3876 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3877 gp_XY* uv = theUVMap[ aNode ];
3878 aP.SetCoord( uv->X(), uv->Y(), 0. );
3882 double elemArea = anAreaFunc.GetValue( aNodePoints );
3883 totalArea += elemArea;
3885 aNewXYZ += elemCenter * elemArea;
3887 aNewXYZ /= totalArea;
3888 if ( !theSurface.IsNull() ) {
3889 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3890 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3895 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3898 //=======================================================================
3899 //function : getClosestUV
3900 //purpose : return UV of closest projection
3901 //=======================================================================
3903 static bool getClosestUV (Extrema_GenExtPS& projector,
3904 const gp_Pnt& point,
3907 projector.Perform( point );
3908 if ( projector.IsDone() ) {
3909 double u, v, minVal = DBL_MAX;
3910 for ( int i = projector.NbExt(); i > 0; i-- )
3911 if ( projector.SquareDistance( i ) < minVal ) {
3912 minVal = projector.SquareDistance( i );
3913 projector.Point( i ).Parameter( u, v );
3915 result.SetCoord( u, v );
3921 //=======================================================================
3923 //purpose : Smooth theElements during theNbIterations or until a worst
3924 // element has aspect ratio <= theTgtAspectRatio.
3925 // Aspect Ratio varies in range [1.0, inf].
3926 // If theElements is empty, the whole mesh is smoothed.
3927 // theFixedNodes contains additionally fixed nodes. Nodes built
3928 // on edges and boundary nodes are always fixed.
3929 //=======================================================================
3931 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3932 set<const SMDS_MeshNode*> & theFixedNodes,
3933 const SmoothMethod theSmoothMethod,
3934 const int theNbIterations,
3935 double theTgtAspectRatio,
3938 myLastCreatedElems.Clear();
3939 myLastCreatedNodes.Clear();
3941 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3943 if ( theTgtAspectRatio < 1.0 )
3944 theTgtAspectRatio = 1.0;
3946 const double disttol = 1.e-16;
3948 SMESH::Controls::AspectRatio aQualityFunc;
3950 SMESHDS_Mesh* aMesh = GetMeshDS();
3952 if ( theElems.empty() ) {
3953 // add all faces to theElems
3954 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3955 while ( fIt->more() ) {
3956 const SMDS_MeshElement* face = fIt->next();
3957 theElems.insert( theElems.end(), face );
3960 // get all face ids theElems are on
3961 set< int > faceIdSet;
3962 TIDSortedElemSet::iterator itElem;
3964 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3965 int fId = FindShape( *itElem );
3966 // check that corresponding submesh exists and a shape is face
3968 faceIdSet.find( fId ) == faceIdSet.end() &&
3969 aMesh->MeshElements( fId )) {
3970 TopoDS_Shape F = aMesh->IndexToShape( fId );
3971 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3972 faceIdSet.insert( fId );
3975 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3977 // ===============================================
3978 // smooth elements on each TopoDS_Face separately
3979 // ===============================================
3981 SMESH_MesherHelper helper( *GetMesh() );
3983 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3984 for ( ; fId != faceIdSet.rend(); ++fId )
3986 // get face surface and submesh
3987 Handle(Geom_Surface) surface;
3988 SMESHDS_SubMesh* faceSubMesh = 0;
3991 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3992 bool isUPeriodic = false, isVPeriodic = false;
3995 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3996 surface = BRep_Tool::Surface( face );
3997 faceSubMesh = aMesh->MeshElements( *fId );
3998 fToler2 = BRep_Tool::Tolerance( face );
3999 fToler2 *= fToler2 * 10.;
4000 isUPeriodic = surface->IsUPeriodic();
4003 isVPeriodic = surface->IsVPeriodic();
4006 surface->Bounds( u1, u2, v1, v2 );
4007 helper.SetSubShape( face );
4009 // ---------------------------------------------------------
4010 // for elements on a face, find movable and fixed nodes and
4011 // compute UV for them
4012 // ---------------------------------------------------------
4013 bool checkBoundaryNodes = false;
4014 bool isQuadratic = false;
4015 set<const SMDS_MeshNode*> setMovableNodes;
4016 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4017 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4018 list< const SMDS_MeshElement* > elemsOnFace;
4020 Extrema_GenExtPS projector;
4021 GeomAdaptor_Surface surfAdaptor;
4022 if ( !surface.IsNull() ) {
4023 surfAdaptor.Load( surface );
4024 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4026 int nbElemOnFace = 0;
4027 itElem = theElems.begin();
4028 // loop on not yet smoothed elements: look for elems on a face
4029 while ( itElem != theElems.end() )
4031 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4032 break; // all elements found
4034 const SMDS_MeshElement* elem = *itElem;
4035 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4036 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4040 elemsOnFace.push_back( elem );
4041 theElems.erase( itElem++ );
4045 isQuadratic = elem->IsQuadratic();
4047 // get movable nodes of elem
4048 const SMDS_MeshNode* node;
4049 SMDS_TypeOfPosition posType;
4050 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4051 int nn = 0, nbn = elem->NbNodes();
4052 if(elem->IsQuadratic())
4054 while ( nn++ < nbn ) {
4055 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4056 const SMDS_PositionPtr& pos = node->GetPosition();
4057 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4058 if (posType != SMDS_TOP_EDGE &&
4059 posType != SMDS_TOP_VERTEX &&
4060 theFixedNodes.find( node ) == theFixedNodes.end())
4062 // check if all faces around the node are on faceSubMesh
4063 // because a node on edge may be bound to face
4064 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4066 if ( faceSubMesh ) {
4067 while ( eIt->more() && all ) {
4068 const SMDS_MeshElement* e = eIt->next();
4069 all = faceSubMesh->Contains( e );
4073 setMovableNodes.insert( node );
4075 checkBoundaryNodes = true;
4077 if ( posType == SMDS_TOP_3DSPACE )
4078 checkBoundaryNodes = true;
4081 if ( surface.IsNull() )
4084 // get nodes to check UV
4085 list< const SMDS_MeshNode* > uvCheckNodes;
4086 const SMDS_MeshNode* nodeInFace = 0;
4087 itN = elem->nodesIterator();
4088 nn = 0; nbn = elem->NbNodes();
4089 if(elem->IsQuadratic())
4091 while ( nn++ < nbn ) {
4092 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4093 if ( node->GetPosition()->GetDim() == 2 )
4095 if ( uvMap.find( node ) == uvMap.end() )
4096 uvCheckNodes.push_back( node );
4097 // add nodes of elems sharing node
4098 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4099 // while ( eIt->more() ) {
4100 // const SMDS_MeshElement* e = eIt->next();
4101 // if ( e != elem ) {
4102 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4103 // while ( nIt->more() ) {
4104 // const SMDS_MeshNode* n =
4105 // static_cast<const SMDS_MeshNode*>( nIt->next() );
4106 // if ( uvMap.find( n ) == uvMap.end() )
4107 // uvCheckNodes.push_back( n );
4113 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4114 for ( ; n != uvCheckNodes.end(); ++n ) {
4117 const SMDS_PositionPtr& pos = node->GetPosition();
4118 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4122 bool toCheck = true;
4123 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4125 // compute not existing UV
4126 bool project = ( posType == SMDS_TOP_3DSPACE );
4127 // double dist1 = DBL_MAX, dist2 = 0;
4128 // if ( posType != SMDS_TOP_3DSPACE ) {
4129 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4130 // project = dist1 > fToler2;
4132 if ( project ) { // compute new UV
4134 gp_Pnt pNode = SMESH_TNodeXYZ( node );
4135 if ( !getClosestUV( projector, pNode, newUV )) {
4136 MESSAGE("Node Projection Failed " << node);
4140 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4142 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4144 // if ( posType != SMDS_TOP_3DSPACE )
4145 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4146 // if ( dist2 < dist1 )
4150 // store UV in the map
4151 listUV.push_back( uv );
4152 uvMap.insert( make_pair( node, &listUV.back() ));
4154 } // loop on not yet smoothed elements
4156 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4157 checkBoundaryNodes = true;
4159 // fix nodes on mesh boundary
4161 if ( checkBoundaryNodes ) {
4162 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4163 map< SMESH_TLink, int >::iterator link_nb;
4164 // put all elements links to linkNbMap
4165 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4166 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4167 const SMDS_MeshElement* elem = (*elemIt);
4168 int nbn = elem->NbCornerNodes();
4169 // loop on elem links: insert them in linkNbMap
4170 for ( int iN = 0; iN < nbn; ++iN ) {
4171 const SMDS_MeshNode* n1 = elem->GetNode( iN );
4172 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4173 SMESH_TLink link( n1, n2 );
4174 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4178 // remove nodes that are in links encountered only once from setMovableNodes
4179 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4180 if ( link_nb->second == 1 ) {
4181 setMovableNodes.erase( link_nb->first.node1() );
4182 setMovableNodes.erase( link_nb->first.node2() );
4187 // -----------------------------------------------------
4188 // for nodes on seam edge, compute one more UV ( uvMap2 );
4189 // find movable nodes linked to nodes on seam and which
4190 // are to be smoothed using the second UV ( uvMap2 )
4191 // -----------------------------------------------------
4193 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4194 if ( !surface.IsNull() ) {
4195 TopExp_Explorer eExp( face, TopAbs_EDGE );
4196 for ( ; eExp.More(); eExp.Next() ) {
4197 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4198 if ( !BRep_Tool::IsClosed( edge, face ))
4200 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4201 if ( !sm ) continue;
4202 // find out which parameter varies for a node on seam
4205 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4206 if ( pcurve.IsNull() ) continue;
4207 uv1 = pcurve->Value( f );
4209 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4210 if ( pcurve.IsNull() ) continue;
4211 uv2 = pcurve->Value( f );
4212 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4214 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4215 std::swap( uv1, uv2 );
4216 // get nodes on seam and its vertices
4217 list< const SMDS_MeshNode* > seamNodes;
4218 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4219 while ( nSeamIt->more() ) {
4220 const SMDS_MeshNode* node = nSeamIt->next();
4221 if ( !isQuadratic || !IsMedium( node ))
4222 seamNodes.push_back( node );
4224 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4225 for ( ; vExp.More(); vExp.Next() ) {
4226 sm = aMesh->MeshElements( vExp.Current() );
4228 nSeamIt = sm->GetNodes();
4229 while ( nSeamIt->more() )
4230 seamNodes.push_back( nSeamIt->next() );
4233 // loop on nodes on seam
4234 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4235 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4236 const SMDS_MeshNode* nSeam = *noSeIt;
4237 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4238 if ( n_uv == uvMap.end() )
4241 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4242 // set the second UV
4243 listUV.push_back( *n_uv->second );
4244 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4245 if ( uvMap2.empty() )
4246 uvMap2 = uvMap; // copy the uvMap contents
4247 uvMap2[ nSeam ] = &listUV.back();
4249 // collect movable nodes linked to ones on seam in nodesNearSeam
4250 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4251 while ( eIt->more() ) {
4252 const SMDS_MeshElement* e = eIt->next();
4253 int nbUseMap1 = 0, nbUseMap2 = 0;
4254 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4255 int nn = 0, nbn = e->NbNodes();
4256 if(e->IsQuadratic()) nbn = nbn/2;
4257 while ( nn++ < nbn )
4259 const SMDS_MeshNode* n =
4260 static_cast<const SMDS_MeshNode*>( nIt->next() );
4262 setMovableNodes.find( n ) == setMovableNodes.end() )
4264 // add only nodes being closer to uv2 than to uv1
4265 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4266 // 0.5 * ( n->Y() + nSeam->Y() ),
4267 // 0.5 * ( n->Z() + nSeam->Z() ));
4269 // getClosestUV( projector, pMid, uv );
4270 double x = uvMap[ n ]->Coord( iPar );
4271 if ( Abs( uv1.Coord( iPar ) - x ) >
4272 Abs( uv2.Coord( iPar ) - x )) {
4273 nodesNearSeam.insert( n );
4279 // for centroidalSmooth all element nodes must
4280 // be on one side of a seam
4281 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4282 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4284 while ( nn++ < nbn ) {
4285 const SMDS_MeshNode* n =
4286 static_cast<const SMDS_MeshNode*>( nIt->next() );
4287 setMovableNodes.erase( n );
4291 } // loop on nodes on seam
4292 } // loop on edge of a face
4293 } // if ( !face.IsNull() )
4295 if ( setMovableNodes.empty() ) {
4296 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4297 continue; // goto next face
4305 double maxRatio = -1., maxDisplacement = -1.;
4306 set<const SMDS_MeshNode*>::iterator nodeToMove;
4307 for ( it = 0; it < theNbIterations; it++ ) {
4308 maxDisplacement = 0.;
4309 nodeToMove = setMovableNodes.begin();
4310 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4311 const SMDS_MeshNode* node = (*nodeToMove);
4312 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4315 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4316 if ( theSmoothMethod == LAPLACIAN )
4317 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4319 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4321 // node displacement
4322 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4323 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4324 if ( aDispl > maxDisplacement )
4325 maxDisplacement = aDispl;
4327 // no node movement => exit
4328 //if ( maxDisplacement < 1.e-16 ) {
4329 if ( maxDisplacement < disttol ) {
4330 MESSAGE("-- no node movement --");
4334 // check elements quality
4336 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4337 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4338 const SMDS_MeshElement* elem = (*elemIt);
4339 if ( !elem || elem->GetType() != SMDSAbs_Face )
4341 SMESH::Controls::TSequenceOfXYZ aPoints;
4342 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4343 double aValue = aQualityFunc.GetValue( aPoints );
4344 if ( aValue > maxRatio )
4348 if ( maxRatio <= theTgtAspectRatio ) {
4349 MESSAGE("-- quality achived --");
4352 if (it+1 == theNbIterations) {
4353 MESSAGE("-- Iteration limit exceeded --");
4355 } // smoothing iterations
4357 MESSAGE(" Face id: " << *fId <<
4358 " Nb iterstions: " << it <<
4359 " Displacement: " << maxDisplacement <<
4360 " Aspect Ratio " << maxRatio);
4362 // ---------------------------------------
4363 // new nodes positions are computed,
4364 // record movement in DS and set new UV
4365 // ---------------------------------------
4366 nodeToMove = setMovableNodes.begin();
4367 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4368 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4369 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4370 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4371 if ( node_uv != uvMap.end() ) {
4372 gp_XY* uv = node_uv->second;
4374 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4378 // move medium nodes of quadratic elements
4381 vector<const SMDS_MeshNode*> nodes;
4383 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4384 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4386 const SMDS_MeshElement* QF = *elemIt;
4387 if ( QF->IsQuadratic() )
4389 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4390 SMDS_MeshElement::iterator() );
4391 nodes.push_back( nodes[0] );
4393 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4395 if ( !surface.IsNull() )
4397 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4398 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4399 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4400 xyz = surface->Value( uv.X(), uv.Y() );
4403 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4405 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4406 // we have to move a medium node
4407 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4413 } // loop on face ids
4419 //=======================================================================
4420 //function : isReverse
4421 //purpose : Return true if normal of prevNodes is not co-directied with
4422 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4423 // iNotSame is where prevNodes and nextNodes are different.
4424 // If result is true then future volume orientation is OK
4425 //=======================================================================
4427 bool isReverse(const SMDS_MeshElement* face,
4428 const vector<const SMDS_MeshNode*>& prevNodes,
4429 const vector<const SMDS_MeshNode*>& nextNodes,
4433 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4434 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4435 gp_XYZ extrDir( pN - pP ), faceNorm;
4436 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4438 return faceNorm * extrDir < 0.0;
4441 //================================================================================
4443 * \brief Assure that theElemSets[0] holds elements, not nodes
4445 //================================================================================
4447 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4449 if ( !theElemSets[0].empty() &&
4450 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4452 std::swap( theElemSets[0], theElemSets[1] );
4454 else if ( !theElemSets[1].empty() &&
4455 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4457 std::swap( theElemSets[0], theElemSets[1] );
4462 //=======================================================================
4464 * \brief Create elements by sweeping an element
4465 * \param elem - element to sweep
4466 * \param newNodesItVec - nodes generated from each node of the element
4467 * \param newElems - generated elements
4468 * \param nbSteps - number of sweeping steps
4469 * \param srcElements - to append elem for each generated element
4471 //=======================================================================
4473 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4474 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4475 list<const SMDS_MeshElement*>& newElems,
4476 const size_t nbSteps,
4477 SMESH_SequenceOfElemPtr& srcElements)
4479 //MESSAGE("sweepElement " << nbSteps);
4480 SMESHDS_Mesh* aMesh = GetMeshDS();
4482 const int nbNodes = elem->NbNodes();
4483 const int nbCorners = elem->NbCornerNodes();
4484 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4485 polyhedron creation !!! */
4486 // Loop on elem nodes:
4487 // find new nodes and detect same nodes indices
4488 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4489 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4490 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4491 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4493 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4494 vector<int> sames(nbNodes);
4495 vector<bool> isSingleNode(nbNodes);
4497 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4498 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4499 const SMDS_MeshNode* node = nnIt->first;
4500 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4501 if ( listNewNodes.empty() )
4504 itNN [ iNode ] = listNewNodes.begin();
4505 prevNod[ iNode ] = node;
4506 nextNod[ iNode ] = listNewNodes.front();
4508 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4509 corner node of linear */
4510 if ( prevNod[ iNode ] != nextNod [ iNode ])
4511 nbDouble += !isSingleNode[iNode];
4513 if( iNode < nbCorners ) { // check corners only
4514 if ( prevNod[ iNode ] == nextNod [ iNode ])
4515 sames[nbSame++] = iNode;
4517 iNotSameNode = iNode;
4521 if ( nbSame == nbNodes || nbSame > 2) {
4522 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4526 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4528 // fix nodes order to have bottom normal external
4529 if ( baseType == SMDSEntity_Polygon )
4531 std::reverse( itNN.begin(), itNN.end() );
4532 std::reverse( prevNod.begin(), prevNod.end() );
4533 std::reverse( midlNod.begin(), midlNod.end() );
4534 std::reverse( nextNod.begin(), nextNod.end() );
4535 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4539 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4540 SMDS_MeshCell::applyInterlace( ind, itNN );
4541 SMDS_MeshCell::applyInterlace( ind, prevNod );
4542 SMDS_MeshCell::applyInterlace( ind, nextNod );
4543 SMDS_MeshCell::applyInterlace( ind, midlNod );
4544 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4547 sames[nbSame] = iNotSameNode;
4548 for ( int j = 0; j <= nbSame; ++j )
4549 for ( size_t i = 0; i < ind.size(); ++i )
4550 if ( ind[i] == sames[j] )
4555 iNotSameNode = sames[nbSame];
4559 else if ( elem->GetType() == SMDSAbs_Edge )
4561 // orient a new face same as adjacent one
4563 const SMDS_MeshElement* e;
4564 TIDSortedElemSet dummy;
4565 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4566 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4567 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4569 // there is an adjacent face, check order of nodes in it
4570 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4573 std::swap( itNN[0], itNN[1] );
4574 std::swap( prevNod[0], prevNod[1] );
4575 std::swap( nextNod[0], nextNod[1] );
4576 isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4578 sames[0] = 1 - sames[0];
4579 iNotSameNode = 1 - iNotSameNode;
4584 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4586 iSameNode = sames[ nbSame-1 ];
4587 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4588 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4589 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4592 if ( baseType == SMDSEntity_Polygon )
4594 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4595 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4597 else if ( baseType == SMDSEntity_Quad_Polygon )
4599 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4600 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4603 // make new elements
4604 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4607 for ( iNode = 0; iNode < nbNodes; iNode++ )
4609 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4610 nextNod[ iNode ] = *itNN[ iNode ]++;
4613 SMDS_MeshElement* aNewElem = 0;
4614 /*if(!elem->IsPoly())*/ {
4615 switch ( baseType ) {
4617 case SMDSEntity_Node: { // sweep NODE
4618 if ( nbSame == 0 ) {
4619 if ( isSingleNode[0] )
4620 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4622 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4628 case SMDSEntity_Edge: { // sweep EDGE
4629 if ( nbDouble == 0 )
4631 if ( nbSame == 0 ) // ---> quadrangle
4632 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4633 nextNod[ 1 ], nextNod[ 0 ] );
4634 else // ---> triangle
4635 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4636 nextNod[ iNotSameNode ] );
4638 else // ---> polygon
4640 vector<const SMDS_MeshNode*> poly_nodes;
4641 poly_nodes.push_back( prevNod[0] );
4642 poly_nodes.push_back( prevNod[1] );
4643 if ( prevNod[1] != nextNod[1] )
4645 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4646 poly_nodes.push_back( nextNod[1] );
4648 if ( prevNod[0] != nextNod[0] )
4650 poly_nodes.push_back( nextNod[0] );
4651 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4653 switch ( poly_nodes.size() ) {
4655 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4658 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4659 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4662 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4667 case SMDSEntity_Triangle: // TRIANGLE --->
4669 if ( nbDouble > 0 ) break;
4670 if ( nbSame == 0 ) // ---> pentahedron
4671 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4672 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4674 else if ( nbSame == 1 ) // ---> pyramid
4675 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4676 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4677 nextNod[ iSameNode ]);
4679 else // 2 same nodes: ---> tetrahedron
4680 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4681 nextNod[ iNotSameNode ]);
4684 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4688 if ( nbDouble+nbSame == 2 )
4690 if(nbSame==0) { // ---> quadratic quadrangle
4691 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4692 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4694 else { //(nbSame==1) // ---> quadratic triangle
4696 return; // medium node on axis
4698 else if(sames[0]==0)
4699 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4700 prevNod[2], midlNod[1], nextNod[2] );
4702 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4703 prevNod[2], nextNod[2], midlNod[0]);
4706 else if ( nbDouble == 3 )
4708 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4709 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4710 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4717 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4718 if ( nbDouble > 0 ) break;
4720 if ( nbSame == 0 ) // ---> hexahedron
4721 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4722 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4724 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4725 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4726 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4727 nextNod[ iSameNode ]);
4728 newElems.push_back( aNewElem );
4729 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4730 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4731 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4733 else if ( nbSame == 2 ) { // ---> pentahedron
4734 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4735 // iBeforeSame is same too
4736 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4737 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4738 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4740 // iAfterSame is same too
4741 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4742 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4743 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4747 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4748 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4749 if ( nbDouble+nbSame != 3 ) break;
4751 // ---> pentahedron with 15 nodes
4752 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4753 nextNod[0], nextNod[1], nextNod[2],
4754 prevNod[3], prevNod[4], prevNod[5],
4755 nextNod[3], nextNod[4], nextNod[5],
4756 midlNod[0], midlNod[1], midlNod[2]);
4758 else if(nbSame==1) {
4759 // ---> 2d order pyramid of 13 nodes
4760 int apex = iSameNode;
4761 int i0 = ( apex + 1 ) % nbCorners;
4762 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4766 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4767 nextNod[i0], nextNod[i1], prevNod[apex],
4768 prevNod[i01], midlNod[i0],
4769 nextNod[i01], midlNod[i1],
4770 prevNod[i1a], prevNod[i0a],
4771 nextNod[i0a], nextNod[i1a]);
4773 else if(nbSame==2) {
4774 // ---> 2d order tetrahedron of 10 nodes
4775 int n1 = iNotSameNode;
4776 int n2 = ( n1 + 1 ) % nbCorners;
4777 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4781 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4782 prevNod[n12], prevNod[n23], prevNod[n31],
4783 midlNod[n1], nextNod[n12], nextNod[n31]);
4787 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4789 if ( nbDouble != 4 ) break;
4790 // ---> hexahedron with 20 nodes
4791 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4792 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4793 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4794 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4795 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4797 else if(nbSame==1) {
4798 // ---> pyramid + pentahedron - can not be created since it is needed
4799 // additional middle node at the center of face
4800 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4803 else if( nbSame == 2 ) {
4804 if ( nbDouble != 2 ) break;
4805 // ---> 2d order Pentahedron with 15 nodes
4807 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4808 // iBeforeSame is same too
4815 // iAfterSame is same too
4825 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4826 prevNod[n4], prevNod[n5], nextNod[n5],
4827 prevNod[n12], midlNod[n2], nextNod[n12],
4828 prevNod[n45], midlNod[n5], nextNod[n45],
4829 prevNod[n14], prevNod[n25], nextNod[n25]);
4833 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4835 if( nbSame == 0 && nbDouble == 9 ) {
4836 // ---> tri-quadratic hexahedron with 27 nodes
4837 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4838 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4839 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4840 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4841 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4842 prevNod[8], // bottom center
4843 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4844 nextNod[8], // top center
4845 midlNod[8]);// elem center
4853 case SMDSEntity_Polygon: { // sweep POLYGON
4855 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4856 // ---> hexagonal prism
4857 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4858 prevNod[3], prevNod[4], prevNod[5],
4859 nextNod[0], nextNod[1], nextNod[2],
4860 nextNod[3], nextNod[4], nextNod[5]);
4864 case SMDSEntity_Ball:
4869 } // switch ( baseType )
4872 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4874 if ( baseType != SMDSEntity_Polygon )
4876 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4877 SMDS_MeshCell::applyInterlace( ind, prevNod );
4878 SMDS_MeshCell::applyInterlace( ind, nextNod );
4879 SMDS_MeshCell::applyInterlace( ind, midlNod );
4880 SMDS_MeshCell::applyInterlace( ind, itNN );
4881 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4882 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4884 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4885 vector<int> quantities (nbNodes + 2);
4886 polyedre_nodes.clear();
4890 for (int inode = 0; inode < nbNodes; inode++)
4891 polyedre_nodes.push_back( prevNod[inode] );
4892 quantities.push_back( nbNodes );
4895 polyedre_nodes.push_back( nextNod[0] );
4896 for (int inode = nbNodes; inode-1; --inode )
4897 polyedre_nodes.push_back( nextNod[inode-1] );
4898 quantities.push_back( nbNodes );
4906 const int iQuad = elem->IsQuadratic();
4907 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4909 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4910 int inextface = (iface+1+iQuad) % nbNodes;
4911 int imid = (iface+1) % nbNodes;
4912 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4913 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4914 polyedre_nodes.push_back( prevNod[iface] ); // 1
4915 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4917 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4918 polyedre_nodes.push_back( nextNod[iface] ); // 2
4920 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4921 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4923 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4924 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4926 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4927 if ( nbFaceNodes > 2 )
4928 quantities.push_back( nbFaceNodes );
4929 else // degenerated face
4930 polyedre_nodes.resize( prevNbNodes );
4932 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4934 } // try to create a polyherdal prism
4937 newElems.push_back( aNewElem );
4938 myLastCreatedElems.Append(aNewElem);
4939 srcElements.Append( elem );
4942 // set new prev nodes
4943 for ( iNode = 0; iNode < nbNodes; iNode++ )
4944 prevNod[ iNode ] = nextNod[ iNode ];
4949 //=======================================================================
4951 * \brief Create 1D and 2D elements around swept elements
4952 * \param mapNewNodes - source nodes and ones generated from them
4953 * \param newElemsMap - source elements and ones generated from them
4954 * \param elemNewNodesMap - nodes generated from each node of each element
4955 * \param elemSet - all swept elements
4956 * \param nbSteps - number of sweeping steps
4957 * \param srcElements - to append elem for each generated element
4959 //=======================================================================
4961 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4962 TTElemOfElemListMap & newElemsMap,
4963 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4964 TIDSortedElemSet& elemSet,
4966 SMESH_SequenceOfElemPtr& srcElements)
4968 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4969 SMESHDS_Mesh* aMesh = GetMeshDS();
4971 // Find nodes belonging to only one initial element - sweep them into edges.
4973 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4974 for ( ; nList != mapNewNodes.end(); nList++ )
4976 const SMDS_MeshNode* node =
4977 static_cast<const SMDS_MeshNode*>( nList->first );
4978 if ( newElemsMap.count( node ))
4979 continue; // node was extruded into edge
4980 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4981 int nbInitElems = 0;
4982 const SMDS_MeshElement* el = 0;
4983 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4984 while ( eIt->more() && nbInitElems < 2 ) {
4985 const SMDS_MeshElement* e = eIt->next();
4986 SMDSAbs_ElementType type = e->GetType();
4987 if ( type == SMDSAbs_Volume ||
4991 if ( type > highType ) {
4998 if ( nbInitElems == 1 ) {
4999 bool NotCreateEdge = el && el->IsMediumNode(node);
5000 if(!NotCreateEdge) {
5001 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5002 list<const SMDS_MeshElement*> newEdges;
5003 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5008 // Make a ceiling for each element ie an equal element of last new nodes.
5009 // Find free links of faces - make edges and sweep them into faces.
5011 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5013 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
5014 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5015 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5017 const SMDS_MeshElement* elem = itElem->first;
5018 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5020 if(itElem->second.size()==0) continue;
5022 const bool isQuadratic = elem->IsQuadratic();
5024 if ( elem->GetType() == SMDSAbs_Edge ) {
5025 // create a ceiling edge
5026 if ( !isQuadratic ) {
5027 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5028 vecNewNodes[ 1 ]->second.back())) {
5029 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5030 vecNewNodes[ 1 ]->second.back()));
5031 srcElements.Append( elem );
5035 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5036 vecNewNodes[ 1 ]->second.back(),
5037 vecNewNodes[ 2 ]->second.back())) {
5038 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5039 vecNewNodes[ 1 ]->second.back(),
5040 vecNewNodes[ 2 ]->second.back()));
5041 srcElements.Append( elem );
5045 if ( elem->GetType() != SMDSAbs_Face )
5048 bool hasFreeLinks = false;
5050 TIDSortedElemSet avoidSet;
5051 avoidSet.insert( elem );
5053 set<const SMDS_MeshNode*> aFaceLastNodes;
5054 int iNode, nbNodes = vecNewNodes.size();
5055 if ( !isQuadratic ) {
5056 // loop on the face nodes
5057 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5058 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5059 // look for free links of the face
5060 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5061 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5062 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5063 // check if a link n1-n2 is free
5064 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5065 hasFreeLinks = true;
5066 // make a new edge and a ceiling for a new edge
5067 const SMDS_MeshElement* edge;
5068 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5069 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5070 srcElements.Append( myLastCreatedElems.Last() );
5072 n1 = vecNewNodes[ iNode ]->second.back();
5073 n2 = vecNewNodes[ iNext ]->second.back();
5074 if ( !aMesh->FindEdge( n1, n2 )) {
5075 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5076 srcElements.Append( edge );
5081 else { // elem is quadratic face
5082 int nbn = nbNodes/2;
5083 for ( iNode = 0; iNode < nbn; iNode++ ) {
5084 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5085 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5086 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5087 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5088 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5089 // check if a link is free
5090 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5091 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5092 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5093 hasFreeLinks = true;
5094 // make an edge and a ceiling for a new edge
5096 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5097 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5098 srcElements.Append( elem );
5100 n1 = vecNewNodes[ iNode ]->second.back();
5101 n2 = vecNewNodes[ iNext ]->second.back();
5102 n3 = vecNewNodes[ iNode+nbn ]->second.back();
5103 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5104 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5105 srcElements.Append( elem );
5109 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5110 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5114 // sweep free links into faces
5116 if ( hasFreeLinks ) {
5117 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5118 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5120 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5121 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5122 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5123 initNodeSet.insert( vecNewNodes[ iNode ]->first );
5124 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5126 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
5127 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5128 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5130 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5131 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5132 std::advance( v, volNb );
5133 // find indices of free faces of a volume and their source edges
5134 list< int > freeInd;
5135 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5136 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5137 int iF, nbF = vTool.NbFaces();
5138 for ( iF = 0; iF < nbF; iF ++ ) {
5139 if (vTool.IsFreeFace( iF ) &&
5140 vTool.GetFaceNodes( iF, faceNodeSet ) &&
5141 initNodeSet != faceNodeSet) // except an initial face
5143 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5145 if ( faceNodeSet == initNodeSetNoCenter )
5147 freeInd.push_back( iF );
5148 // find source edge of a free face iF
5149 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5150 vector<const SMDS_MeshNode*>::iterator lastCommom;
5151 commonNodes.resize( nbNodes, 0 );
5152 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5153 initNodeSet.begin(), initNodeSet.end(),
5154 commonNodes.begin());
5155 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5156 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5158 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5160 if ( !srcEdges.back() )
5162 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5163 << iF << " of volume #" << vTool.ID() << endl;
5168 if ( freeInd.empty() )
5171 // create wall faces for all steps;
5172 // if such a face has been already created by sweep of edge,
5173 // assure that its orientation is OK
5174 for ( int iStep = 0; iStep < nbSteps; iStep++ )
5176 vTool.Set( *v, /*ignoreCentralNodes=*/false );
5177 vTool.SetExternalNormal();
5178 const int nextShift = vTool.IsForward() ? +1 : -1;
5179 list< int >::iterator ind = freeInd.begin();
5180 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5181 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5183 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5184 int nbn = vTool.NbFaceNodes( *ind );
5185 const SMDS_MeshElement * f = 0;
5186 if ( nbn == 3 ) ///// triangle
5188 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5190 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5192 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5194 nodes[ 1 + nextShift ] };
5196 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5198 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5202 else if ( nbn == 4 ) ///// quadrangle
5204 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5206 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5208 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5209 nodes[ 2 ], nodes[ 2+nextShift ] };
5211 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5213 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5214 newOrder[ 2 ], newOrder[ 3 ]));
5217 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5219 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5221 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5223 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5225 nodes[2 + 2*nextShift],
5226 nodes[3 - 2*nextShift],
5228 nodes[3 + 2*nextShift]};
5230 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5232 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5240 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5242 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5243 nodes[1], nodes[3], nodes[5], nodes[7] );
5245 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5247 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5248 nodes[4 - 2*nextShift],
5250 nodes[4 + 2*nextShift],
5252 nodes[5 - 2*nextShift],
5254 nodes[5 + 2*nextShift] };
5256 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5258 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5259 newOrder[ 2 ], newOrder[ 3 ],
5260 newOrder[ 4 ], newOrder[ 5 ],
5261 newOrder[ 6 ], newOrder[ 7 ]));
5264 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5266 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5267 SMDSAbs_Face, /*noMedium=*/false);
5269 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5271 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5272 nodes[4 - 2*nextShift],
5274 nodes[4 + 2*nextShift],
5276 nodes[5 - 2*nextShift],
5278 nodes[5 + 2*nextShift],
5281 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5283 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5284 newOrder[ 2 ], newOrder[ 3 ],
5285 newOrder[ 4 ], newOrder[ 5 ],
5286 newOrder[ 6 ], newOrder[ 7 ],
5290 else //////// polygon
5292 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5293 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5295 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5297 if ( !vTool.IsForward() )
5298 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5300 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5302 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5306 while ( srcElements.Length() < myLastCreatedElems.Length() )
5307 srcElements.Append( *srcEdge );
5309 } // loop on free faces
5311 // go to the next volume
5313 while ( iVol++ < nbVolumesByStep ) v++;
5316 } // loop on volumes of one step
5317 } // sweep free links into faces
5319 // Make a ceiling face with a normal external to a volume
5321 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5322 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5323 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5325 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5326 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5327 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5331 lastVol.SetExternalNormal();
5332 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5333 const int nbn = lastVol.NbFaceNodes( iF );
5334 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5335 if ( !hasFreeLinks ||
5336 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5338 const vector<int>& interlace =
5339 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5340 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5342 AddElement( nodeVec, anyFace.Init( elem ));
5344 while ( srcElements.Length() < myLastCreatedElems.Length() )
5345 srcElements.Append( elem );
5348 } // loop on swept elements
5351 //=======================================================================
5352 //function : RotationSweep
5354 //=======================================================================
5356 SMESH_MeshEditor::PGroupIDs
5357 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5358 const gp_Ax1& theAxis,
5359 const double theAngle,
5360 const int theNbSteps,
5361 const double theTol,
5362 const bool theMakeGroups,
5363 const bool theMakeWalls)
5365 myLastCreatedElems.Clear();
5366 myLastCreatedNodes.Clear();
5368 // source elements for each generated one
5369 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5371 MESSAGE( "RotationSweep()");
5373 aTrsf.SetRotation( theAxis, theAngle );
5375 aTrsf2.SetRotation( theAxis, theAngle/2. );
5377 gp_Lin aLine( theAxis );
5378 double aSqTol = theTol * theTol;
5380 SMESHDS_Mesh* aMesh = GetMeshDS();
5382 TNodeOfNodeListMap mapNewNodes;
5383 TElemOfVecOfNnlmiMap mapElemNewNodes;
5384 TTElemOfElemListMap newElemsMap;
5386 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5387 myMesh->NbFaces(ORDER_QUADRATIC) +
5388 myMesh->NbVolumes(ORDER_QUADRATIC) );
5389 // loop on theElemSets
5390 setElemsFirst( theElemSets );
5391 TIDSortedElemSet::iterator itElem;
5392 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5394 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5395 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5396 const SMDS_MeshElement* elem = *itElem;
5397 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5399 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5400 newNodesItVec.reserve( elem->NbNodes() );
5402 // loop on elem nodes
5403 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5404 while ( itN->more() )
5406 const SMDS_MeshNode* node = cast2Node( itN->next() );
5408 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5410 aXYZ.Coord( coord[0], coord[1], coord[2] );
5411 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5413 // check if a node has been already sweeped
5414 TNodeOfNodeListMapItr nIt =
5415 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5416 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5417 if ( listNewNodes.empty() )
5419 // check if we are to create medium nodes between corner ones
5420 bool needMediumNodes = false;
5421 if ( isQuadraticMesh )
5423 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5424 while (it->more() && !needMediumNodes )
5426 const SMDS_MeshElement* invElem = it->next();
5427 if ( invElem != elem && !theElems.count( invElem )) continue;
5428 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5429 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5430 needMediumNodes = true;
5435 const SMDS_MeshNode * newNode = node;
5436 for ( int i = 0; i < theNbSteps; i++ ) {
5438 if ( needMediumNodes ) // create a medium node
5440 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5441 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5442 myLastCreatedNodes.Append(newNode);
5443 srcNodes.Append( node );
5444 listNewNodes.push_back( newNode );
5445 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5448 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5450 // create a corner node
5451 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5452 myLastCreatedNodes.Append(newNode);
5453 srcNodes.Append( node );
5454 listNewNodes.push_back( newNode );
5457 listNewNodes.push_back( newNode );
5458 // if ( needMediumNodes )
5459 // listNewNodes.push_back( newNode );
5463 newNodesItVec.push_back( nIt );
5465 // make new elements
5466 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5471 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5473 PGroupIDs newGroupIDs;
5474 if ( theMakeGroups )
5475 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5480 //=======================================================================
5481 //function : ExtrusParam
5482 //purpose : standard construction
5483 //=======================================================================
5485 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5486 const int theNbSteps,
5488 const double theTolerance):
5490 myFlags( theFlags ),
5491 myTolerance( theTolerance ),
5492 myElemsToUse( NULL )
5494 mySteps = new TColStd_HSequenceOfReal;
5495 const double stepSize = theStep.Magnitude();
5496 for (int i=1; i<=theNbSteps; i++ )
5497 mySteps->Append( stepSize );
5499 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5500 ( theTolerance > 0 ))
5502 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5506 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5510 //=======================================================================
5511 //function : ExtrusParam
5512 //purpose : steps are given explicitly
5513 //=======================================================================
5515 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5516 Handle(TColStd_HSequenceOfReal) theSteps,
5518 const double theTolerance):
5520 mySteps( theSteps ),
5521 myFlags( theFlags ),
5522 myTolerance( theTolerance ),
5523 myElemsToUse( NULL )
5525 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5526 ( theTolerance > 0 ))
5528 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5532 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5536 //=======================================================================
5537 //function : ExtrusParam
5538 //purpose : for extrusion by normal
5539 //=======================================================================
5541 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5542 const int theNbSteps,
5546 mySteps( new TColStd_HSequenceOfReal ),
5547 myFlags( theFlags ),
5549 myElemsToUse( NULL )
5551 for (int i = 0; i < theNbSteps; i++ )
5552 mySteps->Append( theStepSize );
5556 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5560 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5564 //=======================================================================
5565 //function : ExtrusParam::SetElementsToUse
5566 //purpose : stores elements to use for extrusion by normal, depending on
5567 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5568 //=======================================================================
5570 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5572 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5575 //=======================================================================
5576 //function : ExtrusParam::beginStepIter
5577 //purpose : prepare iteration on steps
5578 //=======================================================================
5580 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5582 myWithMediumNodes = withMediumNodes;
5586 //=======================================================================
5587 //function : ExtrusParam::moreSteps
5588 //purpose : are there more steps?
5589 //=======================================================================
5591 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5593 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5595 //=======================================================================
5596 //function : ExtrusParam::nextStep
5597 //purpose : returns the next step
5598 //=======================================================================
5600 double SMESH_MeshEditor::ExtrusParam::nextStep()
5603 if ( !myCurSteps.empty() )
5605 res = myCurSteps.back();
5606 myCurSteps.pop_back();
5608 else if ( myNextStep <= mySteps->Length() )
5610 myCurSteps.push_back( mySteps->Value( myNextStep ));
5612 if ( myWithMediumNodes )
5614 myCurSteps.back() /= 2.;
5615 myCurSteps.push_back( myCurSteps.back() );
5622 //=======================================================================
5623 //function : ExtrusParam::makeNodesByDir
5624 //purpose : create nodes for standard extrusion
5625 //=======================================================================
5627 int SMESH_MeshEditor::ExtrusParam::
5628 makeNodesByDir( SMESHDS_Mesh* mesh,
5629 const SMDS_MeshNode* srcNode,
5630 std::list<const SMDS_MeshNode*> & newNodes,
5631 const bool makeMediumNodes)
5633 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5636 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5638 p += myDir.XYZ() * nextStep();
5639 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5640 newNodes.push_back( newNode );
5645 //=======================================================================
5646 //function : ExtrusParam::makeNodesByDirAndSew
5647 //purpose : create nodes for standard extrusion with sewing
5648 //=======================================================================
5650 int SMESH_MeshEditor::ExtrusParam::
5651 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5652 const SMDS_MeshNode* srcNode,
5653 std::list<const SMDS_MeshNode*> & newNodes,
5654 const bool makeMediumNodes)
5656 gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5659 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5661 P1 += myDir.XYZ() * nextStep();
5663 // try to search in sequence of existing nodes
5664 // if myNodes.Length()>0 we 'nave to use given sequence
5665 // else - use all nodes of mesh
5666 const SMDS_MeshNode * node = 0;
5667 if ( myNodes.Length() > 0 ) {
5669 for(i=1; i<=myNodes.Length(); i++) {
5670 gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5671 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5673 node = myNodes.Value(i);
5679 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5680 while(itn->more()) {
5681 SMESH_TNodeXYZ P2( itn->next() );
5682 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5691 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5693 newNodes.push_back( node );
5700 //=======================================================================
5701 //function : ExtrusParam::makeNodesByNormal2D
5702 //purpose : create nodes for extrusion using normals of faces
5703 //=======================================================================
5705 int SMESH_MeshEditor::ExtrusParam::
5706 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5707 const SMDS_MeshNode* srcNode,
5708 std::list<const SMDS_MeshNode*> & newNodes,
5709 const bool makeMediumNodes)
5711 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5713 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5715 // get normals to faces sharing srcNode
5716 vector< gp_XYZ > norms, baryCenters;
5717 gp_XYZ norm, avgNorm( 0,0,0 );
5718 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5719 while ( faceIt->more() )
5721 const SMDS_MeshElement* face = faceIt->next();
5722 if ( myElemsToUse && !myElemsToUse->count( face ))
5724 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5726 norms.push_back( norm );
5728 if ( !alongAvgNorm )
5732 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5733 bc += SMESH_TNodeXYZ( nIt->next() );
5734 baryCenters.push_back( bc / nbN );
5739 if ( norms.empty() ) return 0;
5741 double normSize = avgNorm.Modulus();
5742 if ( normSize < std::numeric_limits<double>::min() )
5745 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5748 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5751 avgNorm /= normSize;
5754 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5757 double stepSize = nextStep();
5759 if ( norms.size() > 1 )
5761 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5763 // translate plane of a face
5764 baryCenters[ iF ] += norms[ iF ] * stepSize;
5766 // find point of intersection of the face plane located at baryCenters[ iF ]
5767 // and avgNorm located at pNew
5768 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5769 double dot = ( norms[ iF ] * avgNorm );
5770 if ( dot < std::numeric_limits<double>::min() )
5771 dot = stepSize * 1e-3;
5772 double step = -( norms[ iF ] * pNew + d ) / dot;
5773 pNew += step * avgNorm;
5778 pNew += stepSize * avgNorm;
5782 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5783 newNodes.push_back( newNode );
5788 //=======================================================================
5789 //function : ExtrusParam::makeNodesByNormal1D
5790 //purpose : create nodes for extrusion using normals of edges
5791 //=======================================================================
5793 int SMESH_MeshEditor::ExtrusParam::
5794 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5795 const SMDS_MeshNode* srcNode,
5796 std::list<const SMDS_MeshNode*> & newNodes,
5797 const bool makeMediumNodes)
5799 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5803 //=======================================================================
5804 //function : ExtrusionSweep
5806 //=======================================================================
5808 SMESH_MeshEditor::PGroupIDs
5809 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5810 const gp_Vec& theStep,
5811 const int theNbSteps,
5812 TTElemOfElemListMap& newElemsMap,
5814 const double theTolerance)
5816 ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5817 return ExtrusionSweep( theElems, aParams, newElemsMap );
5821 //=======================================================================
5822 //function : ExtrusionSweep
5824 //=======================================================================
5826 SMESH_MeshEditor::PGroupIDs
5827 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5828 ExtrusParam& theParams,
5829 TTElemOfElemListMap& newElemsMap)
5831 myLastCreatedElems.Clear();
5832 myLastCreatedNodes.Clear();
5834 // source elements for each generated one
5835 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5837 //SMESHDS_Mesh* aMesh = GetMeshDS();
5839 setElemsFirst( theElemSets );
5840 const int nbSteps = theParams.NbSteps();
5841 theParams.SetElementsToUse( theElemSets[0] );
5843 TNodeOfNodeListMap mapNewNodes;
5844 //TNodeOfNodeVecMap mapNewNodes;
5845 TElemOfVecOfNnlmiMap mapElemNewNodes;
5846 //TElemOfVecOfMapNodesMap mapElemNewNodes;
5848 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5849 myMesh->NbFaces(ORDER_QUADRATIC) +
5850 myMesh->NbVolumes(ORDER_QUADRATIC) );
5852 TIDSortedElemSet::iterator itElem;
5853 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5855 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5856 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5858 // check element type
5859 const SMDS_MeshElement* elem = *itElem;
5860 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5863 const size_t nbNodes = elem->NbNodes();
5864 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5865 newNodesItVec.reserve( nbNodes );
5867 // loop on elem nodes
5868 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5869 while ( itN->more() )
5871 // check if a node has been already sweeped
5872 const SMDS_MeshNode* node = cast2Node( itN->next() );
5873 TNodeOfNodeListMap::iterator nIt =
5874 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5875 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5876 if ( listNewNodes.empty() )
5880 // check if we are to create medium nodes between corner ones
5881 bool needMediumNodes = false;
5882 if ( isQuadraticMesh )
5884 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5885 while (it->more() && !needMediumNodes )
5887 const SMDS_MeshElement* invElem = it->next();
5888 if ( invElem != elem && !theElems.count( invElem )) continue;
5889 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5890 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5891 needMediumNodes = true;
5894 // create nodes for all steps
5895 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5897 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5898 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5900 myLastCreatedNodes.Append( *newNodesIt );
5901 srcNodes.Append( node );
5906 break; // newNodesItVec will be shorter than nbNodes
5909 newNodesItVec.push_back( nIt );
5911 // make new elements
5912 if ( newNodesItVec.size() == nbNodes )
5913 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5917 if ( theParams.ToMakeBoundary() ) {
5918 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5920 PGroupIDs newGroupIDs;
5921 if ( theParams.ToMakeGroups() )
5922 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5927 //=======================================================================
5928 //function : ExtrusionAlongTrack
5930 //=======================================================================
5931 SMESH_MeshEditor::Extrusion_Error
5932 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5933 SMESH_subMesh* theTrack,
5934 const SMDS_MeshNode* theN1,
5935 const bool theHasAngles,
5936 list<double>& theAngles,
5937 const bool theLinearVariation,
5938 const bool theHasRefPoint,
5939 const gp_Pnt& theRefPoint,
5940 const bool theMakeGroups)
5942 MESSAGE("ExtrusionAlongTrack");
5943 myLastCreatedElems.Clear();
5944 myLastCreatedNodes.Clear();
5947 std::list<double> aPrms;
5948 TIDSortedElemSet::iterator itElem;
5951 TopoDS_Edge aTrackEdge;
5952 TopoDS_Vertex aV1, aV2;
5954 SMDS_ElemIteratorPtr aItE;
5955 SMDS_NodeIteratorPtr aItN;
5956 SMDSAbs_ElementType aTypeE;
5958 TNodeOfNodeListMap mapNewNodes;
5961 aNbE = theElements[0].size() + theElements[1].size();
5964 return EXTR_NO_ELEMENTS;
5966 // 1.1 Track Pattern
5969 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5971 return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
5972 theHasAngles, theAngles, theLinearVariation,
5973 theHasRefPoint, theRefPoint, theMakeGroups );
5975 aItE = pSubMeshDS->GetElements();
5976 while ( aItE->more() ) {
5977 const SMDS_MeshElement* pE = aItE->next();
5978 aTypeE = pE->GetType();
5979 // Pattern must contain links only
5980 if ( aTypeE != SMDSAbs_Edge )
5981 return EXTR_PATH_NOT_EDGE;
5984 list<SMESH_MeshEditor_PathPoint> fullList;
5986 const TopoDS_Shape& aS = theTrack->GetSubShape();
5987 // Sub-shape for the Pattern must be an Edge or Wire
5988 if( aS.ShapeType() == TopAbs_EDGE ) {
5989 aTrackEdge = TopoDS::Edge( aS );
5990 // the Edge must not be degenerated
5991 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5992 return EXTR_BAD_PATH_SHAPE;
5993 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5994 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5995 const SMDS_MeshNode* aN1 = aItN->next();
5996 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5997 const SMDS_MeshNode* aN2 = aItN->next();
5998 // starting node must be aN1 or aN2
5999 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6000 return EXTR_BAD_STARTING_NODE;
6001 aItN = pSubMeshDS->GetNodes();
6002 while ( aItN->more() ) {
6003 const SMDS_MeshNode* pNode = aItN->next();
6004 const SMDS_EdgePosition* pEPos =
6005 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6006 double aT = pEPos->GetUParameter();
6007 aPrms.push_back( aT );
6009 //Extrusion_Error err =
6010 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6011 } else if( aS.ShapeType() == TopAbs_WIRE ) {
6012 list< SMESH_subMesh* > LSM;
6013 TopTools_SequenceOfShape Edges;
6014 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6015 while(itSM->more()) {
6016 SMESH_subMesh* SM = itSM->next();
6018 const TopoDS_Shape& aS = SM->GetSubShape();
6021 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6022 int startNid = theN1->GetID();
6023 TColStd_MapOfInteger UsedNums;
6025 int NbEdges = Edges.Length();
6027 for(; i<=NbEdges; i++) {
6029 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6030 for(; itLSM!=LSM.end(); itLSM++) {
6032 if(UsedNums.Contains(k)) continue;
6033 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6034 SMESH_subMesh* locTrack = *itLSM;
6035 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6036 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6037 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6038 const SMDS_MeshNode* aN1 = aItN->next();
6039 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6040 const SMDS_MeshNode* aN2 = aItN->next();
6041 // starting node must be aN1 or aN2
6042 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6043 // 2. Collect parameters on the track edge
6045 aItN = locMeshDS->GetNodes();
6046 while ( aItN->more() ) {
6047 const SMDS_MeshNode* pNode = aItN->next();
6048 const SMDS_EdgePosition* pEPos =
6049 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6050 double aT = pEPos->GetUParameter();
6051 aPrms.push_back( aT );
6053 list<SMESH_MeshEditor_PathPoint> LPP;
6054 //Extrusion_Error err =
6055 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6056 LLPPs.push_back(LPP);
6058 // update startN for search following egde
6059 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6060 else startNid = aN1->GetID();
6064 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6065 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6066 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6067 for(; itPP!=firstList.end(); itPP++) {
6068 fullList.push_back( *itPP );
6070 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6071 fullList.pop_back();
6073 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6074 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6075 itPP = currList.begin();
6076 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6077 gp_Dir D1 = PP1.Tangent();
6078 gp_Dir D2 = PP2.Tangent();
6079 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6080 (D1.Z()+D2.Z())/2 ) );
6081 PP1.SetTangent(Dnew);
6082 fullList.push_back(PP1);
6084 for(; itPP!=firstList.end(); itPP++) {
6085 fullList.push_back( *itPP );
6087 PP1 = fullList.back();
6088 fullList.pop_back();
6090 // if wire not closed
6091 fullList.push_back(PP1);
6095 return EXTR_BAD_PATH_SHAPE;
6098 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6099 theHasRefPoint, theRefPoint, theMakeGroups);
6103 //=======================================================================
6104 //function : ExtrusionAlongTrack
6106 //=======================================================================
6107 SMESH_MeshEditor::Extrusion_Error
6108 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6109 SMESH_Mesh* theTrack,
6110 const SMDS_MeshNode* theN1,
6111 const bool theHasAngles,
6112 list<double>& theAngles,
6113 const bool theLinearVariation,
6114 const bool theHasRefPoint,
6115 const gp_Pnt& theRefPoint,
6116 const bool theMakeGroups)
6118 myLastCreatedElems.Clear();
6119 myLastCreatedNodes.Clear();
6122 std::list<double> aPrms;
6123 TIDSortedElemSet::iterator itElem;
6126 TopoDS_Edge aTrackEdge;
6127 TopoDS_Vertex aV1, aV2;
6129 SMDS_ElemIteratorPtr aItE;
6130 SMDS_NodeIteratorPtr aItN;
6131 SMDSAbs_ElementType aTypeE;
6133 TNodeOfNodeListMap mapNewNodes;
6136 aNbE = theElements[0].size() + theElements[1].size();
6139 return EXTR_NO_ELEMENTS;
6141 // 1.1 Track Pattern
6144 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6146 aItE = pMeshDS->elementsIterator();
6147 while ( aItE->more() ) {
6148 const SMDS_MeshElement* pE = aItE->next();
6149 aTypeE = pE->GetType();
6150 // Pattern must contain links only
6151 if ( aTypeE != SMDSAbs_Edge )
6152 return EXTR_PATH_NOT_EDGE;
6155 list<SMESH_MeshEditor_PathPoint> fullList;
6157 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6159 if ( !theTrack->HasShapeToMesh() ) {
6160 //Mesh without shape
6161 const SMDS_MeshNode* currentNode = NULL;
6162 const SMDS_MeshNode* prevNode = theN1;
6163 std::vector<const SMDS_MeshNode*> aNodesList;
6164 aNodesList.push_back(theN1);
6165 int nbEdges = 0, conn=0;
6166 const SMDS_MeshElement* prevElem = NULL;
6167 const SMDS_MeshElement* currentElem = NULL;
6168 int totalNbEdges = theTrack->NbEdges();
6169 SMDS_ElemIteratorPtr nIt;
6172 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6173 return EXTR_BAD_STARTING_NODE;
6176 conn = nbEdgeConnectivity(theN1);
6178 return EXTR_PATH_NOT_EDGE;
6180 aItE = theN1->GetInverseElementIterator();
6181 prevElem = aItE->next();
6182 currentElem = prevElem;
6184 if(totalNbEdges == 1 ) {
6185 nIt = currentElem->nodesIterator();
6186 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6187 if(currentNode == prevNode)
6188 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6189 aNodesList.push_back(currentNode);
6191 nIt = currentElem->nodesIterator();
6192 while( nIt->more() ) {
6193 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6194 if(currentNode == prevNode)
6195 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6196 aNodesList.push_back(currentNode);
6198 //case of the closed mesh
6199 if(currentNode == theN1) {
6204 conn = nbEdgeConnectivity(currentNode);
6206 return EXTR_PATH_NOT_EDGE;
6207 }else if( conn == 1 && nbEdges > 0 ) {
6212 prevNode = currentNode;
6213 aItE = currentNode->GetInverseElementIterator();
6214 currentElem = aItE->next();
6215 if( currentElem == prevElem)
6216 currentElem = aItE->next();
6217 nIt = currentElem->nodesIterator();
6218 prevElem = currentElem;
6224 if(nbEdges != totalNbEdges)
6225 return EXTR_PATH_NOT_EDGE;
6227 TopTools_SequenceOfShape Edges;
6228 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6229 int startNid = theN1->GetID();
6230 for ( size_t i = 1; i < aNodesList.size(); i++ )
6232 gp_Pnt p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6233 gp_Pnt p2 = SMESH_TNodeXYZ( aNodesList[i] );
6234 TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6235 list<SMESH_MeshEditor_PathPoint> LPP;
6237 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6238 LLPPs.push_back(LPP);
6239 if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i ]->GetID();
6240 else startNid = aNodesList[i-1]->GetID();
6243 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6244 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6245 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6246 for(; itPP!=firstList.end(); itPP++) {
6247 fullList.push_back( *itPP );
6250 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6251 SMESH_MeshEditor_PathPoint PP2;
6252 fullList.pop_back();
6254 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6255 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6256 itPP = currList.begin();
6257 PP2 = currList.front();
6258 gp_Dir D1 = PP1.Tangent();
6259 gp_Dir D2 = PP2.Tangent();
6260 gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6261 PP1.SetTangent(Dnew);
6262 fullList.push_back(PP1);
6264 for(; itPP!=currList.end(); itPP++) {
6265 fullList.push_back( *itPP );
6267 PP1 = fullList.back();
6268 fullList.pop_back();
6270 fullList.push_back(PP1);
6272 } // Sub-shape for the Pattern must be an Edge or Wire
6273 else if ( aS.ShapeType() == TopAbs_EDGE )
6275 aTrackEdge = TopoDS::Edge( aS );
6276 // the Edge must not be degenerated
6277 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6278 return EXTR_BAD_PATH_SHAPE;
6279 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6280 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6281 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6282 // starting node must be aN1 or aN2
6283 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6284 return EXTR_BAD_STARTING_NODE;
6285 aItN = pMeshDS->nodesIterator();
6286 while ( aItN->more() ) {
6287 const SMDS_MeshNode* pNode = aItN->next();
6288 if( pNode==aN1 || pNode==aN2 ) continue;
6289 const SMDS_EdgePosition* pEPos =
6290 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6291 double aT = pEPos->GetUParameter();
6292 aPrms.push_back( aT );
6294 //Extrusion_Error err =
6295 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6297 else if( aS.ShapeType() == TopAbs_WIRE ) {
6298 list< SMESH_subMesh* > LSM;
6299 TopTools_SequenceOfShape Edges;
6300 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6301 for(; eExp.More(); eExp.Next()) {
6302 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6303 if( SMESH_Algo::isDegenerated(E) ) continue;
6304 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6310 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6311 TopoDS_Vertex aVprev;
6312 TColStd_MapOfInteger UsedNums;
6313 int NbEdges = Edges.Length();
6315 for(; i<=NbEdges; i++) {
6317 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6318 for(; itLSM!=LSM.end(); itLSM++) {
6320 if(UsedNums.Contains(k)) continue;
6321 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6322 SMESH_subMesh* locTrack = *itLSM;
6323 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6324 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6325 bool aN1isOK = false, aN2isOK = false;
6326 if ( aVprev.IsNull() ) {
6327 // if previous vertex is not yet defined, it means that we in the beginning of wire
6328 // and we have to find initial vertex corresponding to starting node theN1
6329 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6330 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6331 // starting node must be aN1 or aN2
6332 aN1isOK = ( aN1 && aN1 == theN1 );
6333 aN2isOK = ( aN2 && aN2 == theN1 );
6336 // we have specified ending vertex of the previous edge on the previous iteration
6337 // and we have just to check that it corresponds to any vertex in current segment
6338 aN1isOK = aVprev.IsSame( aV1 );
6339 aN2isOK = aVprev.IsSame( aV2 );
6341 if ( !aN1isOK && !aN2isOK ) continue;
6342 // 2. Collect parameters on the track edge
6344 aItN = locMeshDS->GetNodes();
6345 while ( aItN->more() ) {
6346 const SMDS_MeshNode* pNode = aItN->next();
6347 const SMDS_EdgePosition* pEPos =
6348 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6349 double aT = pEPos->GetUParameter();
6350 aPrms.push_back( aT );
6352 list<SMESH_MeshEditor_PathPoint> LPP;
6353 //Extrusion_Error err =
6354 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6355 LLPPs.push_back(LPP);
6357 // update startN for search following egde
6358 if ( aN1isOK ) aVprev = aV2;
6363 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6364 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6365 fullList.splice( fullList.end(), firstList );
6367 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6368 fullList.pop_back();
6370 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6371 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6372 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6373 gp_Dir D1 = PP1.Tangent();
6374 gp_Dir D2 = PP2.Tangent();
6375 gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6376 PP1.SetTangent(Dnew);
6377 fullList.push_back(PP1);
6378 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6379 PP1 = fullList.back();
6380 fullList.pop_back();
6382 // if wire not closed
6383 fullList.push_back(PP1);
6387 return EXTR_BAD_PATH_SHAPE;
6390 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6391 theHasRefPoint, theRefPoint, theMakeGroups);
6395 //=======================================================================
6396 //function : MakeEdgePathPoints
6397 //purpose : auxilary for ExtrusionAlongTrack
6398 //=======================================================================
6399 SMESH_MeshEditor::Extrusion_Error
6400 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
6401 const TopoDS_Edge& aTrackEdge,
6403 list<SMESH_MeshEditor_PathPoint>& LPP)
6405 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6407 aTolVec2=aTolVec*aTolVec;
6409 TopoDS_Vertex aV1, aV2;
6410 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6411 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6412 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6413 // 2. Collect parameters on the track edge
6414 aPrms.push_front( aT1 );
6415 aPrms.push_back( aT2 );
6418 if( FirstIsStart ) {
6429 SMESH_MeshEditor_PathPoint aPP;
6430 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6431 std::list<double>::iterator aItD = aPrms.begin();
6432 for(; aItD != aPrms.end(); ++aItD) {
6436 aC3D->D1( aT, aP3D, aVec );
6437 aL2 = aVec.SquareMagnitude();
6438 if ( aL2 < aTolVec2 )
6439 return EXTR_CANT_GET_TANGENT;
6440 gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6442 aPP.SetTangent( aTgt );
6443 aPP.SetParameter( aT );
6450 //=======================================================================
6451 //function : MakeExtrElements
6452 //purpose : auxilary for ExtrusionAlongTrack
6453 //=======================================================================
6454 SMESH_MeshEditor::Extrusion_Error
6455 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet theElemSets[2],
6456 list<SMESH_MeshEditor_PathPoint>& fullList,
6457 const bool theHasAngles,
6458 list<double>& theAngles,
6459 const bool theLinearVariation,
6460 const bool theHasRefPoint,
6461 const gp_Pnt& theRefPoint,
6462 const bool theMakeGroups)
6464 const int aNbTP = fullList.size();
6467 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6468 LinearAngleVariation(aNbTP-1, theAngles);
6470 // fill vector of path points with angles
6471 vector<SMESH_MeshEditor_PathPoint> aPPs;
6472 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6473 list<double>::iterator itAngles = theAngles.begin();
6474 aPPs.push_back( *itPP++ );
6475 for( ; itPP != fullList.end(); itPP++) {
6476 aPPs.push_back( *itPP );
6477 if ( theHasAngles && itAngles != theAngles.end() )
6478 aPPs.back().SetAngle( *itAngles++ );
6481 TNodeOfNodeListMap mapNewNodes;
6482 TElemOfVecOfNnlmiMap mapElemNewNodes;
6483 TTElemOfElemListMap newElemsMap;
6484 TIDSortedElemSet::iterator itElem;
6485 // source elements for each generated one
6486 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6488 // 3. Center of rotation aV0
6489 gp_Pnt aV0 = theRefPoint;
6490 if ( !theHasRefPoint )
6492 gp_XYZ aGC( 0.,0.,0. );
6493 TIDSortedElemSet newNodes;
6495 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6497 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6498 itElem = theElements.begin();
6499 for ( ; itElem != theElements.end(); itElem++ )
6501 const SMDS_MeshElement* elem = *itElem;
6502 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6503 while ( itN->more() ) {
6504 const SMDS_MeshElement* node = itN->next();
6505 if ( newNodes.insert( node ).second )
6506 aGC += SMESH_TNodeXYZ( node );
6510 aGC /= newNodes.size();
6512 } // if (!theHasRefPoint) {
6514 // 4. Processing the elements
6515 SMESHDS_Mesh* aMesh = GetMeshDS();
6516 list<const SMDS_MeshNode*> emptyList;
6518 setElemsFirst( theElemSets );
6519 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6521 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6522 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6524 const SMDS_MeshElement* elem = *itElem;
6526 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6527 newNodesItVec.reserve( elem->NbNodes() );
6529 // loop on elem nodes
6531 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6532 while ( itN->more() )
6535 // check if a node has been already processed
6536 const SMDS_MeshNode* node = cast2Node( itN->next() );
6537 TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6538 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6539 if ( listNewNodes.empty() )
6542 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6543 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6544 gp_Ax1 anAx1, anAxT1T0;
6545 gp_Dir aDT1x, aDT0x, aDT1T0;
6550 aPN0 = SMESH_TNodeXYZ( node );
6552 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6554 aDT0x= aPP0.Tangent();
6556 for ( int j = 1; j < aNbTP; ++j ) {
6557 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6559 aDT1x = aPP1.Tangent();
6560 aAngle1x = aPP1.Angle();
6562 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6564 gp_Vec aV01x( aP0x, aP1x );
6565 aTrsf.SetTranslation( aV01x );
6568 aV1x = aV0x.Transformed( aTrsf );
6569 aPN1 = aPN0.Transformed( aTrsf );
6571 // rotation 1 [ T1,T0 ]
6572 aAngleT1T0=-aDT1x.Angle( aDT0x );
6573 if (fabs(aAngleT1T0) > aTolAng)
6576 anAxT1T0.SetLocation( aV1x );
6577 anAxT1T0.SetDirection( aDT1T0 );
6578 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6580 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6584 if ( theHasAngles ) {
6585 anAx1.SetLocation( aV1x );
6586 anAx1.SetDirection( aDT1x );
6587 aTrsfRot.SetRotation( anAx1, aAngle1x );
6589 aPN1 = aPN1.Transformed( aTrsfRot );
6593 if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6595 // create additional node
6596 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6597 const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6598 myLastCreatedNodes.Append(newNode);
6599 srcNodes.Append( node );
6600 listNewNodes.push_back( newNode );
6602 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6603 myLastCreatedNodes.Append(newNode);
6604 srcNodes.Append( node );
6605 listNewNodes.push_back( newNode );
6613 else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6615 // if current elem is quadratic and current node is not medium
6616 // we have to check - may be it is needed to insert additional nodes
6617 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6618 if ((int) listNewNodes.size() == aNbTP-1 )
6620 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6621 gp_XYZ P(node->X(), node->Y(), node->Z());
6622 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6624 for(i=0; i<aNbTP-1; i++) {
6625 const SMDS_MeshNode* N = *it;
6626 double x = ( N->X() + P.X() )/2.;
6627 double y = ( N->Y() + P.Y() )/2.;
6628 double z = ( N->Z() + P.Z() )/2.;
6629 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6630 srcNodes.Append( node );
6631 myLastCreatedNodes.Append(newN);
6634 P = gp_XYZ(N->X(),N->Y(),N->Z());
6636 listNewNodes.clear();
6637 for(i=0; i<2*(aNbTP-1); i++) {
6638 listNewNodes.push_back(aNodes[i]);
6643 newNodesItVec.push_back( nIt );
6646 // make new elements
6647 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6651 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6653 if ( theMakeGroups )
6654 generateGroups( srcNodes, srcElems, "extruded");
6660 //=======================================================================
6661 //function : LinearAngleVariation
6662 //purpose : auxilary for ExtrusionAlongTrack
6663 //=======================================================================
6664 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6665 list<double>& Angles)
6667 int nbAngles = Angles.size();
6668 if( nbSteps > nbAngles ) {
6669 vector<double> theAngles(nbAngles);
6670 list<double>::iterator it = Angles.begin();
6672 for(; it!=Angles.end(); it++) {
6674 theAngles[i] = (*it);
6677 double rAn2St = double( nbAngles ) / double( nbSteps );
6678 double angPrev = 0, angle;
6679 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6680 double angCur = rAn2St * ( iSt+1 );
6681 double angCurFloor = floor( angCur );
6682 double angPrevFloor = floor( angPrev );
6683 if ( angPrevFloor == angCurFloor )
6684 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6686 int iP = int( angPrevFloor );
6687 double angPrevCeil = ceil(angPrev);
6688 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6690 int iC = int( angCurFloor );
6691 if ( iC < nbAngles )
6692 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6694 iP = int( angPrevCeil );
6696 angle += theAngles[ iC ];
6698 res.push_back(angle);
6703 for(; it!=res.end(); it++)
6704 Angles.push_back( *it );
6709 //================================================================================
6711 * \brief Move or copy theElements applying theTrsf to their nodes
6712 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6713 * \param theTrsf - transformation to apply
6714 * \param theCopy - if true, create translated copies of theElems
6715 * \param theMakeGroups - if true and theCopy, create translated groups
6716 * \param theTargetMesh - mesh to copy translated elements into
6717 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6719 //================================================================================
6721 SMESH_MeshEditor::PGroupIDs
6722 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6723 const gp_Trsf& theTrsf,
6725 const bool theMakeGroups,
6726 SMESH_Mesh* theTargetMesh)
6728 myLastCreatedElems.Clear();
6729 myLastCreatedNodes.Clear();
6731 bool needReverse = false;
6732 string groupPostfix;
6733 switch ( theTrsf.Form() ) {
6735 MESSAGE("gp_PntMirror");
6737 groupPostfix = "mirrored";
6740 MESSAGE("gp_Ax1Mirror");
6741 groupPostfix = "mirrored";
6744 MESSAGE("gp_Ax2Mirror");
6746 groupPostfix = "mirrored";
6749 MESSAGE("gp_Rotation");
6750 groupPostfix = "rotated";
6752 case gp_Translation:
6753 MESSAGE("gp_Translation");
6754 groupPostfix = "translated";
6757 MESSAGE("gp_Scale");
6758 groupPostfix = "scaled";
6760 case gp_CompoundTrsf: // different scale by axis
6761 MESSAGE("gp_CompoundTrsf");
6762 groupPostfix = "scaled";
6766 needReverse = false;
6767 groupPostfix = "transformed";
6770 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6771 SMESHDS_Mesh* aMesh = GetMeshDS();
6773 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6774 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6775 SMESH_MeshEditor::ElemFeatures elemType;
6777 // map old node to new one
6778 TNodeNodeMap nodeMap;
6780 // elements sharing moved nodes; those of them which have all
6781 // nodes mirrored but are not in theElems are to be reversed
6782 TIDSortedElemSet inverseElemSet;
6784 // source elements for each generated one
6785 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6787 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6788 TIDSortedElemSet orphanNode;
6790 if ( theElems.empty() ) // transform the whole mesh
6793 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6794 while ( eIt->more() ) theElems.insert( eIt->next() );
6796 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6797 while ( nIt->more() )
6799 const SMDS_MeshNode* node = nIt->next();
6800 if ( node->NbInverseElements() == 0)
6801 orphanNode.insert( node );
6805 // loop on elements to transform nodes : first orphan nodes then elems
6806 TIDSortedElemSet::iterator itElem;
6807 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6808 for (int i=0; i<2; i++)
6809 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6811 const SMDS_MeshElement* elem = *itElem;
6815 // loop on elem nodes
6817 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6818 while ( itN->more() )
6820 const SMDS_MeshNode* node = cast2Node( itN->next() );
6821 // check if a node has been already transformed
6822 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6823 nodeMap.insert( make_pair ( node, node ));
6824 if ( !n2n_isnew.second )
6827 node->GetXYZ( coord );
6828 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6829 if ( theTargetMesh ) {
6830 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6831 n2n_isnew.first->second = newNode;
6832 myLastCreatedNodes.Append(newNode);
6833 srcNodes.Append( node );
6835 else if ( theCopy ) {
6836 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6837 n2n_isnew.first->second = newNode;
6838 myLastCreatedNodes.Append(newNode);
6839 srcNodes.Append( node );
6842 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6843 // node position on shape becomes invalid
6844 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6845 ( SMDS_SpacePosition::originSpacePosition() );
6848 // keep inverse elements
6849 if ( !theCopy && !theTargetMesh && needReverse ) {
6850 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6851 while ( invElemIt->more() ) {
6852 const SMDS_MeshElement* iel = invElemIt->next();
6853 inverseElemSet.insert( iel );
6857 } // loop on elems in { &orphanNode, &theElems };
6859 // either create new elements or reverse mirrored ones
6860 if ( !theCopy && !needReverse && !theTargetMesh )
6863 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6865 // Replicate or reverse elements
6867 std::vector<int> iForw;
6868 vector<const SMDS_MeshNode*> nodes;
6869 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6871 const SMDS_MeshElement* elem = *itElem;
6872 if ( !elem ) continue;
6874 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6875 size_t nbNodes = elem->NbNodes();
6876 if ( geomType == SMDSGeom_NONE ) continue; // node
6878 nodes.resize( nbNodes );
6880 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6882 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6886 bool allTransformed = true;
6887 int nbFaces = aPolyedre->NbFaces();
6888 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6890 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6891 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6893 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6894 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6895 if ( nodeMapIt == nodeMap.end() )
6896 allTransformed = false; // not all nodes transformed
6898 nodes.push_back((*nodeMapIt).second);
6900 if ( needReverse && allTransformed )
6901 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6903 if ( !allTransformed )
6904 continue; // not all nodes transformed
6906 else // ----------------------- the rest element types
6908 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6909 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6910 const vector<int>& i = needReverse ? iRev : iForw;
6912 // find transformed nodes
6914 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6915 while ( itN->more() ) {
6916 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6917 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6918 if ( nodeMapIt == nodeMap.end() )
6919 break; // not all nodes transformed
6920 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6922 if ( iNode != nbNodes )
6923 continue; // not all nodes transformed
6927 // copy in this or a new mesh
6928 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6929 srcElems.Append( elem );
6932 // reverse element as it was reversed by transformation
6934 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6937 } // loop on elements
6939 if ( editor && editor != this )
6940 myLastCreatedElems = editor->myLastCreatedElems;
6942 PGroupIDs newGroupIDs;
6944 if ( ( theMakeGroups && theCopy ) ||
6945 ( theMakeGroups && theTargetMesh ) )
6946 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6951 //=======================================================================
6953 * \brief Create groups of elements made during transformation
6954 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6955 * \param elemGens - elements making corresponding myLastCreatedElems
6956 * \param postfix - to append to names of new groups
6957 * \param targetMesh - mesh to create groups in
6958 * \param topPresent - is there "top" elements that are created by sweeping
6960 //=======================================================================
6962 SMESH_MeshEditor::PGroupIDs
6963 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6964 const SMESH_SequenceOfElemPtr& elemGens,
6965 const std::string& postfix,
6966 SMESH_Mesh* targetMesh,
6967 const bool topPresent)
6969 PGroupIDs newGroupIDs( new list<int> );
6970 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6972 // Sort existing groups by types and collect their names
6974 // containers to store an old group and generated new ones;
6975 // 1st new group is for result elems of different type than a source one;
6976 // 2nd new group is for same type result elems ("top" group at extrusion)
6978 using boost::make_tuple;
6979 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6980 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6981 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6983 set< string > groupNames;
6985 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6986 if ( !groupIt->more() ) return newGroupIDs;
6988 int newGroupID = mesh->GetGroupIds().back()+1;
6989 while ( groupIt->more() )
6991 SMESH_Group * group = groupIt->next();
6992 if ( !group ) continue;
6993 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6994 if ( !groupDS || groupDS->IsEmpty() ) continue;
6995 groupNames.insert ( group->GetName() );
6996 groupDS->SetStoreName( group->GetName() );
6997 const SMDSAbs_ElementType type = groupDS->GetType();
6998 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6999 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7000 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7001 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7004 // Loop on nodes and elements to add them in new groups
7006 vector< const SMDS_MeshElement* > resultElems;
7007 for ( int isNodes = 0; isNodes < 2; ++isNodes )
7009 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
7010 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7011 if ( gens.Length() != elems.Length() )
7012 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7014 // loop on created elements
7015 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7017 const SMDS_MeshElement* sourceElem = gens( iElem );
7018 if ( !sourceElem ) {
7019 MESSAGE("generateGroups(): NULL source element");
7022 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7023 if ( groupsOldNew.empty() ) { // no groups of this type at all
7024 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7025 ++iElem; // skip all elements made by sourceElem
7028 // collect all elements made by the iElem-th sourceElem
7029 resultElems.clear();
7030 if ( const SMDS_MeshElement* resElem = elems( iElem ))
7031 if ( resElem != sourceElem )
7032 resultElems.push_back( resElem );
7033 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7034 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7035 if ( resElem != sourceElem )
7036 resultElems.push_back( resElem );
7038 const SMDS_MeshElement* topElem = 0;
7039 if ( isNodes ) // there must be a top element
7041 topElem = resultElems.back();
7042 resultElems.pop_back();
7046 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7047 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7048 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7050 topElem = *resElemIt;
7051 *resElemIt = 0; // erase *resElemIt
7055 // add resultElems to groups originted from ones the sourceElem belongs to
7056 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7057 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7059 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7060 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7062 // fill in a new group
7063 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7064 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7065 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7067 newGroup.Add( *resElemIt );
7069 // fill a "top" group
7072 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7073 newTopGroup.Add( topElem );
7077 } // loop on created elements
7078 }// loop on nodes and elements
7080 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7082 list<int> topGrouIds;
7083 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7085 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
7086 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7087 orderedOldNewGroups[i]->get<2>() };
7088 for ( int is2nd = 0; is2nd < 2; ++is2nd )
7090 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7091 if ( newGroupDS->IsEmpty() )
7093 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7098 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7101 const bool isTop = ( topPresent &&
7102 newGroupDS->GetType() == oldGroupDS->GetType() &&
7105 string name = oldGroupDS->GetStoreName();
7106 { // remove trailing whitespaces (issue 22599)
7107 size_t size = name.size();
7108 while ( size > 1 && isspace( name[ size-1 ]))
7110 if ( size != name.size() )
7112 name.resize( size );
7113 oldGroupDS->SetStoreName( name.c_str() );
7116 if ( !targetMesh ) {
7117 string suffix = ( isTop ? "top": postfix.c_str() );
7121 while ( !groupNames.insert( name ).second ) // name exists
7122 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7127 newGroupDS->SetStoreName( name.c_str() );
7129 // make a SMESH_Groups
7130 mesh->AddGroup( newGroupDS );
7132 topGrouIds.push_back( newGroupDS->GetID() );
7134 newGroupIDs->push_back( newGroupDS->GetID() );
7138 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7143 //================================================================================
7145 * * \brief Return list of group of nodes close to each other within theTolerance
7146 * * Search among theNodes or in the whole mesh if theNodes is empty using
7147 * * an Octree algorithm
7148 * \param [in,out] theNodes - the nodes to treat
7149 * \param [in] theTolerance - the tolerance
7150 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7151 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7152 * corner and medium nodes in separate groups
7154 //================================================================================
7156 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7157 const double theTolerance,
7158 TListOfListOfNodes & theGroupsOfNodes,
7159 bool theSeparateCornersAndMedium)
7161 myLastCreatedElems.Clear();
7162 myLastCreatedNodes.Clear();
7164 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7165 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7166 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7167 theSeparateCornersAndMedium = false;
7169 TIDSortedNodeSet& corners = theNodes;
7170 TIDSortedNodeSet medium;
7172 if ( theNodes.empty() ) // get all nodes in the mesh
7174 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7175 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7176 if ( theSeparateCornersAndMedium )
7177 while ( nIt->more() )
7179 const SMDS_MeshNode* n = nIt->next();
7180 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7181 nodeSet->insert( nodeSet->end(), n );
7184 while ( nIt->more() )
7185 theNodes.insert( theNodes.end(),nIt->next() );
7187 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7189 TIDSortedNodeSet::iterator nIt = corners.begin();
7190 while ( nIt != corners.end() )
7191 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7193 medium.insert( medium.end(), *nIt );
7194 corners.erase( nIt++ );
7202 if ( !corners.empty() )
7203 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7204 if ( !medium.empty() )
7205 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7208 //=======================================================================
7209 //function : SimplifyFace
7210 //purpose : split a chain of nodes into several closed chains
7211 //=======================================================================
7213 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7214 vector<const SMDS_MeshNode *>& poly_nodes,
7215 vector<int>& quantities) const
7217 int nbNodes = faceNodes.size();
7222 set<const SMDS_MeshNode*> nodeSet;
7224 // get simple seq of nodes
7225 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7228 simpleNodes[iSimple++] = faceNodes[0];
7229 for (int iCur = 1; iCur < nbNodes; iCur++) {
7230 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7231 simpleNodes[iSimple++] = faceNodes[iCur];
7232 nodeSet.insert( faceNodes[iCur] );
7235 int nbUnique = nodeSet.size();
7236 int nbSimple = iSimple;
7237 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7247 bool foundLoop = (nbSimple > nbUnique);
7250 set<const SMDS_MeshNode*> loopSet;
7251 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7252 const SMDS_MeshNode* n = simpleNodes[iSimple];
7253 if (!loopSet.insert( n ).second) {
7257 int iC = 0, curLast = iSimple;
7258 for (; iC < curLast; iC++) {
7259 if (simpleNodes[iC] == n) break;
7261 int loopLen = curLast - iC;
7263 // create sub-element
7265 quantities.push_back(loopLen);
7266 for (; iC < curLast; iC++) {
7267 poly_nodes.push_back(simpleNodes[iC]);
7270 // shift the rest nodes (place from the first loop position)
7271 for (iC = curLast + 1; iC < nbSimple; iC++) {
7272 simpleNodes[iC - loopLen] = simpleNodes[iC];
7274 nbSimple -= loopLen;
7277 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7278 } // while (foundLoop)
7282 quantities.push_back(iSimple);
7283 for (int i = 0; i < iSimple; i++)
7284 poly_nodes.push_back(simpleNodes[i]);
7290 //=======================================================================
7291 //function : MergeNodes
7292 //purpose : In each group, the cdr of nodes are substituted by the first one
7294 //=======================================================================
7296 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7298 MESSAGE("MergeNodes");
7299 myLastCreatedElems.Clear();
7300 myLastCreatedNodes.Clear();
7302 SMESHDS_Mesh* aMesh = GetMeshDS();
7304 TNodeNodeMap nodeNodeMap; // node to replace - new node
7305 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7306 list< int > rmElemIds, rmNodeIds;
7308 // Fill nodeNodeMap and elems
7310 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7311 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7313 list<const SMDS_MeshNode*>& nodes = *grIt;
7314 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7315 const SMDS_MeshNode* nToKeep = *nIt;
7316 for ( ++nIt; nIt != nodes.end(); nIt++ )
7318 const SMDS_MeshNode* nToRemove = *nIt;
7319 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7320 if ( nToRemove != nToKeep )
7322 rmNodeIds.push_back( nToRemove->GetID() );
7323 AddToSameGroups( nToKeep, nToRemove, aMesh );
7324 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7325 // after MergeNodes() w/o creating node in place of merged ones.
7326 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7327 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7328 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7329 sm->SetIsAlwaysComputed( true );
7331 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7332 while ( invElemIt->more() ) {
7333 const SMDS_MeshElement* elem = invElemIt->next();
7338 // Change element nodes or remove an element
7340 set<const SMDS_MeshNode*> nodeSet;
7341 vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7343 ElemFeatures elemType;
7345 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7346 for ( ; eIt != elems.end(); eIt++ )
7348 const SMDS_MeshElement* elem = *eIt;
7349 const int nbNodes = elem->NbNodes();
7350 const int aShapeId = FindShape( elem );
7353 curNodes.resize( nbNodes );
7354 uniqueNodes.resize( nbNodes );
7355 iRepl.resize( nbNodes );
7356 int iUnique = 0, iCur = 0, nbRepl = 0;
7358 // get new seq of nodes
7359 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7360 while ( itN->more() )
7362 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7364 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7365 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7367 { ////////// BUG 0020185: begin
7368 bool stopRecur = false;
7369 set<const SMDS_MeshNode*> nodesRecur;
7370 nodesRecur.insert(n);
7371 while (!stopRecur) {
7372 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7373 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7374 n = (*nnIt_i).second;
7375 if (!nodesRecur.insert(n).second) {
7376 // error: recursive dependancy
7383 } ////////// BUG 0020185: end
7385 curNodes[ iCur ] = n;
7386 bool isUnique = nodeSet.insert( n ).second;
7388 uniqueNodes[ iUnique++ ] = n;
7390 iRepl[ nbRepl++ ] = iCur;
7394 // Analyse element topology after replacement
7397 int nbUniqueNodes = nodeSet.size();
7398 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7400 if (elem->IsPoly()) // Polygons and Polyhedral volumes
7402 if (elem->GetType() == SMDSAbs_Face) // Polygon
7404 elemType.Init( elem );
7405 const bool isQuad = elemType.myIsQuad;
7407 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7408 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7410 // a polygon can divide into several elements
7411 vector<const SMDS_MeshNode *> polygons_nodes;
7412 vector<int> quantities;
7413 int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7416 vector<const SMDS_MeshNode *> face_nodes;
7418 for (int iface = 0; iface < nbNew; iface++)
7420 int nbNewNodes = quantities[iface];
7421 face_nodes.assign( polygons_nodes.begin() + inode,
7422 polygons_nodes.begin() + inode + nbNewNodes );
7423 inode += nbNewNodes;
7424 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7426 bool isValid = ( nbNewNodes % 2 == 0 );
7427 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7428 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7429 elemType.SetQuad( isValid );
7430 if ( isValid ) // put medium nodes after corners
7431 SMDS_MeshCell::applyInterlaceRev
7432 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7433 nbNewNodes ), face_nodes );
7435 elemType.SetPoly(( nbNewNodes / ( elemType.myIsQuad + 1 ) > 4 ));
7437 SMDS_MeshElement* newElem = AddElement( face_nodes, elemType );
7439 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7442 rmElemIds.push_back(elem->GetID());
7446 else if (elem->GetType() == SMDSAbs_Volume) // Polyhedral volume
7448 if (nbUniqueNodes < 4) {
7449 rmElemIds.push_back(elem->GetID());
7452 // each face has to be analyzed in order to check volume validity
7453 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
7456 int nbFaces = aPolyedre->NbFaces();
7458 vector<const SMDS_MeshNode *> poly_nodes;
7459 vector<int> quantities;
7461 for (int iface = 1; iface <= nbFaces; iface++) {
7462 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7463 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7465 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7466 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7467 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7468 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7469 faceNode = (*nnIt).second;
7471 faceNodes[inode - 1] = faceNode;
7474 SimplifyFace(faceNodes, poly_nodes, quantities);
7477 if (quantities.size() > 3) {
7478 // to be done: remove coincident faces
7481 if (quantities.size() > 3)
7483 const SMDS_MeshElement* newElem =
7484 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7485 myLastCreatedElems.Append(newElem);
7486 if ( aShapeId && newElem )
7487 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7488 rmElemIds.push_back(elem->GetID());
7492 rmElemIds.push_back(elem->GetID());
7503 // TODO not all the possible cases are solved. Find something more generic?
7504 switch ( nbNodes ) {
7505 case 2: ///////////////////////////////////// EDGE
7506 isOk = false; break;
7507 case 3: ///////////////////////////////////// TRIANGLE
7508 isOk = false; break;
7510 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7512 else { //////////////////////////////////// QUADRANGLE
7513 if ( nbUniqueNodes < 3 )
7515 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7516 isOk = false; // opposite nodes stick
7517 //MESSAGE("isOk " << isOk);
7520 case 6: ///////////////////////////////////// PENTAHEDRON
7521 if ( nbUniqueNodes == 4 ) {
7522 // ---------------------------------> tetrahedron
7524 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7525 // all top nodes stick: reverse a bottom
7526 uniqueNodes[ 0 ] = curNodes [ 1 ];
7527 uniqueNodes[ 1 ] = curNodes [ 0 ];
7529 else if (nbRepl == 3 &&
7530 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7531 // all bottom nodes stick: set a top before
7532 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7533 uniqueNodes[ 0 ] = curNodes [ 3 ];
7534 uniqueNodes[ 1 ] = curNodes [ 4 ];
7535 uniqueNodes[ 2 ] = curNodes [ 5 ];
7537 else if (nbRepl == 4 &&
7538 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7539 // a lateral face turns into a line: reverse a bottom
7540 uniqueNodes[ 0 ] = curNodes [ 1 ];
7541 uniqueNodes[ 1 ] = curNodes [ 0 ];
7546 else if ( nbUniqueNodes == 5 ) {
7547 // PENTAHEDRON --------------------> 2 tetrahedrons
7548 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7549 // a bottom node sticks with a linked top one
7551 SMDS_MeshElement* newElem =
7552 aMesh->AddVolume(curNodes[ 3 ],
7555 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7556 myLastCreatedElems.Append(newElem);
7558 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7559 // 2. : reverse a bottom
7560 uniqueNodes[ 0 ] = curNodes [ 1 ];
7561 uniqueNodes[ 1 ] = curNodes [ 0 ];
7571 if(elem->IsQuadratic()) { // Quadratic quadrangle
7583 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7586 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7588 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7589 uniqueNodes[0] = curNodes[0];
7590 uniqueNodes[1] = curNodes[2];
7591 uniqueNodes[2] = curNodes[3];
7592 uniqueNodes[3] = curNodes[5];
7593 uniqueNodes[4] = curNodes[6];
7594 uniqueNodes[5] = curNodes[7];
7597 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7598 uniqueNodes[0] = curNodes[0];
7599 uniqueNodes[1] = curNodes[1];
7600 uniqueNodes[2] = curNodes[2];
7601 uniqueNodes[3] = curNodes[4];
7602 uniqueNodes[4] = curNodes[5];
7603 uniqueNodes[5] = curNodes[6];
7606 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7607 uniqueNodes[0] = curNodes[1];
7608 uniqueNodes[1] = curNodes[2];
7609 uniqueNodes[2] = curNodes[3];
7610 uniqueNodes[3] = curNodes[5];
7611 uniqueNodes[4] = curNodes[6];
7612 uniqueNodes[5] = curNodes[0];
7615 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7616 uniqueNodes[0] = curNodes[0];
7617 uniqueNodes[1] = curNodes[1];
7618 uniqueNodes[2] = curNodes[3];
7619 uniqueNodes[3] = curNodes[4];
7620 uniqueNodes[4] = curNodes[6];
7621 uniqueNodes[5] = curNodes[7];
7624 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7625 uniqueNodes[0] = curNodes[0];
7626 uniqueNodes[1] = curNodes[2];
7627 uniqueNodes[2] = curNodes[3];
7628 uniqueNodes[3] = curNodes[1];
7629 uniqueNodes[4] = curNodes[6];
7630 uniqueNodes[5] = curNodes[7];
7633 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7634 uniqueNodes[0] = curNodes[0];
7635 uniqueNodes[1] = curNodes[1];
7636 uniqueNodes[2] = curNodes[2];
7637 uniqueNodes[3] = curNodes[4];
7638 uniqueNodes[4] = curNodes[5];
7639 uniqueNodes[5] = curNodes[7];
7642 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7643 uniqueNodes[0] = curNodes[0];
7644 uniqueNodes[1] = curNodes[1];
7645 uniqueNodes[2] = curNodes[3];
7646 uniqueNodes[3] = curNodes[4];
7647 uniqueNodes[4] = curNodes[2];
7648 uniqueNodes[5] = curNodes[7];
7651 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7652 uniqueNodes[0] = curNodes[0];
7653 uniqueNodes[1] = curNodes[1];
7654 uniqueNodes[2] = curNodes[2];
7655 uniqueNodes[3] = curNodes[4];
7656 uniqueNodes[4] = curNodes[5];
7657 uniqueNodes[5] = curNodes[3];
7662 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7665 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7669 //////////////////////////////////// HEXAHEDRON
7671 SMDS_VolumeTool hexa (elem);
7672 hexa.SetExternalNormal();
7673 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7674 //////////////////////// HEX ---> 1 tetrahedron
7675 for ( int iFace = 0; iFace < 6; iFace++ ) {
7676 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7677 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7678 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7679 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7680 // one face turns into a point ...
7681 int iOppFace = hexa.GetOppFaceIndex( iFace );
7682 ind = hexa.GetFaceNodesIndices( iOppFace );
7684 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7685 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7688 if ( nbStick == 1 ) {
7689 // ... and the opposite one - into a triangle.
7691 ind = hexa.GetFaceNodesIndices( iFace );
7692 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7699 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7700 //////////////////////// HEX ---> 1 prism
7701 int nbTria = 0, iTria[3];
7702 const int *ind; // indices of face nodes
7703 // look for triangular faces
7704 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7705 ind = hexa.GetFaceNodesIndices( iFace );
7706 TIDSortedNodeSet faceNodes;
7707 for ( iCur = 0; iCur < 4; iCur++ )
7708 faceNodes.insert( curNodes[ind[iCur]] );
7709 if ( faceNodes.size() == 3 )
7710 iTria[ nbTria++ ] = iFace;
7712 // check if triangles are opposite
7713 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7716 // set nodes of the bottom triangle
7717 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7719 for ( iCur = 0; iCur < 4; iCur++ )
7720 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7721 indB.push_back( ind[iCur] );
7722 if ( !hexa.IsForward() )
7723 std::swap( indB[0], indB[2] );
7724 for ( iCur = 0; iCur < 3; iCur++ )
7725 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7726 // set nodes of the top triangle
7727 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7728 for ( iCur = 0; iCur < 3; ++iCur )
7729 for ( int j = 0; j < 4; ++j )
7730 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7732 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7738 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7739 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7740 for ( int iFace = 0; iFace < 6; iFace++ ) {
7741 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7742 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7743 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7744 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7745 // one face turns into a point ...
7746 int iOppFace = hexa.GetOppFaceIndex( iFace );
7747 ind = hexa.GetFaceNodesIndices( iOppFace );
7749 iUnique = 2; // reverse a tetrahedron 1 bottom
7750 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7751 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7753 else if ( iUnique >= 0 )
7754 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7756 if ( nbStick == 0 ) {
7757 // ... and the opposite one is a quadrangle
7759 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7760 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7763 SMDS_MeshElement* newElem =
7764 aMesh->AddVolume(curNodes[ind[ 0 ]],
7767 curNodes[indTop[ 0 ]]);
7768 myLastCreatedElems.Append(newElem);
7770 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7777 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7778 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7779 // find indices of quad and tri faces
7780 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7781 for ( iFace = 0; iFace < 6; iFace++ ) {
7782 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7784 for ( iCur = 0; iCur < 4; iCur++ )
7785 nodeSet.insert( curNodes[ind[ iCur ]] );
7786 nbUniqueNodes = nodeSet.size();
7787 if ( nbUniqueNodes == 3 )
7788 iTriFace[ nbTri++ ] = iFace;
7789 else if ( nbUniqueNodes == 4 )
7790 iQuadFace[ nbQuad++ ] = iFace;
7792 if (nbQuad == 2 && nbTri == 4 &&
7793 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7794 // 2 opposite quadrangles stuck with a diagonal;
7795 // sample groups of merged indices: (0-4)(2-6)
7796 // --------------------------------------------> 2 tetrahedrons
7797 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7798 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7799 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7800 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7801 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7802 // stuck with 0-2 diagonal
7810 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7811 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7812 // stuck with 1-3 diagonal
7824 uniqueNodes[ 0 ] = curNodes [ i0 ];
7825 uniqueNodes[ 1 ] = curNodes [ i1d ];
7826 uniqueNodes[ 2 ] = curNodes [ i3d ];
7827 uniqueNodes[ 3 ] = curNodes [ i0t ];
7830 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7834 myLastCreatedElems.Append(newElem);
7836 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7839 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7840 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7841 // --------------------------------------------> prism
7842 // find 2 opposite triangles
7844 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7845 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7846 // find indices of kept and replaced nodes
7847 // and fill unique nodes of 2 opposite triangles
7848 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7849 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7850 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7851 // fill unique nodes
7854 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7855 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7856 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7858 // iCur of a linked node of the opposite face (make normals co-directed):
7859 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7860 // check that correspondent corners of triangles are linked
7861 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7864 uniqueNodes[ iUnique ] = n;
7865 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7874 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7877 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7884 } // switch ( nbNodes )
7886 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7888 if ( isOk ) // the non-poly elem remains valid after sticking nodes
7890 if ( nbNodes != nbUniqueNodes ||
7891 !aMesh->ChangeElementNodes( elem, & curNodes[0], nbNodes ))
7893 elemType.Init( elem ).SetID( elem->GetID() );
7895 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7896 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7898 uniqueNodes.resize(nbUniqueNodes);
7899 SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7900 if ( sm && newElem )
7901 sm->AddElement( newElem );
7902 if ( elem != newElem )
7903 ReplaceElemInGroups( elem, newElem, aMesh );
7907 // Remove invalid regular element or invalid polygon
7908 rmElemIds.push_back( elem->GetID() );
7911 } // loop on elements
7913 // Remove bad elements, then equal nodes (order important)
7915 Remove( rmElemIds, false );
7916 Remove( rmNodeIds, true );
7922 // ========================================================
7923 // class : SortableElement
7924 // purpose : allow sorting elements basing on their nodes
7925 // ========================================================
7926 class SortableElement : public set <const SMDS_MeshElement*>
7930 SortableElement( const SMDS_MeshElement* theElem )
7933 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7934 while ( nodeIt->more() )
7935 this->insert( nodeIt->next() );
7938 const SMDS_MeshElement* Get() const
7942 mutable const SMDS_MeshElement* myElem;
7945 //=======================================================================
7946 //function : FindEqualElements
7947 //purpose : Return list of group of elements built on the same nodes.
7948 // Search among theElements or in the whole mesh if theElements is empty
7949 //=======================================================================
7951 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7952 TListOfListOfElementsID & theGroupsOfElementsID)
7954 myLastCreatedElems.Clear();
7955 myLastCreatedNodes.Clear();
7957 typedef map< SortableElement, int > TMapOfNodeSet;
7958 typedef list<int> TGroupOfElems;
7960 if ( theElements.empty() )
7961 { // get all elements in the mesh
7962 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7963 while ( eIt->more() )
7964 theElements.insert( theElements.end(), eIt->next() );
7967 vector< TGroupOfElems > arrayOfGroups;
7968 TGroupOfElems groupOfElems;
7969 TMapOfNodeSet mapOfNodeSet;
7971 TIDSortedElemSet::iterator elemIt = theElements.begin();
7972 for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7974 const SMDS_MeshElement* curElem = *elemIt;
7975 SortableElement SE(curElem);
7977 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7978 if ( !pp.second ) { // one more coincident elem
7979 TMapOfNodeSet::iterator& itSE = pp.first;
7980 int ind = (*itSE).second;
7981 arrayOfGroups[ind].push_back( curElem->GetID() );
7984 arrayOfGroups.push_back( groupOfElems );
7985 arrayOfGroups.back().push_back( curElem->GetID() );
7990 groupOfElems.clear();
7991 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7992 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7994 if ( groupIt->size() > 1 ) {
7995 //groupOfElems.sort(); -- theElements is sorted already
7996 theGroupsOfElementsID.push_back( groupOfElems );
7997 theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8002 //=======================================================================
8003 //function : MergeElements
8004 //purpose : In each given group, substitute all elements by the first one.
8005 //=======================================================================
8007 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8009 myLastCreatedElems.Clear();
8010 myLastCreatedNodes.Clear();
8012 typedef list<int> TListOfIDs;
8013 TListOfIDs rmElemIds; // IDs of elems to remove
8015 SMESHDS_Mesh* aMesh = GetMeshDS();
8017 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8018 while ( groupsIt != theGroupsOfElementsID.end() ) {
8019 TListOfIDs& aGroupOfElemID = *groupsIt;
8020 aGroupOfElemID.sort();
8021 int elemIDToKeep = aGroupOfElemID.front();
8022 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8023 aGroupOfElemID.pop_front();
8024 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8025 while ( idIt != aGroupOfElemID.end() ) {
8026 int elemIDToRemove = *idIt;
8027 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8028 // add the kept element in groups of removed one (PAL15188)
8029 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8030 rmElemIds.push_back( elemIDToRemove );
8036 Remove( rmElemIds, false );
8039 //=======================================================================
8040 //function : MergeEqualElements
8041 //purpose : Remove all but one of elements built on the same nodes.
8042 //=======================================================================
8044 void SMESH_MeshEditor::MergeEqualElements()
8046 TIDSortedElemSet aMeshElements; /* empty input ==
8047 to merge equal elements in the whole mesh */
8048 TListOfListOfElementsID aGroupsOfElementsID;
8049 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8050 MergeElements(aGroupsOfElementsID);
8053 //=======================================================================
8054 //function : findAdjacentFace
8056 //=======================================================================
8058 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8059 const SMDS_MeshNode* n2,
8060 const SMDS_MeshElement* elem)
8062 TIDSortedElemSet elemSet, avoidSet;
8064 avoidSet.insert ( elem );
8065 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8068 //=======================================================================
8069 //function : findSegment
8070 //purpose : Return a mesh segment by two nodes one of which can be medium
8071 //=======================================================================
8073 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8074 const SMDS_MeshNode* n2)
8076 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8077 while ( it->more() )
8079 const SMDS_MeshElement* seg = it->next();
8080 if ( seg->GetNodeIndex( n2 ) >= 0 )
8086 //=======================================================================
8087 //function : FindFreeBorder
8089 //=======================================================================
8091 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8093 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8094 const SMDS_MeshNode* theSecondNode,
8095 const SMDS_MeshNode* theLastNode,
8096 list< const SMDS_MeshNode* > & theNodes,
8097 list< const SMDS_MeshElement* >& theFaces)
8099 if ( !theFirstNode || !theSecondNode )
8101 // find border face between theFirstNode and theSecondNode
8102 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8106 theFaces.push_back( curElem );
8107 theNodes.push_back( theFirstNode );
8108 theNodes.push_back( theSecondNode );
8110 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8111 TIDSortedElemSet foundElems;
8112 bool needTheLast = ( theLastNode != 0 );
8114 while ( nStart != theLastNode ) {
8115 if ( nStart == theFirstNode )
8116 return !needTheLast;
8118 // find all free border faces sharing form nStart
8120 list< const SMDS_MeshElement* > curElemList;
8121 list< const SMDS_MeshNode* > nStartList;
8122 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8123 while ( invElemIt->more() ) {
8124 const SMDS_MeshElement* e = invElemIt->next();
8125 if ( e == curElem || foundElems.insert( e ).second ) {
8127 int iNode = 0, nbNodes = e->NbNodes();
8128 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8130 if ( e->IsQuadratic() ) {
8131 const SMDS_VtkFace* F =
8132 dynamic_cast<const SMDS_VtkFace*>(e);
8133 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8134 // use special nodes iterator
8135 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8136 while( anIter->more() ) {
8137 nodes[ iNode++ ] = cast2Node(anIter->next());
8141 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8142 while ( nIt->more() )
8143 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8145 nodes[ iNode ] = nodes[ 0 ];
8147 for ( iNode = 0; iNode < nbNodes; iNode++ )
8148 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8149 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8150 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8152 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8153 curElemList.push_back( e );
8157 // analyse the found
8159 int nbNewBorders = curElemList.size();
8160 if ( nbNewBorders == 0 ) {
8161 // no free border furthermore
8162 return !needTheLast;
8164 else if ( nbNewBorders == 1 ) {
8165 // one more element found
8167 nStart = nStartList.front();
8168 curElem = curElemList.front();
8169 theFaces.push_back( curElem );
8170 theNodes.push_back( nStart );
8173 // several continuations found
8174 list< const SMDS_MeshElement* >::iterator curElemIt;
8175 list< const SMDS_MeshNode* >::iterator nStartIt;
8176 // check if one of them reached the last node
8177 if ( needTheLast ) {
8178 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8179 curElemIt!= curElemList.end();
8180 curElemIt++, nStartIt++ )
8181 if ( *nStartIt == theLastNode ) {
8182 theFaces.push_back( *curElemIt );
8183 theNodes.push_back( *nStartIt );
8187 // find the best free border by the continuations
8188 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8189 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8190 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8191 curElemIt!= curElemList.end();
8192 curElemIt++, nStartIt++ )
8194 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8195 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8196 // find one more free border
8197 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8201 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8202 // choice: clear a worse one
8203 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8204 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8205 contNodes[ iWorse ].clear();
8206 contFaces[ iWorse ].clear();
8209 if ( contNodes[0].empty() && contNodes[1].empty() )
8212 // append the best free border
8213 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8214 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8215 theNodes.pop_back(); // remove nIgnore
8216 theNodes.pop_back(); // remove nStart
8217 theFaces.pop_back(); // remove curElem
8218 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8219 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8220 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8221 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8224 } // several continuations found
8225 } // while ( nStart != theLastNode )
8230 //=======================================================================
8231 //function : CheckFreeBorderNodes
8232 //purpose : Return true if the tree nodes are on a free border
8233 //=======================================================================
8235 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8236 const SMDS_MeshNode* theNode2,
8237 const SMDS_MeshNode* theNode3)
8239 list< const SMDS_MeshNode* > nodes;
8240 list< const SMDS_MeshElement* > faces;
8241 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8244 //=======================================================================
8245 //function : SewFreeBorder
8247 //warning : for border-to-side sewing theSideSecondNode is considered as
8248 // the last side node and theSideThirdNode is not used
8249 //=======================================================================
8251 SMESH_MeshEditor::Sew_Error
8252 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8253 const SMDS_MeshNode* theBordSecondNode,
8254 const SMDS_MeshNode* theBordLastNode,
8255 const SMDS_MeshNode* theSideFirstNode,
8256 const SMDS_MeshNode* theSideSecondNode,
8257 const SMDS_MeshNode* theSideThirdNode,
8258 const bool theSideIsFreeBorder,
8259 const bool toCreatePolygons,
8260 const bool toCreatePolyedrs)
8262 myLastCreatedElems.Clear();
8263 myLastCreatedNodes.Clear();
8265 MESSAGE("::SewFreeBorder()");
8266 Sew_Error aResult = SEW_OK;
8268 // ====================================
8269 // find side nodes and elements
8270 // ====================================
8272 list< const SMDS_MeshNode* > nSide[ 2 ];
8273 list< const SMDS_MeshElement* > eSide[ 2 ];
8274 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8275 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8279 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8280 nSide[0], eSide[0])) {
8281 MESSAGE(" Free Border 1 not found " );
8282 aResult = SEW_BORDER1_NOT_FOUND;
8284 if (theSideIsFreeBorder) {
8287 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8288 nSide[1], eSide[1])) {
8289 MESSAGE(" Free Border 2 not found " );
8290 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8293 if ( aResult != SEW_OK )
8296 if (!theSideIsFreeBorder) {
8300 // -------------------------------------------------------------------------
8302 // 1. If nodes to merge are not coincident, move nodes of the free border
8303 // from the coord sys defined by the direction from the first to last
8304 // nodes of the border to the correspondent sys of the side 2
8305 // 2. On the side 2, find the links most co-directed with the correspondent
8306 // links of the free border
8307 // -------------------------------------------------------------------------
8309 // 1. Since sewing may break if there are volumes to split on the side 2,
8310 // we wont move nodes but just compute new coordinates for them
8311 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8312 TNodeXYZMap nBordXYZ;
8313 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8314 list< const SMDS_MeshNode* >::iterator nBordIt;
8316 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8317 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8318 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8319 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8320 double tol2 = 1.e-8;
8321 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8322 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8323 // Need node movement.
8325 // find X and Z axes to create trsf
8326 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8328 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8330 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8333 gp_Ax3 toBordAx( Pb1, Zb, X );
8334 gp_Ax3 fromSideAx( Ps1, Zs, X );
8335 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8337 gp_Trsf toBordSys, fromSide2Sys;
8338 toBordSys.SetTransformation( toBordAx );
8339 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8340 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8343 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8344 const SMDS_MeshNode* n = *nBordIt;
8345 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8346 toBordSys.Transforms( xyz );
8347 fromSide2Sys.Transforms( xyz );
8348 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8352 // just insert nodes XYZ in the nBordXYZ map
8353 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8354 const SMDS_MeshNode* n = *nBordIt;
8355 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8359 // 2. On the side 2, find the links most co-directed with the correspondent
8360 // links of the free border
8362 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8363 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8364 sideNodes.push_back( theSideFirstNode );
8366 bool hasVolumes = false;
8367 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8368 set<long> foundSideLinkIDs, checkedLinkIDs;
8369 SMDS_VolumeTool volume;
8370 //const SMDS_MeshNode* faceNodes[ 4 ];
8372 const SMDS_MeshNode* sideNode;
8373 const SMDS_MeshElement* sideElem;
8374 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8375 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8376 nBordIt = bordNodes.begin();
8378 // border node position and border link direction to compare with
8379 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8380 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8381 // choose next side node by link direction or by closeness to
8382 // the current border node:
8383 bool searchByDir = ( *nBordIt != theBordLastNode );
8385 // find the next node on the Side 2
8387 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8389 checkedLinkIDs.clear();
8390 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8392 // loop on inverse elements of current node (prevSideNode) on the Side 2
8393 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8394 while ( invElemIt->more() )
8396 const SMDS_MeshElement* elem = invElemIt->next();
8397 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8398 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8399 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8400 bool isVolume = volume.Set( elem );
8401 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8402 if ( isVolume ) // --volume
8404 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8405 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8406 if(elem->IsQuadratic()) {
8407 const SMDS_VtkFace* F =
8408 dynamic_cast<const SMDS_VtkFace*>(elem);
8409 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8410 // use special nodes iterator
8411 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8412 while( anIter->more() ) {
8413 nodes[ iNode ] = cast2Node(anIter->next());
8414 if ( nodes[ iNode++ ] == prevSideNode )
8415 iPrevNode = iNode - 1;
8419 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8420 while ( nIt->more() ) {
8421 nodes[ iNode ] = cast2Node( nIt->next() );
8422 if ( nodes[ iNode++ ] == prevSideNode )
8423 iPrevNode = iNode - 1;
8426 // there are 2 links to check
8431 // loop on links, to be precise, on the second node of links
8432 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8433 const SMDS_MeshNode* n = nodes[ iNode ];
8435 if ( !volume.IsLinked( n, prevSideNode ))
8439 if ( iNode ) // a node before prevSideNode
8440 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8441 else // a node after prevSideNode
8442 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8444 // check if this link was already used
8445 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8446 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8447 if (!isJustChecked &&
8448 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8450 // test a link geometrically
8451 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8452 bool linkIsBetter = false;
8453 double dot = 0.0, dist = 0.0;
8454 if ( searchByDir ) { // choose most co-directed link
8455 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8456 linkIsBetter = ( dot > maxDot );
8458 else { // choose link with the node closest to bordPos
8459 dist = ( nextXYZ - bordPos ).SquareModulus();
8460 linkIsBetter = ( dist < minDist );
8462 if ( linkIsBetter ) {
8471 } // loop on inverse elements of prevSideNode
8474 MESSAGE(" Cant find path by links of the Side 2 ");
8475 return SEW_BAD_SIDE_NODES;
8477 sideNodes.push_back( sideNode );
8478 sideElems.push_back( sideElem );
8479 foundSideLinkIDs.insert ( linkID );
8480 prevSideNode = sideNode;
8482 if ( *nBordIt == theBordLastNode )
8483 searchByDir = false;
8485 // find the next border link to compare with
8486 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8487 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8488 // move to next border node if sideNode is before forward border node (bordPos)
8489 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8490 prevBordNode = *nBordIt;
8492 bordPos = nBordXYZ[ *nBordIt ];
8493 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8494 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8498 while ( sideNode != theSideSecondNode );
8500 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8501 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8502 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8504 } // end nodes search on the side 2
8506 // ============================
8507 // sew the border to the side 2
8508 // ============================
8510 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8511 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8513 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8514 if ( toMergeConformal && toCreatePolygons )
8516 // do not merge quadrangles if polygons are OK (IPAL0052824)
8517 eIt[0] = eSide[0].begin();
8518 eIt[1] = eSide[1].begin();
8519 bool allQuads[2] = { true, true };
8520 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8521 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8522 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8524 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8527 TListOfListOfNodes nodeGroupsToMerge;
8528 if (( toMergeConformal ) ||
8529 ( theSideIsFreeBorder && !theSideThirdNode )) {
8531 // all nodes are to be merged
8533 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8534 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8535 nIt[0]++, nIt[1]++ )
8537 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8538 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8539 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8544 // insert new nodes into the border and the side to get equal nb of segments
8546 // get normalized parameters of nodes on the borders
8547 vector< double > param[ 2 ];
8548 param[0].resize( maxNbNodes );
8549 param[1].resize( maxNbNodes );
8551 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8552 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8553 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8554 const SMDS_MeshNode* nPrev = *nIt;
8555 double bordLength = 0;
8556 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8557 const SMDS_MeshNode* nCur = *nIt;
8558 gp_XYZ segment (nCur->X() - nPrev->X(),
8559 nCur->Y() - nPrev->Y(),
8560 nCur->Z() - nPrev->Z());
8561 double segmentLen = segment.Modulus();
8562 bordLength += segmentLen;
8563 param[ iBord ][ iNode ] = bordLength;
8566 // normalize within [0,1]
8567 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8568 param[ iBord ][ iNode ] /= bordLength;
8572 // loop on border segments
8573 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8574 int i[ 2 ] = { 0, 0 };
8575 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8576 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8578 TElemOfNodeListMap insertMap;
8579 TElemOfNodeListMap::iterator insertMapIt;
8581 // key: elem to insert nodes into
8582 // value: 2 nodes to insert between + nodes to be inserted
8584 bool next[ 2 ] = { false, false };
8586 // find min adjacent segment length after sewing
8587 double nextParam = 10., prevParam = 0;
8588 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8589 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8590 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8591 if ( i[ iBord ] > 0 )
8592 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8594 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8595 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8596 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8598 // choose to insert or to merge nodes
8599 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8600 if ( Abs( du ) <= minSegLen * 0.2 ) {
8603 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8604 const SMDS_MeshNode* n0 = *nIt[0];
8605 const SMDS_MeshNode* n1 = *nIt[1];
8606 nodeGroupsToMerge.back().push_back( n1 );
8607 nodeGroupsToMerge.back().push_back( n0 );
8608 // position of node of the border changes due to merge
8609 param[ 0 ][ i[0] ] += du;
8610 // move n1 for the sake of elem shape evaluation during insertion.
8611 // n1 will be removed by MergeNodes() anyway
8612 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8613 next[0] = next[1] = true;
8618 int intoBord = ( du < 0 ) ? 0 : 1;
8619 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8620 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8621 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8622 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8623 if ( intoBord == 1 ) {
8624 // move node of the border to be on a link of elem of the side
8625 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8626 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8627 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8628 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8629 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8631 insertMapIt = insertMap.find( elem );
8632 bool notFound = ( insertMapIt == insertMap.end() );
8633 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8635 // insert into another link of the same element:
8636 // 1. perform insertion into the other link of the elem
8637 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8638 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8639 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8640 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8641 // 2. perform insertion into the link of adjacent faces
8642 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8643 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8645 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8646 InsertNodesIntoLink( seg, n12, n22, nodeList );
8648 if (toCreatePolyedrs) {
8649 // perform insertion into the links of adjacent volumes
8650 UpdateVolumes(n12, n22, nodeList);
8652 // 3. find an element appeared on n1 and n2 after the insertion
8653 insertMap.erase( elem );
8654 elem = findAdjacentFace( n1, n2, 0 );
8656 if ( notFound || otherLink ) {
8657 // add element and nodes of the side into the insertMap
8658 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8659 (*insertMapIt).second.push_back( n1 );
8660 (*insertMapIt).second.push_back( n2 );
8662 // add node to be inserted into elem
8663 (*insertMapIt).second.push_back( nIns );
8664 next[ 1 - intoBord ] = true;
8667 // go to the next segment
8668 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8669 if ( next[ iBord ] ) {
8670 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8672 nPrev[ iBord ] = *nIt[ iBord ];
8673 nIt[ iBord ]++; i[ iBord ]++;
8677 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8679 // perform insertion of nodes into elements
8681 for (insertMapIt = insertMap.begin();
8682 insertMapIt != insertMap.end();
8685 const SMDS_MeshElement* elem = (*insertMapIt).first;
8686 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8687 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8688 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8690 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8692 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8693 InsertNodesIntoLink( seg, n1, n2, nodeList );
8696 if ( !theSideIsFreeBorder ) {
8697 // look for and insert nodes into the faces adjacent to elem
8698 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8699 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8702 if (toCreatePolyedrs) {
8703 // perform insertion into the links of adjacent volumes
8704 UpdateVolumes(n1, n2, nodeList);
8707 } // end: insert new nodes
8709 MergeNodes ( nodeGroupsToMerge );
8712 // Remove coincident segments
8715 TIDSortedElemSet segments;
8716 SMESH_SequenceOfElemPtr newFaces;
8717 for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8719 if ( !myLastCreatedElems(i) ) continue;
8720 if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8721 segments.insert( segments.end(), myLastCreatedElems(i) );
8723 newFaces.Append( myLastCreatedElems(i) );
8725 // get segments adjacent to merged nodes
8726 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8727 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8729 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8730 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8731 while ( segIt->more() )
8732 segments.insert( segIt->next() );
8736 TListOfListOfElementsID equalGroups;
8737 if ( !segments.empty() )
8738 FindEqualElements( segments, equalGroups );
8739 if ( !equalGroups.empty() )
8741 // remove from segments those that will be removed
8742 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8743 for ( ; itGroups != equalGroups.end(); ++itGroups )
8745 list< int >& group = *itGroups;
8746 list< int >::iterator id = group.begin();
8747 for ( ++id; id != group.end(); ++id )
8748 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8749 segments.erase( seg );
8751 // remove equal segments
8752 MergeElements( equalGroups );
8754 // restore myLastCreatedElems
8755 myLastCreatedElems = newFaces;
8756 TIDSortedElemSet::iterator seg = segments.begin();
8757 for ( ; seg != segments.end(); ++seg )
8758 myLastCreatedElems.Append( *seg );
8764 //=======================================================================
8765 //function : InsertNodesIntoLink
8766 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8767 // and theBetweenNode2 and split theElement
8768 //=======================================================================
8770 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8771 const SMDS_MeshNode* theBetweenNode1,
8772 const SMDS_MeshNode* theBetweenNode2,
8773 list<const SMDS_MeshNode*>& theNodesToInsert,
8774 const bool toCreatePoly)
8776 if ( !theElement ) return;
8778 SMESHDS_Mesh *aMesh = GetMeshDS();
8779 vector<const SMDS_MeshElement*> newElems;
8781 if ( theElement->GetType() == SMDSAbs_Edge )
8783 theNodesToInsert.push_front( theBetweenNode1 );
8784 theNodesToInsert.push_back ( theBetweenNode2 );
8785 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8786 const SMDS_MeshNode* n1 = *n;
8787 for ( ++n; n != theNodesToInsert.end(); ++n )
8789 const SMDS_MeshNode* n2 = *n;
8790 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8791 AddToSameGroups( seg, theElement, aMesh );
8793 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8796 theNodesToInsert.pop_front();
8797 theNodesToInsert.pop_back();
8799 if ( theElement->IsQuadratic() ) // add a not split part
8801 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8802 theElement->end_nodes() );
8803 int iOther = 0, nbN = nodes.size();
8804 for ( ; iOther < nbN; ++iOther )
8805 if ( nodes[iOther] != theBetweenNode1 &&
8806 nodes[iOther] != theBetweenNode2 )
8810 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8811 AddToSameGroups( seg, theElement, aMesh );
8813 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8815 else if ( iOther == 2 )
8817 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8818 AddToSameGroups( seg, theElement, aMesh );
8820 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8823 // treat new elements
8824 for ( size_t i = 0; i < newElems.size(); ++i )
8827 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8828 myLastCreatedElems.Append( newElems[i] );
8830 ReplaceElemInGroups( theElement, newElems, aMesh );
8831 aMesh->RemoveElement( theElement );
8834 } // if ( theElement->GetType() == SMDSAbs_Edge )
8836 const SMDS_MeshElement* theFace = theElement;
8837 if ( theFace->GetType() != SMDSAbs_Face ) return;
8839 // find indices of 2 link nodes and of the rest nodes
8840 int iNode = 0, il1, il2, i3, i4;
8841 il1 = il2 = i3 = i4 = -1;
8842 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8844 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8845 while ( nodeIt->more() ) {
8846 const SMDS_MeshNode* n = nodeIt->next();
8847 if ( n == theBetweenNode1 )
8849 else if ( n == theBetweenNode2 )
8855 nodes[ iNode++ ] = n;
8857 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8860 // arrange link nodes to go one after another regarding the face orientation
8861 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8862 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8867 aNodesToInsert.reverse();
8869 // check that not link nodes of a quadrangles are in good order
8870 int nbFaceNodes = theFace->NbNodes();
8871 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8877 if (toCreatePoly || theFace->IsPoly()) {
8880 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8882 // add nodes of face up to first node of link
8885 if ( theFace->IsQuadratic() ) {
8886 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8887 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8888 // use special nodes iterator
8889 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8890 while( anIter->more() && !isFLN ) {
8891 const SMDS_MeshNode* n = cast2Node(anIter->next());
8892 poly_nodes[iNode++] = n;
8893 if (n == nodes[il1]) {
8897 // add nodes to insert
8898 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8899 for (; nIt != aNodesToInsert.end(); nIt++) {
8900 poly_nodes[iNode++] = *nIt;
8902 // add nodes of face starting from last node of link
8903 while ( anIter->more() ) {
8904 poly_nodes[iNode++] = cast2Node(anIter->next());
8908 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8909 while ( nodeIt->more() && !isFLN ) {
8910 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8911 poly_nodes[iNode++] = n;
8912 if (n == nodes[il1]) {
8916 // add nodes to insert
8917 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8918 for (; nIt != aNodesToInsert.end(); nIt++) {
8919 poly_nodes[iNode++] = *nIt;
8921 // add nodes of face starting from last node of link
8922 while ( nodeIt->more() ) {
8923 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8924 poly_nodes[iNode++] = n;
8929 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8932 else if ( !theFace->IsQuadratic() )
8934 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8935 int nbLinkNodes = 2 + aNodesToInsert.size();
8936 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8937 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8938 linkNodes[ 0 ] = nodes[ il1 ];
8939 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8940 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8941 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8942 linkNodes[ iNode++ ] = *nIt;
8944 // decide how to split a quadrangle: compare possible variants
8945 // and choose which of splits to be a quadrangle
8946 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8947 if ( nbFaceNodes == 3 ) {
8948 iBestQuad = nbSplits;
8951 else if ( nbFaceNodes == 4 ) {
8952 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8953 double aBestRate = DBL_MAX;
8954 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8956 double aBadRate = 0;
8957 // evaluate elements quality
8958 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8959 if ( iSplit == iQuad ) {
8960 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8964 aBadRate += getBadRate( &quad, aCrit );
8967 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8969 nodes[ iSplit < iQuad ? i4 : i3 ]);
8970 aBadRate += getBadRate( &tria, aCrit );
8974 if ( aBadRate < aBestRate ) {
8976 aBestRate = aBadRate;
8981 // create new elements
8983 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8985 if ( iSplit == iBestQuad )
8986 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8991 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8993 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8996 const SMDS_MeshNode* newNodes[ 4 ];
8997 newNodes[ 0 ] = linkNodes[ i1 ];
8998 newNodes[ 1 ] = linkNodes[ i2 ];
8999 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9000 newNodes[ 3 ] = nodes[ i4 ];
9001 if (iSplit == iBestQuad)
9002 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
9004 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
9006 } // end if(!theFace->IsQuadratic())
9008 else { // theFace is quadratic
9009 // we have to split theFace on simple triangles and one simple quadrangle
9011 int nbshift = tmp*2;
9012 // shift nodes in nodes[] by nbshift
9014 for(i=0; i<nbshift; i++) {
9015 const SMDS_MeshNode* n = nodes[0];
9016 for(j=0; j<nbFaceNodes-1; j++) {
9017 nodes[j] = nodes[j+1];
9019 nodes[nbFaceNodes-1] = n;
9021 il1 = il1 - nbshift;
9022 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9023 // n0 n1 n2 n0 n1 n2
9024 // +-----+-----+ +-----+-----+
9033 // create new elements
9035 if ( nbFaceNodes == 6 ) { // quadratic triangle
9036 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9037 if ( theFace->IsMediumNode(nodes[il1]) ) {
9038 // create quadrangle
9039 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9045 // create quadrangle
9046 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9052 else { // nbFaceNodes==8 - quadratic quadrangle
9053 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9054 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9055 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9056 if ( theFace->IsMediumNode( nodes[ il1 ])) {
9057 // create quadrangle
9058 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9064 // create quadrangle
9065 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9071 // create needed triangles using n1,n2,n3 and inserted nodes
9072 int nbn = 2 + aNodesToInsert.size();
9073 vector<const SMDS_MeshNode*> aNodes(nbn);
9074 aNodes[0 ] = nodes[n1];
9075 aNodes[nbn-1] = nodes[n2];
9076 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9077 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9078 aNodes[iNode++] = *nIt;
9080 for ( i = 1; i < nbn; i++ )
9081 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9084 // remove the old face
9085 for ( size_t i = 0; i < newElems.size(); ++i )
9088 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9089 myLastCreatedElems.Append( newElems[i] );
9091 ReplaceElemInGroups( theFace, newElems, aMesh );
9092 aMesh->RemoveElement(theFace);
9094 } // InsertNodesIntoLink()
9096 //=======================================================================
9097 //function : UpdateVolumes
9099 //=======================================================================
9101 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9102 const SMDS_MeshNode* theBetweenNode2,
9103 list<const SMDS_MeshNode*>& theNodesToInsert)
9105 myLastCreatedElems.Clear();
9106 myLastCreatedNodes.Clear();
9108 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9109 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9110 const SMDS_MeshElement* elem = invElemIt->next();
9112 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9113 SMDS_VolumeTool aVolume (elem);
9114 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9117 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9118 int iface, nbFaces = aVolume.NbFaces();
9119 vector<const SMDS_MeshNode *> poly_nodes;
9120 vector<int> quantities (nbFaces);
9122 for (iface = 0; iface < nbFaces; iface++) {
9123 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9124 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9125 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9127 for (int inode = 0; inode < nbFaceNodes; inode++) {
9128 poly_nodes.push_back(faceNodes[inode]);
9130 if (nbInserted == 0) {
9131 if (faceNodes[inode] == theBetweenNode1) {
9132 if (faceNodes[inode + 1] == theBetweenNode2) {
9133 nbInserted = theNodesToInsert.size();
9135 // add nodes to insert
9136 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9137 for (; nIt != theNodesToInsert.end(); nIt++) {
9138 poly_nodes.push_back(*nIt);
9142 else if (faceNodes[inode] == theBetweenNode2) {
9143 if (faceNodes[inode + 1] == theBetweenNode1) {
9144 nbInserted = theNodesToInsert.size();
9146 // add nodes to insert in reversed order
9147 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9149 for (; nIt != theNodesToInsert.begin(); nIt--) {
9150 poly_nodes.push_back(*nIt);
9152 poly_nodes.push_back(*nIt);
9159 quantities[iface] = nbFaceNodes + nbInserted;
9162 // Replace the volume
9163 SMESHDS_Mesh *aMesh = GetMeshDS();
9165 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9167 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9168 myLastCreatedElems.Append( newElem );
9169 ReplaceElemInGroups( elem, newElem, aMesh );
9171 aMesh->RemoveElement( elem );
9177 //================================================================================
9179 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9181 //================================================================================
9183 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9184 vector<const SMDS_MeshNode *> & nodes,
9185 vector<int> & nbNodeInFaces )
9188 nbNodeInFaces.clear();
9189 SMDS_VolumeTool vTool ( elem );
9190 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9192 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9193 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9194 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9199 //=======================================================================
9201 * \brief Convert elements contained in a sub-mesh to quadratic
9202 * \return int - nb of checked elements
9204 //=======================================================================
9206 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9207 SMESH_MesherHelper& theHelper,
9208 const bool theForce3d)
9211 if( !theSm ) return nbElem;
9213 vector<int> nbNodeInFaces;
9214 vector<const SMDS_MeshNode *> nodes;
9215 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9216 while(ElemItr->more())
9219 const SMDS_MeshElement* elem = ElemItr->next();
9220 if( !elem ) continue;
9222 // analyse a necessity of conversion
9223 const SMDSAbs_ElementType aType = elem->GetType();
9224 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9226 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9227 bool hasCentralNodes = false;
9228 if ( elem->IsQuadratic() )
9231 switch ( aGeomType ) {
9232 case SMDSEntity_Quad_Triangle:
9233 case SMDSEntity_Quad_Quadrangle:
9234 case SMDSEntity_Quad_Hexa:
9235 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9237 case SMDSEntity_BiQuad_Triangle:
9238 case SMDSEntity_BiQuad_Quadrangle:
9239 case SMDSEntity_TriQuad_Hexa:
9240 alreadyOK = theHelper.GetIsBiQuadratic();
9241 hasCentralNodes = true;
9246 // take into account already present modium nodes
9248 case SMDSAbs_Volume:
9249 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9251 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9253 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9259 // get elem data needed to re-create it
9261 const int id = elem->GetID();
9262 const int nbNodes = elem->NbCornerNodes();
9263 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9264 if ( aGeomType == SMDSEntity_Polyhedra )
9265 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9266 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9267 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9269 // remove a linear element
9270 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9272 // remove central nodes of biquadratic elements (biquad->quad convertion)
9273 if ( hasCentralNodes )
9274 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9275 if ( nodes[i]->NbInverseElements() == 0 )
9276 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9278 const SMDS_MeshElement* NewElem = 0;
9284 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9292 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9295 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9298 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9302 case SMDSAbs_Volume :
9306 case SMDSEntity_Tetra:
9307 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9309 case SMDSEntity_Pyramid:
9310 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9312 case SMDSEntity_Penta:
9313 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9315 case SMDSEntity_Hexa:
9316 case SMDSEntity_Quad_Hexa:
9317 case SMDSEntity_TriQuad_Hexa:
9318 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9319 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9321 case SMDSEntity_Hexagonal_Prism:
9323 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9330 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9331 if( NewElem && NewElem->getshapeId() < 1 )
9332 theSm->AddElement( NewElem );
9336 //=======================================================================
9337 //function : ConvertToQuadratic
9339 //=======================================================================
9341 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9343 SMESHDS_Mesh* meshDS = GetMeshDS();
9345 SMESH_MesherHelper aHelper(*myMesh);
9347 aHelper.SetIsQuadratic( true );
9348 aHelper.SetIsBiQuadratic( theToBiQuad );
9349 aHelper.SetElementsOnShape(true);
9350 aHelper.ToFixNodeParameters( true );
9352 // convert elements assigned to sub-meshes
9353 int nbCheckedElems = 0;
9354 if ( myMesh->HasShapeToMesh() )
9356 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9358 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9359 while ( smIt->more() ) {
9360 SMESH_subMesh* sm = smIt->next();
9361 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9362 aHelper.SetSubShape( sm->GetSubShape() );
9363 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9369 // convert elements NOT assigned to sub-meshes
9370 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9371 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9373 aHelper.SetElementsOnShape(false);
9374 SMESHDS_SubMesh *smDS = 0;
9377 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9378 while( aEdgeItr->more() )
9380 const SMDS_MeshEdge* edge = aEdgeItr->next();
9381 if ( !edge->IsQuadratic() )
9383 int id = edge->GetID();
9384 const SMDS_MeshNode* n1 = edge->GetNode(0);
9385 const SMDS_MeshNode* n2 = edge->GetNode(1);
9387 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9389 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9390 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9394 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9399 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9400 while( aFaceItr->more() )
9402 const SMDS_MeshFace* face = aFaceItr->next();
9403 if ( !face ) continue;
9405 const SMDSAbs_EntityType type = face->GetEntityType();
9409 case SMDSEntity_Quad_Triangle:
9410 case SMDSEntity_Quad_Quadrangle:
9411 alreadyOK = !theToBiQuad;
9412 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9414 case SMDSEntity_BiQuad_Triangle:
9415 case SMDSEntity_BiQuad_Quadrangle:
9416 alreadyOK = theToBiQuad;
9417 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9419 default: alreadyOK = false;
9424 const int id = face->GetID();
9425 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9427 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9429 SMDS_MeshFace * NewFace = 0;
9432 case SMDSEntity_Triangle:
9433 case SMDSEntity_Quad_Triangle:
9434 case SMDSEntity_BiQuad_Triangle:
9435 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9436 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9437 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9440 case SMDSEntity_Quadrangle:
9441 case SMDSEntity_Quad_Quadrangle:
9442 case SMDSEntity_BiQuad_Quadrangle:
9443 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9444 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9445 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9449 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9451 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9455 vector<int> nbNodeInFaces;
9456 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9457 while(aVolumeItr->more())
9459 const SMDS_MeshVolume* volume = aVolumeItr->next();
9460 if ( !volume ) continue;
9462 const SMDSAbs_EntityType type = volume->GetEntityType();
9463 if ( volume->IsQuadratic() )
9468 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9469 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9470 default: alreadyOK = true;
9474 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9478 const int id = volume->GetID();
9479 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9480 if ( type == SMDSEntity_Polyhedra )
9481 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9482 else if ( type == SMDSEntity_Hexagonal_Prism )
9483 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9485 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9487 SMDS_MeshVolume * NewVolume = 0;
9490 case SMDSEntity_Tetra:
9491 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9493 case SMDSEntity_Hexa:
9494 case SMDSEntity_Quad_Hexa:
9495 case SMDSEntity_TriQuad_Hexa:
9496 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9497 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9498 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9499 if ( nodes[i]->NbInverseElements() == 0 )
9500 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9502 case SMDSEntity_Pyramid:
9503 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9504 nodes[3], nodes[4], id, theForce3d);
9506 case SMDSEntity_Penta:
9507 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9508 nodes[3], nodes[4], nodes[5], id, theForce3d);
9510 case SMDSEntity_Hexagonal_Prism:
9512 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9514 ReplaceElemInGroups(volume, NewVolume, meshDS);
9519 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9520 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9521 // aHelper.FixQuadraticElements(myError);
9522 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9526 //================================================================================
9528 * \brief Makes given elements quadratic
9529 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9530 * \param theElements - elements to make quadratic
9532 //================================================================================
9534 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9535 TIDSortedElemSet& theElements,
9536 const bool theToBiQuad)
9538 if ( theElements.empty() ) return;
9540 // we believe that all theElements are of the same type
9541 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9543 // get all nodes shared by theElements
9544 TIDSortedNodeSet allNodes;
9545 TIDSortedElemSet::iterator eIt = theElements.begin();
9546 for ( ; eIt != theElements.end(); ++eIt )
9547 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9549 // complete theElements with elements of lower dim whose all nodes are in allNodes
9551 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9552 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9553 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9554 for ( ; nIt != allNodes.end(); ++nIt )
9556 const SMDS_MeshNode* n = *nIt;
9557 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9558 while ( invIt->more() )
9560 const SMDS_MeshElement* e = invIt->next();
9561 const SMDSAbs_ElementType type = e->GetType();
9562 if ( e->IsQuadratic() )
9564 quadAdjacentElems[ type ].insert( e );
9567 switch ( e->GetEntityType() ) {
9568 case SMDSEntity_Quad_Triangle:
9569 case SMDSEntity_Quad_Quadrangle:
9570 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9571 case SMDSEntity_BiQuad_Triangle:
9572 case SMDSEntity_BiQuad_Quadrangle:
9573 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9574 default: alreadyOK = true;
9579 if ( type >= elemType )
9580 continue; // same type or more complex linear element
9582 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9583 continue; // e is already checked
9587 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9588 while ( nodeIt->more() && allIn )
9589 allIn = allNodes.count( nodeIt->next() );
9591 theElements.insert(e );
9595 SMESH_MesherHelper helper(*myMesh);
9596 helper.SetIsQuadratic( true );
9597 helper.SetIsBiQuadratic( theToBiQuad );
9599 // add links of quadratic adjacent elements to the helper
9601 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9602 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9603 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9605 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9607 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9608 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9609 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9611 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9613 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9614 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9615 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9617 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9620 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9622 SMESHDS_Mesh* meshDS = GetMeshDS();
9623 SMESHDS_SubMesh* smDS = 0;
9624 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9626 const SMDS_MeshElement* elem = *eIt;
9629 int nbCentralNodes = 0;
9630 switch ( elem->GetEntityType() ) {
9631 // linear convertible
9632 case SMDSEntity_Edge:
9633 case SMDSEntity_Triangle:
9634 case SMDSEntity_Quadrangle:
9635 case SMDSEntity_Tetra:
9636 case SMDSEntity_Pyramid:
9637 case SMDSEntity_Hexa:
9638 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9639 // quadratic that can become bi-quadratic
9640 case SMDSEntity_Quad_Triangle:
9641 case SMDSEntity_Quad_Quadrangle:
9642 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9644 case SMDSEntity_BiQuad_Triangle:
9645 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9646 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9648 default: alreadyOK = true;
9650 if ( alreadyOK ) continue;
9652 const SMDSAbs_ElementType type = elem->GetType();
9653 const int id = elem->GetID();
9654 const int nbNodes = elem->NbCornerNodes();
9655 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9657 helper.SetSubShape( elem->getshapeId() );
9659 if ( !smDS || !smDS->Contains( elem ))
9660 smDS = meshDS->MeshElements( elem->getshapeId() );
9661 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9663 SMDS_MeshElement * newElem = 0;
9666 case 4: // cases for most frequently used element types go first (for optimization)
9667 if ( type == SMDSAbs_Volume )
9668 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9670 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9673 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9674 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9677 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9680 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9683 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9684 nodes[4], id, theForce3d);
9687 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9688 nodes[4], nodes[5], id, theForce3d);
9692 ReplaceElemInGroups( elem, newElem, meshDS);
9693 if( newElem && smDS )
9694 smDS->AddElement( newElem );
9696 // remove central nodes
9697 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9698 if ( nodes[i]->NbInverseElements() == 0 )
9699 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9701 } // loop on theElements
9704 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9705 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9706 // helper.FixQuadraticElements( myError );
9707 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9711 //=======================================================================
9713 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9714 * \return int - nb of checked elements
9716 //=======================================================================
9718 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9719 SMDS_ElemIteratorPtr theItr,
9720 const int theShapeID)
9723 SMESHDS_Mesh* meshDS = GetMeshDS();
9724 ElemFeatures elemType;
9725 vector<const SMDS_MeshNode *> nodes;
9727 while( theItr->more() )
9729 const SMDS_MeshElement* elem = theItr->next();
9731 if( elem && elem->IsQuadratic())
9734 int nbCornerNodes = elem->NbCornerNodes();
9735 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9737 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9739 //remove a quadratic element
9740 if ( !theSm || !theSm->Contains( elem ))
9741 theSm = meshDS->MeshElements( elem->getshapeId() );
9742 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9744 // remove medium nodes
9745 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9746 if ( nodes[i]->NbInverseElements() == 0 )
9747 meshDS->RemoveFreeNode( nodes[i], theSm );
9749 // add a linear element
9750 nodes.resize( nbCornerNodes );
9751 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9752 ReplaceElemInGroups(elem, newElem, meshDS);
9753 if( theSm && newElem )
9754 theSm->AddElement( newElem );
9760 //=======================================================================
9761 //function : ConvertFromQuadratic
9763 //=======================================================================
9765 bool SMESH_MeshEditor::ConvertFromQuadratic()
9767 int nbCheckedElems = 0;
9768 if ( myMesh->HasShapeToMesh() )
9770 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9772 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9773 while ( smIt->more() ) {
9774 SMESH_subMesh* sm = smIt->next();
9775 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9776 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9782 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9783 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9785 SMESHDS_SubMesh *aSM = 0;
9786 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9794 //================================================================================
9796 * \brief Return true if all medium nodes of the element are in the node set
9798 //================================================================================
9800 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9802 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9803 if ( !nodeSet.count( elem->GetNode(i) ))
9809 //================================================================================
9811 * \brief Makes given elements linear
9813 //================================================================================
9815 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9817 if ( theElements.empty() ) return;
9819 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9820 set<int> mediumNodeIDs;
9821 TIDSortedElemSet::iterator eIt = theElements.begin();
9822 for ( ; eIt != theElements.end(); ++eIt )
9824 const SMDS_MeshElement* e = *eIt;
9825 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9826 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9829 // replace given elements by linear ones
9830 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9831 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9833 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9834 // except those elements sharing medium nodes of quadratic element whose medium nodes
9835 // are not all in mediumNodeIDs
9837 // get remaining medium nodes
9838 TIDSortedNodeSet mediumNodes;
9839 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9840 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9841 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9842 mediumNodes.insert( mediumNodes.end(), n );
9844 // find more quadratic elements to convert
9845 TIDSortedElemSet moreElemsToConvert;
9846 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9847 for ( ; nIt != mediumNodes.end(); ++nIt )
9849 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9850 while ( invIt->more() )
9852 const SMDS_MeshElement* e = invIt->next();
9853 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9855 // find a more complex element including e and
9856 // whose medium nodes are not in mediumNodes
9857 bool complexFound = false;
9858 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9860 SMDS_ElemIteratorPtr invIt2 =
9861 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9862 while ( invIt2->more() )
9864 const SMDS_MeshElement* eComplex = invIt2->next();
9865 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9867 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9868 if ( nbCommonNodes == e->NbNodes())
9870 complexFound = true;
9871 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9877 if ( !complexFound )
9878 moreElemsToConvert.insert( e );
9882 elemIt = elemSetIterator( moreElemsToConvert );
9883 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9886 //=======================================================================
9887 //function : SewSideElements
9889 //=======================================================================
9891 SMESH_MeshEditor::Sew_Error
9892 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9893 TIDSortedElemSet& theSide2,
9894 const SMDS_MeshNode* theFirstNode1,
9895 const SMDS_MeshNode* theFirstNode2,
9896 const SMDS_MeshNode* theSecondNode1,
9897 const SMDS_MeshNode* theSecondNode2)
9899 myLastCreatedElems.Clear();
9900 myLastCreatedNodes.Clear();
9902 MESSAGE ("::::SewSideElements()");
9903 if ( theSide1.size() != theSide2.size() )
9904 return SEW_DIFF_NB_OF_ELEMENTS;
9906 Sew_Error aResult = SEW_OK;
9908 // 1. Build set of faces representing each side
9909 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9910 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9912 // =======================================================================
9913 // 1. Build set of faces representing each side:
9914 // =======================================================================
9915 // a. build set of nodes belonging to faces
9916 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9917 // c. create temporary faces representing side of volumes if correspondent
9918 // face does not exist
9920 SMESHDS_Mesh* aMesh = GetMeshDS();
9921 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9922 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9923 TIDSortedElemSet faceSet1, faceSet2;
9924 set<const SMDS_MeshElement*> volSet1, volSet2;
9925 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9926 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9927 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9928 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9929 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9930 int iSide, iFace, iNode;
9932 list<const SMDS_MeshElement* > tempFaceList;
9933 for ( iSide = 0; iSide < 2; iSide++ ) {
9934 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9935 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9936 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9937 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9938 set<const SMDS_MeshElement*>::iterator vIt;
9939 TIDSortedElemSet::iterator eIt;
9940 set<const SMDS_MeshNode*>::iterator nIt;
9942 // check that given nodes belong to given elements
9943 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9944 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9945 int firstIndex = -1, secondIndex = -1;
9946 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9947 const SMDS_MeshElement* elem = *eIt;
9948 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9949 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9950 if ( firstIndex > -1 && secondIndex > -1 ) break;
9952 if ( firstIndex < 0 || secondIndex < 0 ) {
9953 // we can simply return until temporary faces created
9954 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9957 // -----------------------------------------------------------
9958 // 1a. Collect nodes of existing faces
9959 // and build set of face nodes in order to detect missing
9960 // faces corresponding to sides of volumes
9961 // -----------------------------------------------------------
9963 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9965 // loop on the given element of a side
9966 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9967 //const SMDS_MeshElement* elem = *eIt;
9968 const SMDS_MeshElement* elem = *eIt;
9969 if ( elem->GetType() == SMDSAbs_Face ) {
9970 faceSet->insert( elem );
9971 set <const SMDS_MeshNode*> faceNodeSet;
9972 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9973 while ( nodeIt->more() ) {
9974 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9975 nodeSet->insert( n );
9976 faceNodeSet.insert( n );
9978 setOfFaceNodeSet.insert( faceNodeSet );
9980 else if ( elem->GetType() == SMDSAbs_Volume )
9981 volSet->insert( elem );
9983 // ------------------------------------------------------------------------------
9984 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9985 // ------------------------------------------------------------------------------
9987 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9988 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9989 while ( fIt->more() ) { // loop on faces sharing a node
9990 const SMDS_MeshElement* f = fIt->next();
9991 if ( faceSet->find( f ) == faceSet->end() ) {
9992 // check if all nodes are in nodeSet and
9993 // complete setOfFaceNodeSet if they are
9994 set <const SMDS_MeshNode*> faceNodeSet;
9995 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9996 bool allInSet = true;
9997 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9998 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9999 if ( nodeSet->find( n ) == nodeSet->end() )
10002 faceNodeSet.insert( n );
10005 faceSet->insert( f );
10006 setOfFaceNodeSet.insert( faceNodeSet );
10012 // -------------------------------------------------------------------------
10013 // 1c. Create temporary faces representing sides of volumes if correspondent
10014 // face does not exist
10015 // -------------------------------------------------------------------------
10017 if ( !volSet->empty() ) {
10018 //int nodeSetSize = nodeSet->size();
10020 // loop on given volumes
10021 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10022 SMDS_VolumeTool vol (*vIt);
10023 // loop on volume faces: find free faces
10024 // --------------------------------------
10025 list<const SMDS_MeshElement* > freeFaceList;
10026 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10027 if ( !vol.IsFreeFace( iFace ))
10029 // check if there is already a face with same nodes in a face set
10030 const SMDS_MeshElement* aFreeFace = 0;
10031 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10032 int nbNodes = vol.NbFaceNodes( iFace );
10033 set <const SMDS_MeshNode*> faceNodeSet;
10034 vol.GetFaceNodes( iFace, faceNodeSet );
10035 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10037 // no such a face is given but it still can exist, check it
10038 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10039 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10041 if ( !aFreeFace ) {
10042 // create a temporary face
10043 if ( nbNodes == 3 ) {
10044 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10045 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10047 else if ( nbNodes == 4 ) {
10048 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10049 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10052 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10053 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10054 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10057 tempFaceList.push_back( aFreeFace );
10061 freeFaceList.push_back( aFreeFace );
10063 } // loop on faces of a volume
10065 // choose one of several free faces of a volume
10066 // --------------------------------------------
10067 if ( freeFaceList.size() > 1 ) {
10068 // choose a face having max nb of nodes shared by other elems of a side
10069 int maxNbNodes = -1;
10070 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10071 while ( fIt != freeFaceList.end() ) { // loop on free faces
10072 int nbSharedNodes = 0;
10073 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10074 while ( nodeIt->more() ) { // loop on free face nodes
10075 const SMDS_MeshNode* n =
10076 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10077 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10078 while ( invElemIt->more() ) {
10079 const SMDS_MeshElement* e = invElemIt->next();
10080 nbSharedNodes += faceSet->count( e );
10081 nbSharedNodes += elemSet->count( e );
10084 if ( nbSharedNodes > maxNbNodes ) {
10085 maxNbNodes = nbSharedNodes;
10086 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10088 else if ( nbSharedNodes == maxNbNodes ) {
10092 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10095 if ( freeFaceList.size() > 1 )
10097 // could not choose one face, use another way
10098 // choose a face most close to the bary center of the opposite side
10099 gp_XYZ aBC( 0., 0., 0. );
10100 set <const SMDS_MeshNode*> addedNodes;
10101 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10102 eIt = elemSet2->begin();
10103 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10104 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10105 while ( nodeIt->more() ) { // loop on free face nodes
10106 const SMDS_MeshNode* n =
10107 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10108 if ( addedNodes.insert( n ).second )
10109 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10112 aBC /= addedNodes.size();
10113 double minDist = DBL_MAX;
10114 fIt = freeFaceList.begin();
10115 while ( fIt != freeFaceList.end() ) { // loop on free faces
10117 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10118 while ( nodeIt->more() ) { // loop on free face nodes
10119 const SMDS_MeshNode* n =
10120 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10121 gp_XYZ p( n->X(),n->Y(),n->Z() );
10122 dist += ( aBC - p ).SquareModulus();
10124 if ( dist < minDist ) {
10126 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10129 fIt = freeFaceList.erase( fIt++ );
10132 } // choose one of several free faces of a volume
10134 if ( freeFaceList.size() == 1 ) {
10135 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10136 faceSet->insert( aFreeFace );
10137 // complete a node set with nodes of a found free face
10138 // for ( iNode = 0; iNode < ; iNode++ )
10139 // nodeSet->insert( fNodes[ iNode ] );
10142 } // loop on volumes of a side
10144 // // complete a set of faces if new nodes in a nodeSet appeared
10145 // // ----------------------------------------------------------
10146 // if ( nodeSetSize != nodeSet->size() ) {
10147 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10148 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10149 // while ( fIt->more() ) { // loop on faces sharing a node
10150 // const SMDS_MeshElement* f = fIt->next();
10151 // if ( faceSet->find( f ) == faceSet->end() ) {
10152 // // check if all nodes are in nodeSet and
10153 // // complete setOfFaceNodeSet if they are
10154 // set <const SMDS_MeshNode*> faceNodeSet;
10155 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10156 // bool allInSet = true;
10157 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10158 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10159 // if ( nodeSet->find( n ) == nodeSet->end() )
10160 // allInSet = false;
10162 // faceNodeSet.insert( n );
10164 // if ( allInSet ) {
10165 // faceSet->insert( f );
10166 // setOfFaceNodeSet.insert( faceNodeSet );
10172 } // Create temporary faces, if there are volumes given
10175 if ( faceSet1.size() != faceSet2.size() ) {
10176 // delete temporary faces: they are in reverseElements of actual nodes
10177 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10178 // while ( tmpFaceIt->more() )
10179 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10180 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10181 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10182 // aMesh->RemoveElement(*tmpFaceIt);
10183 MESSAGE("Diff nb of faces");
10184 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10187 // ============================================================
10188 // 2. Find nodes to merge:
10189 // bind a node to remove to a node to put instead
10190 // ============================================================
10192 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10193 if ( theFirstNode1 != theFirstNode2 )
10194 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10195 if ( theSecondNode1 != theSecondNode2 )
10196 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10198 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10199 set< long > linkIdSet; // links to process
10200 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10202 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10203 list< NLink > linkList[2];
10204 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10205 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10206 // loop on links in linkList; find faces by links and append links
10207 // of the found faces to linkList
10208 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10209 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10211 NLink link[] = { *linkIt[0], *linkIt[1] };
10212 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10213 if ( !linkIdSet.count( linkID ) )
10216 // by links, find faces in the face sets,
10217 // and find indices of link nodes in the found faces;
10218 // in a face set, there is only one or no face sharing a link
10219 // ---------------------------------------------------------------
10221 const SMDS_MeshElement* face[] = { 0, 0 };
10222 vector<const SMDS_MeshNode*> fnodes[2];
10223 int iLinkNode[2][2];
10224 TIDSortedElemSet avoidSet;
10225 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10226 const SMDS_MeshNode* n1 = link[iSide].first;
10227 const SMDS_MeshNode* n2 = link[iSide].second;
10228 //cout << "Side " << iSide << " ";
10229 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10230 // find a face by two link nodes
10231 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10232 *faceSetPtr[ iSide ], avoidSet,
10233 &iLinkNode[iSide][0],
10234 &iLinkNode[iSide][1] );
10235 if ( face[ iSide ])
10237 //cout << " F " << face[ iSide]->GetID() <<endl;
10238 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10239 // put face nodes to fnodes
10240 if ( face[ iSide ]->IsQuadratic() )
10242 // use interlaced nodes iterator
10243 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10244 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10245 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10246 while ( nIter->more() )
10247 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10251 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10252 face[ iSide ]->end_nodes() );
10254 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10258 // check similarity of elements of the sides
10259 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10260 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10261 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10262 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10265 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10267 break; // do not return because it's necessary to remove tmp faces
10270 // set nodes to merge
10271 // -------------------
10273 if ( face[0] && face[1] ) {
10274 const int nbNodes = face[0]->NbNodes();
10275 if ( nbNodes != face[1]->NbNodes() ) {
10276 MESSAGE("Diff nb of face nodes");
10277 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10278 break; // do not return because it s necessary to remove tmp faces
10280 bool reverse[] = { false, false }; // order of nodes in the link
10281 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10282 // analyse link orientation in faces
10283 int i1 = iLinkNode[ iSide ][ 0 ];
10284 int i2 = iLinkNode[ iSide ][ 1 ];
10285 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10287 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10288 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10289 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10291 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10292 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10295 // add other links of the faces to linkList
10296 // -----------------------------------------
10298 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10299 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10300 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10301 if ( !iter_isnew.second ) { // already in a set: no need to process
10302 linkIdSet.erase( iter_isnew.first );
10304 else // new in set == encountered for the first time: add
10306 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10307 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10308 linkList[0].push_back ( NLink( n1, n2 ));
10309 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10314 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10317 } // loop on link lists
10319 if ( aResult == SEW_OK &&
10320 ( //linkIt[0] != linkList[0].end() ||
10321 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10322 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10323 " " << (faceSetPtr[1]->empty()));
10324 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10327 // ====================================================================
10328 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10329 // ====================================================================
10331 // delete temporary faces
10332 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10333 // while ( tmpFaceIt->more() )
10334 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10335 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10336 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10337 aMesh->RemoveElement(*tmpFaceIt);
10339 if ( aResult != SEW_OK)
10342 list< int > nodeIDsToRemove;
10343 vector< const SMDS_MeshNode*> nodes;
10344 ElemFeatures elemType;
10346 // loop on nodes replacement map
10347 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10348 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10349 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10351 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10352 nodeIDsToRemove.push_back( nToRemove->GetID() );
10353 // loop on elements sharing nToRemove
10354 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10355 while ( invElemIt->more() ) {
10356 const SMDS_MeshElement* e = invElemIt->next();
10357 // get a new suite of nodes: make replacement
10358 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10359 nodes.resize( nbNodes );
10360 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10361 while ( nIt->more() ) {
10362 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10363 nnIt = nReplaceMap.find( n );
10364 if ( nnIt != nReplaceMap.end() ) {
10366 n = (*nnIt).second;
10370 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10371 // elemIDsToRemove.push_back( e->GetID() );
10375 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10376 aMesh->RemoveElement( e );
10378 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10380 AddToSameGroups( newElem, e, aMesh );
10381 if ( int aShapeId = e->getshapeId() )
10382 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10388 Remove( nodeIDsToRemove, true );
10393 //================================================================================
10395 * \brief Find corresponding nodes in two sets of faces
10396 * \param theSide1 - first face set
10397 * \param theSide2 - second first face
10398 * \param theFirstNode1 - a boundary node of set 1
10399 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10400 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10401 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10402 * \param nReplaceMap - output map of corresponding nodes
10403 * \return bool - is a success or not
10405 //================================================================================
10408 //#define DEBUG_MATCHING_NODES
10411 SMESH_MeshEditor::Sew_Error
10412 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10413 set<const SMDS_MeshElement*>& theSide2,
10414 const SMDS_MeshNode* theFirstNode1,
10415 const SMDS_MeshNode* theFirstNode2,
10416 const SMDS_MeshNode* theSecondNode1,
10417 const SMDS_MeshNode* theSecondNode2,
10418 TNodeNodeMap & nReplaceMap)
10420 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10422 nReplaceMap.clear();
10423 if ( theFirstNode1 != theFirstNode2 )
10424 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10425 if ( theSecondNode1 != theSecondNode2 )
10426 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10428 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10429 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10431 list< NLink > linkList[2];
10432 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10433 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10435 // loop on links in linkList; find faces by links and append links
10436 // of the found faces to linkList
10437 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10438 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10439 NLink link[] = { *linkIt[0], *linkIt[1] };
10440 if ( linkSet.find( link[0] ) == linkSet.end() )
10443 // by links, find faces in the face sets,
10444 // and find indices of link nodes in the found faces;
10445 // in a face set, there is only one or no face sharing a link
10446 // ---------------------------------------------------------------
10448 const SMDS_MeshElement* face[] = { 0, 0 };
10449 list<const SMDS_MeshNode*> notLinkNodes[2];
10450 //bool reverse[] = { false, false }; // order of notLinkNodes
10452 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10454 const SMDS_MeshNode* n1 = link[iSide].first;
10455 const SMDS_MeshNode* n2 = link[iSide].second;
10456 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10457 set< const SMDS_MeshElement* > facesOfNode1;
10458 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10460 // during a loop of the first node, we find all faces around n1,
10461 // during a loop of the second node, we find one face sharing both n1 and n2
10462 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10463 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10464 while ( fIt->more() ) { // loop on faces sharing a node
10465 const SMDS_MeshElement* f = fIt->next();
10466 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10467 ! facesOfNode1.insert( f ).second ) // f encounters twice
10469 if ( face[ iSide ] ) {
10470 MESSAGE( "2 faces per link " );
10471 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10474 faceSet->erase( f );
10476 // get not link nodes
10477 int nbN = f->NbNodes();
10478 if ( f->IsQuadratic() )
10480 nbNodes[ iSide ] = nbN;
10481 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10482 int i1 = f->GetNodeIndex( n1 );
10483 int i2 = f->GetNodeIndex( n2 );
10484 int iEnd = nbN, iBeg = -1, iDelta = 1;
10485 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10487 std::swap( iEnd, iBeg ); iDelta = -1;
10492 if ( i == iEnd ) i = iBeg + iDelta;
10493 if ( i == i1 ) break;
10494 nodes.push_back ( f->GetNode( i ) );
10500 // check similarity of elements of the sides
10501 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10502 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10503 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10504 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10507 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10511 // set nodes to merge
10512 // -------------------
10514 if ( face[0] && face[1] ) {
10515 if ( nbNodes[0] != nbNodes[1] ) {
10516 MESSAGE("Diff nb of face nodes");
10517 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10519 #ifdef DEBUG_MATCHING_NODES
10520 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10521 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10522 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10524 int nbN = nbNodes[0];
10526 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10527 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10528 for ( int i = 0 ; i < nbN - 2; ++i ) {
10529 #ifdef DEBUG_MATCHING_NODES
10530 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10532 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10536 // add other links of the face 1 to linkList
10537 // -----------------------------------------
10539 const SMDS_MeshElement* f0 = face[0];
10540 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10541 for ( int i = 0; i < nbN; i++ )
10543 const SMDS_MeshNode* n2 = f0->GetNode( i );
10544 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10545 linkSet.insert( SMESH_TLink( n1, n2 ));
10546 if ( !iter_isnew.second ) { // already in a set: no need to process
10547 linkSet.erase( iter_isnew.first );
10549 else // new in set == encountered for the first time: add
10551 #ifdef DEBUG_MATCHING_NODES
10552 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10553 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10555 linkList[0].push_back ( NLink( n1, n2 ));
10556 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10561 } // loop on link lists
10566 //================================================================================
10568 * \brief Create elements equal (on same nodes) to given ones
10569 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10570 * elements of the uppest dimension are duplicated.
10572 //================================================================================
10574 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10576 ClearLastCreated();
10577 SMESHDS_Mesh* mesh = GetMeshDS();
10579 // get an element type and an iterator over elements
10581 SMDSAbs_ElementType type;
10582 SMDS_ElemIteratorPtr elemIt;
10583 vector< const SMDS_MeshElement* > allElems;
10584 if ( theElements.empty() )
10586 if ( mesh->NbNodes() == 0 )
10588 // get most complex type
10589 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10590 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10591 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10593 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10594 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10599 // put all elements in the vector <allElems>
10600 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10601 elemIt = mesh->elementsIterator( type );
10602 while ( elemIt->more() )
10603 allElems.push_back( elemIt->next());
10604 elemIt = elemSetIterator( allElems );
10608 type = (*theElements.begin())->GetType();
10609 elemIt = elemSetIterator( theElements );
10612 // duplicate elements
10614 ElemFeatures elemType;
10616 vector< const SMDS_MeshNode* > nodes;
10617 while ( elemIt->more() )
10619 const SMDS_MeshElement* elem = elemIt->next();
10620 if ( elem->GetType() != type )
10623 elemType.Init( elem, /*basicOnly=*/false );
10624 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10626 AddElement( nodes, elemType );
10630 //================================================================================
10632 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10633 \param theElems - the list of elements (edges or faces) to be replicated
10634 The nodes for duplication could be found from these elements
10635 \param theNodesNot - list of nodes to NOT replicate
10636 \param theAffectedElems - the list of elements (cells and edges) to which the
10637 replicated nodes should be associated to.
10638 \return TRUE if operation has been completed successfully, FALSE otherwise
10640 //================================================================================
10642 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10643 const TIDSortedElemSet& theNodesNot,
10644 const TIDSortedElemSet& theAffectedElems )
10646 myLastCreatedElems.Clear();
10647 myLastCreatedNodes.Clear();
10649 if ( theElems.size() == 0 )
10652 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10657 TNodeNodeMap anOldNodeToNewNode;
10658 // duplicate elements and nodes
10659 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10660 // replce nodes by duplications
10661 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10665 //================================================================================
10667 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10668 \param theMeshDS - mesh instance
10669 \param theElems - the elements replicated or modified (nodes should be changed)
10670 \param theNodesNot - nodes to NOT replicate
10671 \param theNodeNodeMap - relation of old node to new created node
10672 \param theIsDoubleElem - flag os to replicate element or modify
10673 \return TRUE if operation has been completed successfully, FALSE otherwise
10675 //================================================================================
10677 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10678 const TIDSortedElemSet& theElems,
10679 const TIDSortedElemSet& theNodesNot,
10680 TNodeNodeMap& theNodeNodeMap,
10681 const bool theIsDoubleElem )
10683 MESSAGE("doubleNodes");
10684 // iterate through element and duplicate them (by nodes duplication)
10686 std::vector<const SMDS_MeshNode*> newNodes;
10687 ElemFeatures elemType;
10689 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10690 for ( ; elemItr != theElems.end(); ++elemItr )
10692 const SMDS_MeshElement* anElem = *elemItr;
10696 // duplicate nodes to duplicate element
10697 bool isDuplicate = false;
10698 newNodes.resize( anElem->NbNodes() );
10699 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10701 while ( anIter->more() )
10703 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10704 const SMDS_MeshNode* aNewNode = aCurrNode;
10705 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10706 if ( n2n != theNodeNodeMap.end() )
10708 aNewNode = n2n->second;
10710 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10713 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10714 copyPosition( aCurrNode, aNewNode );
10715 theNodeNodeMap[ aCurrNode ] = aNewNode;
10716 myLastCreatedNodes.Append( aNewNode );
10718 isDuplicate |= (aCurrNode != aNewNode);
10719 newNodes[ ind++ ] = aNewNode;
10721 if ( !isDuplicate )
10724 if ( theIsDoubleElem )
10725 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10727 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10734 //================================================================================
10736 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10737 \param theNodes - identifiers of nodes to be doubled
10738 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10739 nodes. If list of element identifiers is empty then nodes are doubled but
10740 they not assigned to elements
10741 \return TRUE if operation has been completed successfully, FALSE otherwise
10743 //================================================================================
10745 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10746 const std::list< int >& theListOfModifiedElems )
10748 MESSAGE("DoubleNodes");
10749 myLastCreatedElems.Clear();
10750 myLastCreatedNodes.Clear();
10752 if ( theListOfNodes.size() == 0 )
10755 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10759 // iterate through nodes and duplicate them
10761 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10763 std::list< int >::const_iterator aNodeIter;
10764 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10766 int aCurr = *aNodeIter;
10767 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10773 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10776 copyPosition( aNode, aNewNode );
10777 anOldNodeToNewNode[ aNode ] = aNewNode;
10778 myLastCreatedNodes.Append( aNewNode );
10782 // Create map of new nodes for modified elements
10784 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10786 std::list< int >::const_iterator anElemIter;
10787 for ( anElemIter = theListOfModifiedElems.begin();
10788 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10790 int aCurr = *anElemIter;
10791 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10795 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10797 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10799 while ( anIter->more() )
10801 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10802 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10804 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10805 aNodeArr[ ind++ ] = aNewNode;
10808 aNodeArr[ ind++ ] = aCurrNode;
10810 anElemToNodes[ anElem ] = aNodeArr;
10813 // Change nodes of elements
10815 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10816 anElemToNodesIter = anElemToNodes.begin();
10817 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10819 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10820 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10823 MESSAGE("ChangeElementNodes");
10824 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10833 //================================================================================
10835 \brief Check if element located inside shape
10836 \return TRUE if IN or ON shape, FALSE otherwise
10838 //================================================================================
10840 template<class Classifier>
10841 bool isInside(const SMDS_MeshElement* theElem,
10842 Classifier& theClassifier,
10843 const double theTol)
10845 gp_XYZ centerXYZ (0, 0, 0);
10846 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10847 while (aNodeItr->more())
10848 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10850 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10851 theClassifier.Perform(aPnt, theTol);
10852 TopAbs_State aState = theClassifier.State();
10853 return (aState == TopAbs_IN || aState == TopAbs_ON );
10856 //================================================================================
10858 * \brief Classifier of the 3D point on the TopoDS_Face
10859 * with interaface suitable for isInside()
10861 //================================================================================
10863 struct _FaceClassifier
10865 Extrema_ExtPS _extremum;
10866 BRepAdaptor_Surface _surface;
10867 TopAbs_State _state;
10869 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10871 _extremum.Initialize( _surface,
10872 _surface.FirstUParameter(), _surface.LastUParameter(),
10873 _surface.FirstVParameter(), _surface.LastVParameter(),
10874 _surface.Tolerance(), _surface.Tolerance() );
10876 void Perform(const gp_Pnt& aPnt, double theTol)
10879 _state = TopAbs_OUT;
10880 _extremum.Perform(aPnt);
10881 if ( _extremum.IsDone() )
10882 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10883 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10885 TopAbs_State State() const
10892 //================================================================================
10894 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10895 This method is the first step of DoubleNodeElemGroupsInRegion.
10896 \param theElems - list of groups of elements (edges or faces) to be replicated
10897 \param theNodesNot - list of groups of nodes not to replicated
10898 \param theShape - shape to detect affected elements (element which geometric center
10899 located on or inside shape). If the shape is null, detection is done on faces orientations
10900 (select elements with a gravity center on the side given by faces normals).
10901 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10902 The replicated nodes should be associated to affected elements.
10903 \return groups of affected elements
10904 \sa DoubleNodeElemGroupsInRegion()
10906 //================================================================================
10908 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10909 const TIDSortedElemSet& theNodesNot,
10910 const TopoDS_Shape& theShape,
10911 TIDSortedElemSet& theAffectedElems)
10913 if ( theShape.IsNull() )
10915 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10916 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10917 std::set<const SMDS_MeshElement*> edgesToCheck;
10918 alreadyCheckedNodes.clear();
10919 alreadyCheckedElems.clear();
10920 edgesToCheck.clear();
10922 // --- iterates on elements to be replicated and get elements by back references from their nodes
10924 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10926 for ( ielem=1; elemItr != theElems.end(); ++elemItr )
10928 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10929 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10932 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10933 MESSAGE("element " << ielem++ << " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10934 std::set<const SMDS_MeshNode*> nodesElem;
10936 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10937 while ( nodeItr->more() )
10939 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10940 nodesElem.insert(aNode);
10942 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10943 for (; nodit != nodesElem.end(); nodit++)
10945 MESSAGE(" noeud ");
10946 const SMDS_MeshNode* aNode = *nodit;
10947 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10949 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10951 alreadyCheckedNodes.insert(aNode);
10952 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10953 while ( backElemItr->more() )
10955 MESSAGE(" backelem ");
10956 const SMDS_MeshElement* curElem = backElemItr->next();
10957 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10959 if (theElems.find(curElem) != theElems.end())
10961 alreadyCheckedElems.insert(curElem);
10962 double x=0, y=0, z=0;
10964 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10965 while ( nodeItr2->more() )
10967 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10968 x += anotherNode->X();
10969 y += anotherNode->Y();
10970 z += anotherNode->Z();
10974 p.SetCoord( x/nb -aNode->X(),
10976 z/nb -aNode->Z() );
10977 MESSAGE(" check " << p.X() << " " << p.Y() << " " << p.Z());
10980 MESSAGE(" --- inserted")
10981 theAffectedElems.insert( curElem );
10983 else if (curElem->GetType() == SMDSAbs_Edge)
10984 edgesToCheck.insert(curElem);
10988 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10989 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10990 for( ; eit != edgesToCheck.end(); eit++)
10992 bool onside = true;
10993 const SMDS_MeshElement* anEdge = *eit;
10994 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10995 while ( nodeItr->more() )
10997 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10998 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
11006 MESSAGE(" --- edge onside inserted")
11007 theAffectedElems.insert(anEdge);
11013 const double aTol = Precision::Confusion();
11014 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11015 auto_ptr<_FaceClassifier> aFaceClassifier;
11016 if ( theShape.ShapeType() == TopAbs_SOLID )
11018 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11019 bsc3d->PerformInfinitePoint(aTol);
11021 else if (theShape.ShapeType() == TopAbs_FACE )
11023 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11026 // iterates on indicated elements and get elements by back references from their nodes
11027 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11029 for ( ielem = 1; elemItr != theElems.end(); ++elemItr )
11031 MESSAGE("element " << ielem++);
11032 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11035 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11036 while ( nodeItr->more() )
11038 MESSAGE(" noeud ");
11039 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11040 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11042 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11043 while ( backElemItr->more() )
11045 MESSAGE(" backelem ");
11046 const SMDS_MeshElement* curElem = backElemItr->next();
11047 if ( curElem && theElems.find(curElem) == theElems.end() &&
11049 isInside( curElem, *bsc3d, aTol ) :
11050 isInside( curElem, *aFaceClassifier, aTol )))
11051 theAffectedElems.insert( curElem );
11059 //================================================================================
11061 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11062 \param theElems - group of of elements (edges or faces) to be replicated
11063 \param theNodesNot - group of nodes not to replicate
11064 \param theShape - shape to detect affected elements (element which geometric center
11065 located on or inside shape).
11066 The replicated nodes should be associated to affected elements.
11067 \return TRUE if operation has been completed successfully, FALSE otherwise
11069 //================================================================================
11071 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11072 const TIDSortedElemSet& theNodesNot,
11073 const TopoDS_Shape& theShape )
11075 if ( theShape.IsNull() )
11078 const double aTol = Precision::Confusion();
11079 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11080 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
11081 if ( theShape.ShapeType() == TopAbs_SOLID )
11083 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11084 bsc3d->PerformInfinitePoint(aTol);
11086 else if (theShape.ShapeType() == TopAbs_FACE )
11088 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11091 // iterates on indicated elements and get elements by back references from their nodes
11092 TIDSortedElemSet anAffected;
11093 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11094 for ( ; elemItr != theElems.end(); ++elemItr )
11096 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11100 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11101 while ( nodeItr->more() )
11103 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11104 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11106 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11107 while ( backElemItr->more() )
11109 const SMDS_MeshElement* curElem = backElemItr->next();
11110 if ( curElem && theElems.find(curElem) == theElems.end() &&
11112 isInside( curElem, *bsc3d, aTol ) :
11113 isInside( curElem, *aFaceClassifier, aTol )))
11114 anAffected.insert( curElem );
11118 return DoubleNodes( theElems, theNodesNot, anAffected );
11122 * \brief compute an oriented angle between two planes defined by four points.
11123 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11124 * @param p0 base of the rotation axe
11125 * @param p1 extremity of the rotation axe
11126 * @param g1 belongs to the first plane
11127 * @param g2 belongs to the second plane
11129 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11131 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11132 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11133 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11134 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11135 gp_Vec vref(p0, p1);
11138 gp_Vec n1 = vref.Crossed(v1);
11139 gp_Vec n2 = vref.Crossed(v2);
11141 return n2.AngleWithRef(n1, vref);
11143 catch ( Standard_Failure ) {
11145 return Max( v1.Magnitude(), v2.Magnitude() );
11149 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11150 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11151 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11152 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11153 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11154 * 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.
11155 * 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.
11156 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11157 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11158 * \param theElems - list of groups of volumes, where a group of volume is a set of
11159 * SMDS_MeshElements sorted by Id.
11160 * \param createJointElems - if TRUE, create the elements
11161 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11162 * the boundary between \a theDomains and the rest mesh
11163 * \return TRUE if operation has been completed successfully, FALSE otherwise
11165 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11166 bool createJointElems,
11167 bool onAllBoundaries)
11169 MESSAGE("----------------------------------------------");
11170 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11171 MESSAGE("----------------------------------------------");
11173 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11174 meshDS->BuildDownWardConnectivity(true);
11176 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11178 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11179 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11180 // build the list of nodes shared by 2 or more domains, with their domain indexes
11182 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11183 std::map<int,int>celldom; // cell vtkId --> domain
11184 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11185 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11186 faceDomains.clear();
11188 cellDomains.clear();
11189 nodeDomains.clear();
11190 std::map<int,int> emptyMap;
11191 std::set<int> emptySet;
11194 MESSAGE(".. Number of domains :"<<theElems.size());
11196 TIDSortedElemSet theRestDomElems;
11197 const int iRestDom = -1;
11198 const int idom0 = onAllBoundaries ? iRestDom : 0;
11199 const int nbDomains = theElems.size();
11201 // Check if the domains do not share an element
11202 for (int idom = 0; idom < nbDomains-1; idom++)
11204 // MESSAGE("... Check of domain #" << idom);
11205 const TIDSortedElemSet& domain = theElems[idom];
11206 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11207 for (; elemItr != domain.end(); ++elemItr)
11209 const SMDS_MeshElement* anElem = *elemItr;
11210 int idombisdeb = idom + 1 ;
11211 // check if the element belongs to a domain further in the list
11212 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11214 const TIDSortedElemSet& domainbis = theElems[idombis];
11215 if ( domainbis.count( anElem ))
11217 MESSAGE(".... Domain #" << idom);
11218 MESSAGE(".... Domain #" << idombis);
11219 throw SALOME_Exception("The domains are not disjoint.");
11226 for (int idom = 0; idom < nbDomains; idom++)
11229 // --- build a map (face to duplicate --> volume to modify)
11230 // with all the faces shared by 2 domains (group of elements)
11231 // and corresponding volume of this domain, for each shared face.
11232 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11234 MESSAGE("... Neighbors of domain #" << idom);
11235 const TIDSortedElemSet& domain = theElems[idom];
11236 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11237 for (; elemItr != domain.end(); ++elemItr)
11239 const SMDS_MeshElement* anElem = *elemItr;
11242 int vtkId = anElem->getVtkId();
11243 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11244 int neighborsVtkIds[NBMAXNEIGHBORS];
11245 int downIds[NBMAXNEIGHBORS];
11246 unsigned char downTypes[NBMAXNEIGHBORS];
11247 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11248 for (int n = 0; n < nbNeighbors; n++)
11250 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11251 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11252 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11255 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11257 // MESSAGE("Domain " << idombis);
11258 const TIDSortedElemSet& domainbis = theElems[idombis];
11259 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11261 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11263 DownIdType face(downIds[n], downTypes[n]);
11264 if (!faceDomains[face].count(idom))
11266 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11267 celldom[vtkId] = idom;
11268 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11272 theRestDomElems.insert( elem );
11273 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11274 celldom[neighborsVtkIds[n]] = iRestDom;
11282 //MESSAGE("Number of shared faces " << faceDomains.size());
11283 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11285 // --- explore the shared faces domain by domain,
11286 // explore the nodes of the face and see if they belong to a cell in the domain,
11287 // which has only a node or an edge on the border (not a shared face)
11289 for (int idomain = idom0; idomain < nbDomains; idomain++)
11291 //MESSAGE("Domain " << idomain);
11292 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11293 itface = faceDomains.begin();
11294 for (; itface != faceDomains.end(); ++itface)
11296 const std::map<int, int>& domvol = itface->second;
11297 if (!domvol.count(idomain))
11299 DownIdType face = itface->first;
11300 //MESSAGE(" --- face " << face.cellId);
11301 std::set<int> oldNodes;
11303 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11304 std::set<int>::iterator itn = oldNodes.begin();
11305 for (; itn != oldNodes.end(); ++itn)
11308 //MESSAGE(" node " << oldId);
11309 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11310 for (int i=0; i<l.ncells; i++)
11312 int vtkId = l.cells[i];
11313 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11314 if (!domain.count(anElem))
11316 int vtkType = grid->GetCellType(vtkId);
11317 int downId = grid->CellIdToDownId(vtkId);
11320 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11321 continue; // not OK at this stage of the algorithm:
11322 //no cells created after BuildDownWardConnectivity
11324 DownIdType aCell(downId, vtkType);
11325 cellDomains[aCell][idomain] = vtkId;
11326 celldom[vtkId] = idomain;
11327 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11333 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11334 // for each shared face, get the nodes
11335 // for each node, for each domain of the face, create a clone of the node
11337 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11338 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11339 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11341 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11342 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11343 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11345 MESSAGE(".. Duplication of the nodes");
11346 for (int idomain = idom0; idomain < nbDomains; idomain++)
11348 itface = faceDomains.begin();
11349 for (; itface != faceDomains.end(); ++itface)
11351 const std::map<int, int>& domvol = itface->second;
11352 if (!domvol.count(idomain))
11354 DownIdType face = itface->first;
11355 //MESSAGE(" --- face " << face.cellId);
11356 std::set<int> oldNodes;
11358 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11359 std::set<int>::iterator itn = oldNodes.begin();
11360 for (; itn != oldNodes.end(); ++itn)
11363 if (nodeDomains[oldId].empty())
11365 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11366 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11368 std::map<int, int>::const_iterator itdom = domvol.begin();
11369 for (; itdom != domvol.end(); ++itdom)
11371 int idom = itdom->first;
11372 //MESSAGE(" domain " << idom);
11373 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11375 if (nodeDomains[oldId].size() >= 2) // a multiple node
11377 vector<int> orderedDoms;
11378 //MESSAGE("multiple node " << oldId);
11379 if (mutipleNodes.count(oldId))
11380 orderedDoms = mutipleNodes[oldId];
11383 map<int,int>::iterator it = nodeDomains[oldId].begin();
11384 for (; it != nodeDomains[oldId].end(); ++it)
11385 orderedDoms.push_back(it->first);
11387 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11388 //stringstream txt;
11389 //for (int i=0; i<orderedDoms.size(); i++)
11390 // txt << orderedDoms[i] << " ";
11391 //MESSAGE("orderedDoms " << txt.str());
11392 mutipleNodes[oldId] = orderedDoms;
11394 double *coords = grid->GetPoint(oldId);
11395 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11396 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11397 int newId = newNode->getVtkId();
11398 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11399 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11406 MESSAGE(".. Creation of elements");
11407 for (int idomain = idom0; idomain < nbDomains; idomain++)
11409 itface = faceDomains.begin();
11410 for (; itface != faceDomains.end(); ++itface)
11412 std::map<int, int> domvol = itface->second;
11413 if (!domvol.count(idomain))
11415 DownIdType face = itface->first;
11416 //MESSAGE(" --- face " << face.cellId);
11417 std::set<int> oldNodes;
11419 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11420 int nbMultipleNodes = 0;
11421 std::set<int>::iterator itn = oldNodes.begin();
11422 for (; itn != oldNodes.end(); ++itn)
11425 if (mutipleNodes.count(oldId))
11428 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11430 //MESSAGE("multiple Nodes detected on a shared face");
11431 int downId = itface->first.cellId;
11432 unsigned char cellType = itface->first.cellType;
11433 // --- shared edge or shared face ?
11434 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11437 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11438 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11439 if (mutipleNodes.count(nodes[i]))
11440 if (!mutipleNodesToFace.count(nodes[i]))
11441 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11443 else // shared face (between two volumes)
11445 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11446 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11447 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11448 for (int ie =0; ie < nbEdges; ie++)
11451 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11452 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11454 vector<int> vn0 = mutipleNodes[nodes[0]];
11455 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11457 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11458 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11459 if ( vn0[i0] == vn1[i1] )
11460 doms.push_back( vn0[ i0 ]);
11461 if ( doms.size() > 2 )
11463 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11464 double *coords = grid->GetPoint(nodes[0]);
11465 gp_Pnt p0(coords[0], coords[1], coords[2]);
11466 coords = grid->GetPoint(nodes[nbNodes - 1]);
11467 gp_Pnt p1(coords[0], coords[1], coords[2]);
11469 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11470 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11471 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11472 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11473 for ( size_t id = 0; id < doms.size(); id++ )
11475 int idom = doms[id];
11476 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11477 for ( int ivol = 0; ivol < nbvol; ivol++ )
11479 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11480 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11481 if (domain.count(elem))
11483 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11484 domvol[idom] = svol;
11485 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11487 vtkIdType npts = 0;
11488 vtkIdType* pts = 0;
11489 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11490 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11493 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11494 angleDom[idom] = 0;
11498 gp_Pnt g(values[0], values[1], values[2]);
11499 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11500 //MESSAGE(" angle=" << angleDom[idom]);
11506 map<double, int> sortedDom; // sort domains by angle
11507 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11508 sortedDom[ia->second] = ia->first;
11509 vector<int> vnodes;
11511 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11513 vdom.push_back(ib->second);
11514 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11516 for (int ino = 0; ino < nbNodes; ino++)
11517 vnodes.push_back(nodes[ino]);
11518 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11527 // --- iterate on shared faces (volumes to modify, face to extrude)
11528 // get node id's of the face (id SMDS = id VTK)
11529 // create flat element with old and new nodes if requested
11531 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11532 // (domain1 X domain2) = domain1 + MAXINT*domain2
11534 std::map<int, std::map<long,int> > nodeQuadDomains;
11535 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11537 MESSAGE(".. Creation of elements: simple junction");
11538 if (createJointElems)
11541 string joints2DName = "joints2D";
11542 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11543 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11544 string joints3DName = "joints3D";
11545 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11546 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11548 itface = faceDomains.begin();
11549 for (; itface != faceDomains.end(); ++itface)
11551 DownIdType face = itface->first;
11552 std::set<int> oldNodes;
11553 std::set<int>::iterator itn;
11555 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11557 std::map<int, int> domvol = itface->second;
11558 std::map<int, int>::iterator itdom = domvol.begin();
11559 int dom1 = itdom->first;
11560 int vtkVolId = itdom->second;
11562 int dom2 = itdom->first;
11563 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11565 stringstream grpname;
11568 grpname << dom1 << "_" << dom2;
11570 grpname << dom2 << "_" << dom1;
11571 string namegrp = grpname.str();
11572 if (!mapOfJunctionGroups.count(namegrp))
11573 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11574 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11576 sgrp->Add(vol->GetID());
11577 if (vol->GetType() == SMDSAbs_Volume)
11578 joints3DGrp->Add(vol->GetID());
11579 else if (vol->GetType() == SMDSAbs_Face)
11580 joints2DGrp->Add(vol->GetID());
11584 // --- create volumes on multiple domain intersection if requested
11585 // iterate on mutipleNodesToFace
11586 // iterate on edgesMultiDomains
11588 MESSAGE(".. Creation of elements: multiple junction");
11589 if (createJointElems)
11591 // --- iterate on mutipleNodesToFace
11593 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11594 for (; itn != mutipleNodesToFace.end(); ++itn)
11596 int node = itn->first;
11597 vector<int> orderDom = itn->second;
11598 vector<vtkIdType> orderedNodes;
11599 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11600 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11601 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11603 stringstream grpname;
11605 grpname << 0 << "_" << 0;
11607 string namegrp = grpname.str();
11608 if (!mapOfJunctionGroups.count(namegrp))
11609 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11610 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11612 sgrp->Add(face->GetID());
11615 // --- iterate on edgesMultiDomains
11617 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11618 for (; ite != edgesMultiDomains.end(); ++ite)
11620 vector<int> nodes = ite->first;
11621 vector<int> orderDom = ite->second;
11622 vector<vtkIdType> orderedNodes;
11623 if (nodes.size() == 2)
11625 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11626 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11627 if ( orderDom.size() == 3 )
11628 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11629 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11631 for (int idom = orderDom.size()-1; idom >=0; idom--)
11632 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11633 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11636 string namegrp = "jointsMultiples";
11637 if (!mapOfJunctionGroups.count(namegrp))
11638 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11639 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11641 sgrp->Add(vol->GetID());
11645 //INFOS("Quadratic multiple joints not implemented");
11646 // TODO quadratic nodes
11651 // --- list the explicit faces and edges of the mesh that need to be modified,
11652 // i.e. faces and edges built with one or more duplicated nodes.
11653 // associate these faces or edges to their corresponding domain.
11654 // only the first domain found is kept when a face or edge is shared
11656 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11657 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11658 faceOrEdgeDom.clear();
11661 MESSAGE(".. Modification of elements");
11662 for (int idomain = idom0; idomain < nbDomains; idomain++)
11664 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11665 for (; itnod != nodeDomains.end(); ++itnod)
11667 int oldId = itnod->first;
11668 //MESSAGE(" node " << oldId);
11669 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11670 for (int i = 0; i < l.ncells; i++)
11672 int vtkId = l.cells[i];
11673 int vtkType = grid->GetCellType(vtkId);
11674 int downId = grid->CellIdToDownId(vtkId);
11676 continue; // new cells: not to be modified
11677 DownIdType aCell(downId, vtkType);
11678 int volParents[1000];
11679 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11680 for (int j = 0; j < nbvol; j++)
11681 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11682 if (!feDom.count(vtkId))
11684 feDom[vtkId] = idomain;
11685 faceOrEdgeDom[aCell] = emptyMap;
11686 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11687 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11688 // << " type " << vtkType << " downId " << downId);
11694 // --- iterate on shared faces (volumes to modify, face to extrude)
11695 // get node id's of the face
11696 // replace old nodes by new nodes in volumes, and update inverse connectivity
11698 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11699 for (int m=0; m<3; m++)
11701 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11702 itface = (*amap).begin();
11703 for (; itface != (*amap).end(); ++itface)
11705 DownIdType face = itface->first;
11706 std::set<int> oldNodes;
11707 std::set<int>::iterator itn;
11709 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11710 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11711 std::map<int, int> localClonedNodeIds;
11713 std::map<int, int> domvol = itface->second;
11714 std::map<int, int>::iterator itdom = domvol.begin();
11715 for (; itdom != domvol.end(); ++itdom)
11717 int idom = itdom->first;
11718 int vtkVolId = itdom->second;
11719 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11720 localClonedNodeIds.clear();
11721 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11724 if (nodeDomains[oldId].count(idom))
11726 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11727 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11730 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11735 // Remove empty groups (issue 0022812)
11736 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11737 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11739 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11740 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11743 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11744 grid->BuildLinks();
11752 * \brief Double nodes on some external faces and create flat elements.
11753 * Flat elements are mainly used by some types of mechanic calculations.
11755 * Each group of the list must be constituted of faces.
11756 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11757 * @param theElems - list of groups of faces, where a group of faces is a set of
11758 * SMDS_MeshElements sorted by Id.
11759 * @return TRUE if operation has been completed successfully, FALSE otherwise
11761 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11763 MESSAGE("-------------------------------------------------");
11764 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11765 MESSAGE("-------------------------------------------------");
11767 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11769 // --- For each group of faces
11770 // duplicate the nodes, create a flat element based on the face
11771 // replace the nodes of the faces by their clones
11773 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11774 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11775 clonedNodes.clear();
11776 intermediateNodes.clear();
11777 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11778 mapOfJunctionGroups.clear();
11780 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11782 const TIDSortedElemSet& domain = theElems[idom];
11783 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11784 for ( ; elemItr != domain.end(); ++elemItr )
11786 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11787 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11790 // MESSAGE("aFace=" << aFace->GetID());
11791 bool isQuad = aFace->IsQuadratic();
11792 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11794 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11796 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11797 while (nodeIt->more())
11799 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11800 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11802 ln2.push_back(node);
11804 ln0.push_back(node);
11806 const SMDS_MeshNode* clone = 0;
11807 if (!clonedNodes.count(node))
11809 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11810 copyPosition( node, clone );
11811 clonedNodes[node] = clone;
11814 clone = clonedNodes[node];
11817 ln3.push_back(clone);
11819 ln1.push_back(clone);
11821 const SMDS_MeshNode* inter = 0;
11822 if (isQuad && (!isMedium))
11824 if (!intermediateNodes.count(node))
11826 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11827 copyPosition( node, inter );
11828 intermediateNodes[node] = inter;
11831 inter = intermediateNodes[node];
11832 ln4.push_back(inter);
11836 // --- extrude the face
11838 vector<const SMDS_MeshNode*> ln;
11839 SMDS_MeshVolume* vol = 0;
11840 vtkIdType aType = aFace->GetVtkType();
11844 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11845 // MESSAGE("vol prism " << vol->GetID());
11846 ln.push_back(ln1[0]);
11847 ln.push_back(ln1[1]);
11848 ln.push_back(ln1[2]);
11851 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11852 // MESSAGE("vol hexa " << vol->GetID());
11853 ln.push_back(ln1[0]);
11854 ln.push_back(ln1[1]);
11855 ln.push_back(ln1[2]);
11856 ln.push_back(ln1[3]);
11858 case VTK_QUADRATIC_TRIANGLE:
11859 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11860 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11861 // MESSAGE("vol quad prism " << vol->GetID());
11862 ln.push_back(ln1[0]);
11863 ln.push_back(ln1[1]);
11864 ln.push_back(ln1[2]);
11865 ln.push_back(ln3[0]);
11866 ln.push_back(ln3[1]);
11867 ln.push_back(ln3[2]);
11869 case VTK_QUADRATIC_QUAD:
11870 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11871 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11872 // ln4[0], ln4[1], ln4[2], ln4[3]);
11873 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11874 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11875 ln4[0], ln4[1], ln4[2], ln4[3]);
11876 // MESSAGE("vol quad hexa " << vol->GetID());
11877 ln.push_back(ln1[0]);
11878 ln.push_back(ln1[1]);
11879 ln.push_back(ln1[2]);
11880 ln.push_back(ln1[3]);
11881 ln.push_back(ln3[0]);
11882 ln.push_back(ln3[1]);
11883 ln.push_back(ln3[2]);
11884 ln.push_back(ln3[3]);
11894 stringstream grpname;
11898 string namegrp = grpname.str();
11899 if (!mapOfJunctionGroups.count(namegrp))
11900 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11901 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11903 sgrp->Add(vol->GetID());
11906 // --- modify the face
11908 aFace->ChangeNodes(&ln[0], ln.size());
11915 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11916 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11917 * groups of faces to remove inside the object, (idem edges).
11918 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11920 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11921 const TopoDS_Shape& theShape,
11922 SMESH_NodeSearcher* theNodeSearcher,
11923 const char* groupName,
11924 std::vector<double>& nodesCoords,
11925 std::vector<std::vector<int> >& listOfListOfNodes)
11927 MESSAGE("--------------------------------");
11928 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11929 MESSAGE("--------------------------------");
11931 // --- zone of volumes to remove is given :
11932 // 1 either by a geom shape (one or more vertices) and a radius,
11933 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11934 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11935 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11936 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11937 // defined by it's name.
11939 SMESHDS_GroupBase* groupDS = 0;
11940 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11941 while ( groupIt->more() )
11944 SMESH_Group * group = groupIt->next();
11945 if ( !group ) continue;
11946 groupDS = group->GetGroupDS();
11947 if ( !groupDS || groupDS->IsEmpty() ) continue;
11948 std::string grpName = group->GetName();
11949 //MESSAGE("grpName=" << grpName);
11950 if (grpName == groupName)
11956 bool isNodeGroup = false;
11957 bool isNodeCoords = false;
11960 if (groupDS->GetType() != SMDSAbs_Node)
11962 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11965 if (nodesCoords.size() > 0)
11966 isNodeCoords = true; // a list o nodes given by their coordinates
11967 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11969 // --- define groups to build
11971 int idg; // --- group of SMDS volumes
11972 string grpvName = groupName;
11973 grpvName += "_vol";
11974 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11977 MESSAGE("group not created " << grpvName);
11980 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11982 int idgs; // --- group of SMDS faces on the skin
11983 string grpsName = groupName;
11984 grpsName += "_skin";
11985 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11988 MESSAGE("group not created " << grpsName);
11991 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11993 int idgi; // --- group of SMDS faces internal (several shapes)
11994 string grpiName = groupName;
11995 grpiName += "_internalFaces";
11996 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11999 MESSAGE("group not created " << grpiName);
12002 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12004 int idgei; // --- group of SMDS faces internal (several shapes)
12005 string grpeiName = groupName;
12006 grpeiName += "_internalEdges";
12007 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12010 MESSAGE("group not created " << grpeiName);
12013 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12015 // --- build downward connectivity
12017 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12018 meshDS->BuildDownWardConnectivity(true);
12019 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12021 // --- set of volumes detected inside
12023 std::set<int> setOfInsideVol;
12024 std::set<int> setOfVolToCheck;
12026 std::vector<gp_Pnt> gpnts;
12029 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12031 MESSAGE("group of nodes provided");
12032 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12033 while ( elemIt->more() )
12035 const SMDS_MeshElement* elem = elemIt->next();
12038 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12041 SMDS_MeshElement* vol = 0;
12042 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12043 while (volItr->more())
12045 vol = (SMDS_MeshElement*)volItr->next();
12046 setOfInsideVol.insert(vol->getVtkId());
12047 sgrp->Add(vol->GetID());
12051 else if (isNodeCoords)
12053 MESSAGE("list of nodes coordinates provided");
12056 while ( i < nodesCoords.size()-2 )
12058 double x = nodesCoords[i++];
12059 double y = nodesCoords[i++];
12060 double z = nodesCoords[i++];
12061 gp_Pnt p = gp_Pnt(x, y ,z);
12062 gpnts.push_back(p);
12063 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12067 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12069 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12070 TopTools_IndexedMapOfShape vertexMap;
12071 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12072 gp_Pnt p = gp_Pnt(0,0,0);
12073 if (vertexMap.Extent() < 1)
12076 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12078 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12079 p = BRep_Tool::Pnt(vertex);
12080 gpnts.push_back(p);
12081 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12085 if (gpnts.size() > 0)
12088 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12090 nodeId = startNode->GetID();
12091 MESSAGE("nodeId " << nodeId);
12093 double radius2 = radius*radius;
12094 MESSAGE("radius2 " << radius2);
12096 // --- volumes on start node
12098 setOfVolToCheck.clear();
12099 SMDS_MeshElement* startVol = 0;
12100 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12101 while (volItr->more())
12103 startVol = (SMDS_MeshElement*)volItr->next();
12104 setOfVolToCheck.insert(startVol->getVtkId());
12106 if (setOfVolToCheck.empty())
12108 MESSAGE("No volumes found");
12112 // --- starting with central volumes then their neighbors, check if they are inside
12113 // or outside the domain, until no more new neighbor volume is inside.
12114 // Fill the group of inside volumes
12116 std::map<int, double> mapOfNodeDistance2;
12117 mapOfNodeDistance2.clear();
12118 std::set<int> setOfOutsideVol;
12119 while (!setOfVolToCheck.empty())
12121 std::set<int>::iterator it = setOfVolToCheck.begin();
12123 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12124 bool volInside = false;
12125 vtkIdType npts = 0;
12126 vtkIdType* pts = 0;
12127 grid->GetCellPoints(vtkId, npts, pts);
12128 for (int i=0; i<npts; i++)
12130 double distance2 = 0;
12131 if (mapOfNodeDistance2.count(pts[i]))
12133 distance2 = mapOfNodeDistance2[pts[i]];
12134 MESSAGE("point " << pts[i] << " distance2 " << distance2);
12138 double *coords = grid->GetPoint(pts[i]);
12139 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12141 for ( size_t j = 0; j < gpnts.size(); j++ )
12143 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12144 if (d2 < distance2)
12147 if (distance2 < radius2)
12151 mapOfNodeDistance2[pts[i]] = distance2;
12152 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12154 if (distance2 < radius2)
12156 volInside = true; // one or more nodes inside the domain
12157 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12163 setOfInsideVol.insert(vtkId);
12164 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12165 int neighborsVtkIds[NBMAXNEIGHBORS];
12166 int downIds[NBMAXNEIGHBORS];
12167 unsigned char downTypes[NBMAXNEIGHBORS];
12168 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12169 for (int n = 0; n < nbNeighbors; n++)
12170 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12171 setOfVolToCheck.insert(neighborsVtkIds[n]);
12175 setOfOutsideVol.insert(vtkId);
12176 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12178 setOfVolToCheck.erase(vtkId);
12182 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12183 // If yes, add the volume to the inside set
12185 bool addedInside = true;
12186 std::set<int> setOfVolToReCheck;
12187 while (addedInside)
12189 MESSAGE(" --------------------------- re check");
12190 addedInside = false;
12191 std::set<int>::iterator itv = setOfInsideVol.begin();
12192 for (; itv != setOfInsideVol.end(); ++itv)
12195 int neighborsVtkIds[NBMAXNEIGHBORS];
12196 int downIds[NBMAXNEIGHBORS];
12197 unsigned char downTypes[NBMAXNEIGHBORS];
12198 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12199 for (int n = 0; n < nbNeighbors; n++)
12200 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12201 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12203 setOfVolToCheck = setOfVolToReCheck;
12204 setOfVolToReCheck.clear();
12205 while (!setOfVolToCheck.empty())
12207 std::set<int>::iterator it = setOfVolToCheck.begin();
12209 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12211 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12212 int countInside = 0;
12213 int neighborsVtkIds[NBMAXNEIGHBORS];
12214 int downIds[NBMAXNEIGHBORS];
12215 unsigned char downTypes[NBMAXNEIGHBORS];
12216 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12217 for (int n = 0; n < nbNeighbors; n++)
12218 if (setOfInsideVol.count(neighborsVtkIds[n]))
12220 MESSAGE("countInside " << countInside);
12221 if (countInside > 1)
12223 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12224 setOfInsideVol.insert(vtkId);
12225 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12226 addedInside = true;
12229 setOfVolToReCheck.insert(vtkId);
12231 setOfVolToCheck.erase(vtkId);
12235 // --- map of Downward faces at the boundary, inside the global volume
12236 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12237 // fill group of SMDS faces inside the volume (when several volume shapes)
12238 // fill group of SMDS faces on the skin of the global volume (if skin)
12240 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12241 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12242 std::set<int>::iterator it = setOfInsideVol.begin();
12243 for (; it != setOfInsideVol.end(); ++it)
12246 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12247 int neighborsVtkIds[NBMAXNEIGHBORS];
12248 int downIds[NBMAXNEIGHBORS];
12249 unsigned char downTypes[NBMAXNEIGHBORS];
12250 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12251 for (int n = 0; n < nbNeighbors; n++)
12253 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12254 if (neighborDim == 3)
12256 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12258 DownIdType face(downIds[n], downTypes[n]);
12259 boundaryFaces[face] = vtkId;
12261 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12262 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12263 if (vtkFaceId >= 0)
12265 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12266 // find also the smds edges on this face
12267 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12268 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12269 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12270 for (int i = 0; i < nbEdges; i++)
12272 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12273 if (vtkEdgeId >= 0)
12274 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12278 else if (neighborDim == 2) // skin of the volume
12280 DownIdType face(downIds[n], downTypes[n]);
12281 skinFaces[face] = vtkId;
12282 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12283 if (vtkFaceId >= 0)
12284 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12289 // --- identify the edges constituting the wire of each subshape on the skin
12290 // define polylines with the nodes of edges, equivalent to wires
12291 // project polylines on subshapes, and partition, to get geom faces
12293 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12294 std::set<int> emptySet;
12296 std::set<int> shapeIds;
12298 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12299 while (itelem->more())
12301 const SMDS_MeshElement *elem = itelem->next();
12302 int shapeId = elem->getshapeId();
12303 int vtkId = elem->getVtkId();
12304 if (!shapeIdToVtkIdSet.count(shapeId))
12306 shapeIdToVtkIdSet[shapeId] = emptySet;
12307 shapeIds.insert(shapeId);
12309 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12312 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12313 std::set<DownIdType, DownIdCompare> emptyEdges;
12314 emptyEdges.clear();
12316 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12317 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12319 int shapeId = itShape->first;
12320 MESSAGE(" --- Shape ID --- "<< shapeId);
12321 shapeIdToEdges[shapeId] = emptyEdges;
12323 std::vector<int> nodesEdges;
12325 std::set<int>::iterator its = itShape->second.begin();
12326 for (; its != itShape->second.end(); ++its)
12329 MESSAGE(" " << vtkId);
12330 int neighborsVtkIds[NBMAXNEIGHBORS];
12331 int downIds[NBMAXNEIGHBORS];
12332 unsigned char downTypes[NBMAXNEIGHBORS];
12333 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12334 for (int n = 0; n < nbNeighbors; n++)
12336 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12338 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12339 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12340 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12342 DownIdType edge(downIds[n], downTypes[n]);
12343 if (!shapeIdToEdges[shapeId].count(edge))
12345 shapeIdToEdges[shapeId].insert(edge);
12347 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12348 nodesEdges.push_back(vtkNodeId[0]);
12349 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12350 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12356 std::list<int> order;
12358 if (nodesEdges.size() > 0)
12360 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12361 nodesEdges[0] = -1;
12362 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12363 nodesEdges[1] = -1; // do not reuse this edge
12367 int nodeTofind = order.back(); // try first to push back
12369 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12370 if (nodesEdges[i] == nodeTofind)
12372 if ( i == (int) nodesEdges.size() )
12373 found = false; // no follower found on back
12376 if (i%2) // odd ==> use the previous one
12377 if (nodesEdges[i-1] < 0)
12381 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12382 nodesEdges[i-1] = -1;
12384 else // even ==> use the next one
12385 if (nodesEdges[i+1] < 0)
12389 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12390 nodesEdges[i+1] = -1;
12395 // try to push front
12397 nodeTofind = order.front(); // try to push front
12398 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12399 if ( nodesEdges[i] == nodeTofind )
12401 if ( i == (int)nodesEdges.size() )
12403 found = false; // no predecessor found on front
12406 if (i%2) // odd ==> use the previous one
12407 if (nodesEdges[i-1] < 0)
12411 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12412 nodesEdges[i-1] = -1;
12414 else // even ==> use the next one
12415 if (nodesEdges[i+1] < 0)
12419 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12420 nodesEdges[i+1] = -1;
12426 std::vector<int> nodes;
12427 nodes.push_back(shapeId);
12428 std::list<int>::iterator itl = order.begin();
12429 for (; itl != order.end(); itl++)
12431 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12432 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12434 listOfListOfNodes.push_back(nodes);
12437 // partition geom faces with blocFissure
12438 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12439 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12445 //================================================================================
12447 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12448 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12449 * \return TRUE if operation has been completed successfully, FALSE otherwise
12451 //================================================================================
12453 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12455 // iterates on volume elements and detect all free faces on them
12456 SMESHDS_Mesh* aMesh = GetMeshDS();
12460 ElemFeatures faceType( SMDSAbs_Face );
12461 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12462 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12465 const SMDS_MeshVolume* volume = vIt->next();
12466 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12467 vTool.SetExternalNormal();
12468 const int iQuad = volume->IsQuadratic();
12469 faceType.SetQuad( iQuad );
12470 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12472 if (!vTool.IsFreeFace(iface))
12475 vector<const SMDS_MeshNode *> nodes;
12476 int nbFaceNodes = vTool.NbFaceNodes(iface);
12477 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12479 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12480 nodes.push_back(faceNodes[inode]);
12482 if (iQuad) // add medium nodes
12484 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12485 nodes.push_back(faceNodes[inode]);
12486 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12487 nodes.push_back(faceNodes[8]);
12489 // add new face based on volume nodes
12490 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12492 nbExisted++; // face already exsist
12496 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12501 return ( nbFree == ( nbExisted + nbCreated ));
12506 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12508 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12510 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12513 //================================================================================
12515 * \brief Creates missing boundary elements
12516 * \param elements - elements whose boundary is to be checked
12517 * \param dimension - defines type of boundary elements to create
12518 * \param group - a group to store created boundary elements in
12519 * \param targetMesh - a mesh to store created boundary elements in
12520 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12521 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12522 * boundary elements will be copied into the targetMesh
12523 * \param toAddExistingBondary - if true, not only new but also pre-existing
12524 * boundary elements will be added into the new group
12525 * \param aroundElements - if true, elements will be created on boundary of given
12526 * elements else, on boundary of the whole mesh.
12527 * \return nb of added boundary elements
12529 //================================================================================
12531 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12532 Bnd_Dimension dimension,
12533 SMESH_Group* group/*=0*/,
12534 SMESH_Mesh* targetMesh/*=0*/,
12535 bool toCopyElements/*=false*/,
12536 bool toCopyExistingBoundary/*=false*/,
12537 bool toAddExistingBondary/*= false*/,
12538 bool aroundElements/*= false*/)
12540 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12541 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12542 // hope that all elements are of the same type, do not check them all
12543 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12544 throw SALOME_Exception(LOCALIZED("wrong element type"));
12547 toCopyElements = toCopyExistingBoundary = false;
12549 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12550 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12551 int nbAddedBnd = 0;
12553 // editor adding present bnd elements and optionally holding elements to add to the group
12554 SMESH_MeshEditor* presentEditor;
12555 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12556 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12558 SMESH_MesherHelper helper( *myMesh );
12559 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12560 SMDS_VolumeTool vTool;
12561 TIDSortedElemSet avoidSet;
12562 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12565 typedef vector<const SMDS_MeshNode*> TConnectivity;
12566 TConnectivity tgtNodes;
12567 ElemFeatures elemKind( missType ), elemToCopy;
12569 vector<const SMDS_MeshElement*> presentBndElems;
12570 vector<TConnectivity> missingBndElems;
12571 vector<int> freeFacets;
12572 TConnectivity nodes, elemNodes;
12574 SMDS_ElemIteratorPtr eIt;
12575 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12576 else eIt = elemSetIterator( elements );
12578 while (eIt->more())
12580 const SMDS_MeshElement* elem = eIt->next();
12581 const int iQuad = elem->IsQuadratic();
12582 elemKind.SetQuad( iQuad );
12584 // ------------------------------------------------------------------------------------
12585 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12586 // ------------------------------------------------------------------------------------
12587 presentBndElems.clear();
12588 missingBndElems.clear();
12589 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12590 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12592 const SMDS_MeshElement* otherVol = 0;
12593 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12595 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12596 ( !aroundElements || elements.count( otherVol )))
12598 freeFacets.push_back( iface );
12600 if ( missType == SMDSAbs_Face )
12601 vTool.SetExternalNormal();
12602 for ( size_t i = 0; i < freeFacets.size(); ++i )
12604 int iface = freeFacets[i];
12605 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12606 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12607 if ( missType == SMDSAbs_Edge ) // boundary edges
12609 nodes.resize( 2+iQuad );
12610 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12612 for ( size_t j = 0; j < nodes.size(); ++j )
12613 nodes[ j ] = nn[ i+j ];
12614 if ( const SMDS_MeshElement* edge =
12615 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12616 presentBndElems.push_back( edge );
12618 missingBndElems.push_back( nodes );
12621 else // boundary face
12624 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12625 nodes.push_back( nn[inode] ); // add corner nodes
12627 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12628 nodes.push_back( nn[inode] ); // add medium nodes
12629 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12631 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12633 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12634 SMDSAbs_Face, /*noMedium=*/false ))
12635 presentBndElems.push_back( f );
12637 missingBndElems.push_back( nodes );
12639 if ( targetMesh != myMesh )
12641 // add 1D elements on face boundary to be added to a new mesh
12642 const SMDS_MeshElement* edge;
12643 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12646 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12648 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12649 if ( edge && avoidSet.insert( edge ).second )
12650 presentBndElems.push_back( edge );
12656 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12658 avoidSet.clear(), avoidSet.insert( elem );
12659 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12660 SMDS_MeshElement::iterator() );
12661 elemNodes.push_back( elemNodes[0] );
12662 nodes.resize( 2 + iQuad );
12663 const int nbLinks = elem->NbCornerNodes();
12664 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12666 nodes[0] = elemNodes[iN];
12667 nodes[1] = elemNodes[iN+1+iQuad];
12668 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12669 continue; // not free link
12671 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12672 if ( const SMDS_MeshElement* edge =
12673 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12674 presentBndElems.push_back( edge );
12676 missingBndElems.push_back( nodes );
12680 // ---------------------------------
12681 // 2. Add missing boundary elements
12682 // ---------------------------------
12683 if ( targetMesh != myMesh )
12684 // instead of making a map of nodes in this mesh and targetMesh,
12685 // we create nodes with same IDs.
12686 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12688 TConnectivity& srcNodes = missingBndElems[i];
12689 tgtNodes.resize( srcNodes.size() );
12690 for ( inode = 0; inode < srcNodes.size(); ++inode )
12691 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12692 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12694 /*noMedium=*/false))
12696 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12700 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12702 TConnectivity& nodes = missingBndElems[ i ];
12703 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12705 /*noMedium=*/false))
12707 SMDS_MeshElement* newElem =
12708 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12709 nbAddedBnd += bool( newElem );
12711 // try to set a new element to a shape
12712 if ( myMesh->HasShapeToMesh() )
12715 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12716 const size_t nbN = nodes.size() / (iQuad+1 );
12717 for ( inode = 0; inode < nbN && ok; ++inode )
12719 pair<int, TopAbs_ShapeEnum> i_stype =
12720 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12721 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12722 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12724 if ( ok && mediumShapes.size() > 1 )
12726 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12727 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12728 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12730 if (( ok = ( stype_i->first != stype_i_0.first )))
12731 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12732 aMesh->IndexToShape( stype_i_0.second ));
12735 if ( ok && mediumShapes.begin()->first == missShapeType )
12736 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12740 // ----------------------------------
12741 // 3. Copy present boundary elements
12742 // ----------------------------------
12743 if ( toCopyExistingBoundary )
12744 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12746 const SMDS_MeshElement* e = presentBndElems[i];
12747 tgtNodes.resize( e->NbNodes() );
12748 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12749 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12750 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12752 else // store present elements to add them to a group
12753 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12755 presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
12758 } // loop on given elements
12760 // ---------------------------------------------
12761 // 4. Fill group with boundary elements
12762 // ---------------------------------------------
12765 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12766 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12767 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12769 tgtEditor.myLastCreatedElems.Clear();
12770 tgtEditor2.myLastCreatedElems.Clear();
12772 // -----------------------
12773 // 5. Copy given elements
12774 // -----------------------
12775 if ( toCopyElements && targetMesh != myMesh )
12777 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12778 else eIt = elemSetIterator( elements );
12779 while (eIt->more())
12781 const SMDS_MeshElement* elem = eIt->next();
12782 tgtNodes.resize( elem->NbNodes() );
12783 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12784 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12785 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12787 tgtEditor.myLastCreatedElems.Clear();
12793 //================================================================================
12795 * \brief Copy node position and set \a to node on the same geometry
12797 //================================================================================
12799 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12800 const SMDS_MeshNode* to )
12802 if ( !from || !to ) return;
12804 SMDS_PositionPtr pos = from->GetPosition();
12805 if ( !pos || from->getshapeId() < 1 ) return;
12807 switch ( pos->GetTypeOfPosition() )
12809 case SMDS_TOP_3DSPACE: break;
12811 case SMDS_TOP_FACE:
12813 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12814 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12815 fPos->GetUParameter(), fPos->GetVParameter() );
12818 case SMDS_TOP_EDGE:
12820 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12821 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12822 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12825 case SMDS_TOP_VERTEX:
12827 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12830 case SMDS_TOP_UNSPEC: