1 // Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MeshAlgos.hxx"
46 #include "SMESH_MesherHelper.hxx"
47 #include "SMESH_OctreeNode.hxx"
48 #include "SMESH_subMesh.hxx"
50 #include <Basics_OCCTVersion.hxx>
52 #include "utilities.h"
55 #include <BRepAdaptor_Surface.hxx>
56 #include <BRepBuilderAPI_MakeEdge.hxx>
57 #include <BRepClass3d_SolidClassifier.hxx>
58 #include <BRep_Tool.hxx>
60 #include <Extrema_GenExtPS.hxx>
61 #include <Extrema_POnCurv.hxx>
62 #include <Extrema_POnSurf.hxx>
63 #include <Geom2d_Curve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Surface.hxx>
67 #include <Precision.hxx>
68 #include <TColStd_ListOfInteger.hxx>
69 #include <TopAbs_State.hxx>
71 #include <TopExp_Explorer.hxx>
72 #include <TopTools_ListIteratorOfListOfShape.hxx>
73 #include <TopTools_ListOfShape.hxx>
74 #include <TopTools_SequenceOfShape.hxx>
76 #include <TopoDS_Face.hxx>
77 #include <TopoDS_Solid.hxx>
83 #include <gp_Trsf.hxx>
97 #include <boost/tuple/tuple.hpp>
99 #include <Standard_Failure.hxx>
100 #include <Standard_ErrorHandler.hxx>
102 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
105 using namespace SMESH::Controls;
109 template < class ELEM_SET >
110 SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
112 typedef SMDS_SetIterator
113 < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
114 return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
118 //=======================================================================
119 //function : SMESH_MeshEditor
121 //=======================================================================
123 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
124 :myMesh( theMesh ) // theMesh may be NULL
128 //================================================================================
130 * \brief Clears myLastCreatedNodes and myLastCreatedElems
132 //================================================================================
134 void SMESH_MeshEditor::ClearLastCreated()
136 myLastCreatedNodes.Clear();
137 myLastCreatedElems.Clear();
140 //================================================================================
142 * \brief Initializes members by an existing element
143 * \param [in] elem - the source element
144 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
146 //================================================================================
148 SMESH_MeshEditor::ElemFeatures&
149 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
153 myType = elem->GetType();
154 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
156 myIsPoly = elem->IsPoly();
159 myIsQuad = elem->IsQuadratic();
160 if ( myType == SMDSAbs_Volume && !basicOnly )
162 vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
163 myPolyhedQuantities.swap( quant );
167 else if ( myType == SMDSAbs_Ball && !basicOnly )
169 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
175 //=======================================================================
179 //=======================================================================
182 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
183 const ElemFeatures& features)
185 SMDS_MeshElement* e = 0;
186 int nbnode = node.size();
187 SMESHDS_Mesh* mesh = GetMeshDS();
188 const int ID = features.myID;
190 switch ( features.myType ) {
192 if ( !features.myIsPoly ) {
194 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
195 else e = mesh->AddFace (node[0], node[1], node[2] );
197 else if (nbnode == 4) {
198 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
199 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
201 else if (nbnode == 6) {
202 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
203 node[4], node[5], ID);
204 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
207 else if (nbnode == 7) {
208 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
209 node[4], node[5], node[6], ID);
210 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
211 node[4], node[5], node[6] );
213 else if (nbnode == 8) {
214 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
215 node[4], node[5], node[6], node[7], ID);
216 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
217 node[4], node[5], node[6], node[7] );
219 else if (nbnode == 9) {
220 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
221 node[4], node[5], node[6], node[7], node[8], ID);
222 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
223 node[4], node[5], node[6], node[7], node[8] );
226 else if ( !features.myIsQuad )
228 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
229 else e = mesh->AddPolygonalFace (node );
231 else if ( nbnode % 2 == 0 ) // just a protection
233 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
234 else e = mesh->AddQuadPolygonalFace (node );
239 if ( !features.myIsPoly ) {
241 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
242 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
244 else if (nbnode == 5) {
245 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
247 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
250 else if (nbnode == 6) {
251 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
252 node[4], node[5], ID);
253 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
256 else if (nbnode == 8) {
257 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
258 node[4], node[5], node[6], node[7], ID);
259 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
260 node[4], node[5], node[6], node[7] );
262 else if (nbnode == 10) {
263 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
264 node[4], node[5], node[6], node[7],
265 node[8], node[9], ID);
266 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
267 node[4], node[5], node[6], node[7],
270 else if (nbnode == 12) {
271 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
272 node[4], node[5], node[6], node[7],
273 node[8], node[9], node[10], node[11], ID);
274 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
275 node[4], node[5], node[6], node[7],
276 node[8], node[9], node[10], node[11] );
278 else if (nbnode == 13) {
279 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
280 node[4], node[5], node[6], node[7],
281 node[8], node[9], node[10],node[11],
283 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
284 node[4], node[5], node[6], node[7],
285 node[8], node[9], node[10],node[11],
288 else if (nbnode == 15) {
289 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
290 node[4], node[5], node[6], node[7],
291 node[8], node[9], node[10],node[11],
292 node[12],node[13],node[14],ID);
293 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
294 node[4], node[5], node[6], node[7],
295 node[8], node[9], node[10],node[11],
296 node[12],node[13],node[14] );
298 else if (nbnode == 20) {
299 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
300 node[4], node[5], node[6], node[7],
301 node[8], node[9], node[10],node[11],
302 node[12],node[13],node[14],node[15],
303 node[16],node[17],node[18],node[19],ID);
304 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
305 node[4], node[5], node[6], node[7],
306 node[8], node[9], node[10],node[11],
307 node[12],node[13],node[14],node[15],
308 node[16],node[17],node[18],node[19] );
310 else if (nbnode == 27) {
311 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
312 node[4], node[5], node[6], node[7],
313 node[8], node[9], node[10],node[11],
314 node[12],node[13],node[14],node[15],
315 node[16],node[17],node[18],node[19],
316 node[20],node[21],node[22],node[23],
317 node[24],node[25],node[26], ID);
318 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
319 node[4], node[5], node[6], node[7],
320 node[8], node[9], node[10],node[11],
321 node[12],node[13],node[14],node[15],
322 node[16],node[17],node[18],node[19],
323 node[20],node[21],node[22],node[23],
324 node[24],node[25],node[26] );
327 else if ( !features.myIsQuad )
329 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
330 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
334 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
335 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
341 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
342 else e = mesh->AddEdge (node[0], node[1] );
344 else if ( nbnode == 3 ) {
345 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
346 else e = mesh->AddEdge (node[0], node[1], node[2] );
350 case SMDSAbs_0DElement:
352 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
353 else e = mesh->Add0DElement (node[0] );
358 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
359 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
363 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
364 else e = mesh->AddBall (node[0], features.myBallDiameter );
369 if ( e ) myLastCreatedElems.Append( e );
373 //=======================================================================
377 //=======================================================================
379 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
380 const ElemFeatures& features)
382 vector<const SMDS_MeshNode*> nodes;
383 nodes.reserve( nodeIDs.size() );
384 vector<int>::const_iterator id = nodeIDs.begin();
385 while ( id != nodeIDs.end() ) {
386 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
387 nodes.push_back( node );
391 return AddElement( nodes, features );
394 //=======================================================================
396 //purpose : Remove a node or an element.
397 // Modify a compute state of sub-meshes which become empty
398 //=======================================================================
400 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
403 myLastCreatedElems.Clear();
404 myLastCreatedNodes.Clear();
406 SMESHDS_Mesh* aMesh = GetMeshDS();
407 set< SMESH_subMesh *> smmap;
410 list<int>::const_iterator it = theIDs.begin();
411 for ( ; it != theIDs.end(); it++ ) {
412 const SMDS_MeshElement * elem;
414 elem = aMesh->FindNode( *it );
416 elem = aMesh->FindElement( *it );
420 // Notify VERTEX sub-meshes about modification
422 const SMDS_MeshNode* node = cast2Node( elem );
423 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
424 if ( int aShapeID = node->getshapeId() )
425 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
428 // Find sub-meshes to notify about modification
429 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
430 // while ( nodeIt->more() ) {
431 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
432 // const SMDS_PositionPtr& aPosition = node->GetPosition();
433 // if ( aPosition.get() ) {
434 // if ( int aShapeID = aPosition->GetShapeId() ) {
435 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
436 // smmap.insert( sm );
443 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
445 aMesh->RemoveElement( elem );
449 // Notify sub-meshes about modification
450 if ( !smmap.empty() ) {
451 set< SMESH_subMesh *>::iterator smIt;
452 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
453 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
456 // // Check if the whole mesh becomes empty
457 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
458 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
463 //================================================================================
465 * \brief Create 0D elements on all nodes of the given object except those
466 * nodes on which a 0D element already exists.
467 * \param elements - Elements on whose nodes to create 0D elements; if empty,
468 * the all mesh is treated
469 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
471 //================================================================================
473 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
474 TIDSortedElemSet& all0DElems )
476 SMDS_ElemIteratorPtr elemIt;
477 vector< const SMDS_MeshElement* > allNodes;
478 if ( elements.empty() )
480 allNodes.reserve( GetMeshDS()->NbNodes() );
481 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
482 while ( elemIt->more() )
483 allNodes.push_back( elemIt->next() );
485 elemIt = elemSetIterator( allNodes );
489 elemIt = elemSetIterator( elements );
492 while ( elemIt->more() )
494 const SMDS_MeshElement* e = elemIt->next();
495 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
496 while ( nodeIt->more() )
498 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
499 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
501 all0DElems.insert( it0D->next() );
503 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
504 all0DElems.insert( myLastCreatedElems.Last() );
510 //=======================================================================
511 //function : FindShape
512 //purpose : Return an index of the shape theElem is on
513 // or zero if a shape not found
514 //=======================================================================
516 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
518 myLastCreatedElems.Clear();
519 myLastCreatedNodes.Clear();
521 SMESHDS_Mesh * aMesh = GetMeshDS();
522 if ( aMesh->ShapeToMesh().IsNull() )
525 int aShapeID = theElem->getshapeId();
529 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
530 if ( sm->Contains( theElem ))
533 if ( theElem->GetType() == SMDSAbs_Node ) {
534 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
537 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
540 TopoDS_Shape aShape; // the shape a node of theElem is on
541 if ( theElem->GetType() != SMDSAbs_Node )
543 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
544 while ( nodeIt->more() ) {
545 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
546 if ((aShapeID = node->getshapeId()) > 0) {
547 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
548 if ( sm->Contains( theElem ))
550 if ( aShape.IsNull() )
551 aShape = aMesh->IndexToShape( aShapeID );
557 // None of nodes is on a proper shape,
558 // find the shape among ancestors of aShape on which a node is
559 if ( !aShape.IsNull() ) {
560 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
561 for ( ; ancIt.More(); ancIt.Next() ) {
562 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
563 if ( sm && sm->Contains( theElem ))
564 return aMesh->ShapeToIndex( ancIt.Value() );
569 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
570 while ( const SMESHDS_SubMesh* sm = smIt->next() )
571 if ( sm->Contains( theElem ))
578 //=======================================================================
579 //function : IsMedium
581 //=======================================================================
583 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
584 const SMDSAbs_ElementType typeToCheck)
586 bool isMedium = false;
587 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
588 while (it->more() && !isMedium ) {
589 const SMDS_MeshElement* elem = it->next();
590 isMedium = elem->IsMediumNode(node);
595 //=======================================================================
596 //function : shiftNodesQuadTria
597 //purpose : Shift nodes in the array corresponded to quadratic triangle
598 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
599 //=======================================================================
601 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
603 const SMDS_MeshNode* nd1 = aNodes[0];
604 aNodes[0] = aNodes[1];
605 aNodes[1] = aNodes[2];
607 const SMDS_MeshNode* nd2 = aNodes[3];
608 aNodes[3] = aNodes[4];
609 aNodes[4] = aNodes[5];
613 //=======================================================================
614 //function : nbEdgeConnectivity
615 //purpose : return number of the edges connected with the theNode.
616 // if theEdges has connections with the other type of the
617 // elements, return -1
618 //=======================================================================
620 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
622 // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
624 // while(elemIt->more()) {
629 return theNode->NbInverseElements();
632 //=======================================================================
633 //function : getNodesFromTwoTria
635 //=======================================================================
637 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
638 const SMDS_MeshElement * theTria2,
639 vector< const SMDS_MeshNode*>& N1,
640 vector< const SMDS_MeshNode*>& N2)
642 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
643 if ( N1.size() < 6 ) return false;
644 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
645 if ( N2.size() < 6 ) return false;
647 int sames[3] = {-1,-1,-1};
659 if(nbsames!=2) return false;
661 shiftNodesQuadTria(N1);
663 shiftNodesQuadTria(N1);
666 i = sames[0] + sames[1] + sames[2];
668 shiftNodesQuadTria(N2);
670 // now we receive following N1 and N2 (using numeration as in the image below)
671 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
672 // i.e. first nodes from both arrays form a new diagonal
676 //=======================================================================
677 //function : InverseDiag
678 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
679 // but having other common link.
680 // Return False if args are improper
681 //=======================================================================
683 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
684 const SMDS_MeshElement * theTria2 )
686 MESSAGE("InverseDiag");
687 myLastCreatedElems.Clear();
688 myLastCreatedNodes.Clear();
690 if (!theTria1 || !theTria2)
693 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
694 if (!F1) return false;
695 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
696 if (!F2) return false;
697 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
698 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
700 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
701 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
705 // put nodes in array and find out indices of the same ones
706 const SMDS_MeshNode* aNodes [6];
707 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
709 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
710 while ( it->more() ) {
711 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
713 if ( i > 2 ) // theTria2
714 // find same node of theTria1
715 for ( int j = 0; j < 3; j++ )
716 if ( aNodes[ i ] == aNodes[ j ]) {
725 return false; // theTria1 is not a triangle
726 it = theTria2->nodesIterator();
728 if ( i == 6 && it->more() )
729 return false; // theTria2 is not a triangle
732 // find indices of 1,2 and of A,B in theTria1
733 int iA = -1, iB = 0, i1 = 0, i2 = 0;
734 for ( i = 0; i < 6; i++ ) {
735 if ( sameInd [ i ] == -1 ) {
740 if ( iA >= 0) iB = i;
744 // nodes 1 and 2 should not be the same
745 if ( aNodes[ i1 ] == aNodes[ i2 ] )
749 aNodes[ iA ] = aNodes[ i2 ];
751 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
753 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
754 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
758 } // end if(F1 && F2)
760 // check case of quadratic faces
761 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
762 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
764 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
765 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
769 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
770 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
778 vector< const SMDS_MeshNode* > N1;
779 vector< const SMDS_MeshNode* > N2;
780 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
782 // now we receive following N1 and N2 (using numeration as above image)
783 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
784 // i.e. first nodes from both arrays determ new diagonal
786 vector< const SMDS_MeshNode*> N1new( N1.size() );
787 vector< const SMDS_MeshNode*> N2new( N2.size() );
788 N1new.back() = N1.back(); // central node of biquadratic
789 N2new.back() = N2.back();
790 N1new[0] = N1[0]; N2new[0] = N1[0];
791 N1new[1] = N2[0]; N2new[1] = N1[1];
792 N1new[2] = N2[1]; N2new[2] = N2[0];
793 N1new[3] = N1[4]; N2new[3] = N1[3];
794 N1new[4] = N2[3]; N2new[4] = N2[5];
795 N1new[5] = N1[5]; N2new[5] = N1[4];
796 // change nodes in faces
797 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
798 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
800 // move the central node of biquadratic triangle
801 SMESH_MesherHelper helper( *GetMesh() );
802 for ( int is2nd = 0; is2nd < 2; ++is2nd )
804 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
805 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
806 if ( nodes.size() < 7 )
808 helper.SetSubShape( tria->getshapeId() );
809 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
813 xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
814 SMESH_TNodeXYZ( nodes[4] ) +
815 SMESH_TNodeXYZ( nodes[5] )) / 3.;
820 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
821 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
822 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
824 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
825 xyz = S->Value( uv.X(), uv.Y() );
826 xyz.Transform( loc );
827 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
828 nodes[6]->getshapeId() > 0 )
829 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
831 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
836 //=======================================================================
837 //function : findTriangles
838 //purpose : find triangles sharing theNode1-theNode2 link
839 //=======================================================================
841 static bool findTriangles(const SMDS_MeshNode * theNode1,
842 const SMDS_MeshNode * theNode2,
843 const SMDS_MeshElement*& theTria1,
844 const SMDS_MeshElement*& theTria2)
846 if ( !theNode1 || !theNode2 ) return false;
848 theTria1 = theTria2 = 0;
850 set< const SMDS_MeshElement* > emap;
851 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
853 const SMDS_MeshElement* elem = it->next();
854 if ( elem->NbCornerNodes() == 3 )
857 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
859 const SMDS_MeshElement* elem = it->next();
860 if ( emap.count( elem )) {
868 // theTria1 must be element with minimum ID
869 if ( theTria2->GetID() < theTria1->GetID() )
870 std::swap( theTria2, theTria1 );
878 //=======================================================================
879 //function : InverseDiag
880 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
881 // with ones built on the same 4 nodes but having other common link.
882 // Return false if proper faces not found
883 //=======================================================================
885 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
886 const SMDS_MeshNode * theNode2)
888 myLastCreatedElems.Clear();
889 myLastCreatedNodes.Clear();
891 MESSAGE( "::InverseDiag()" );
893 const SMDS_MeshElement *tr1, *tr2;
894 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
897 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
898 if (!F1) return false;
899 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
900 if (!F2) return false;
901 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
902 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
904 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
905 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
909 // put nodes in array
910 // and find indices of 1,2 and of A in tr1 and of B in tr2
911 int i, iA1 = 0, i1 = 0;
912 const SMDS_MeshNode* aNodes1 [3];
913 SMDS_ElemIteratorPtr it;
914 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
915 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
916 if ( aNodes1[ i ] == theNode1 )
917 iA1 = i; // node A in tr1
918 else if ( aNodes1[ i ] != theNode2 )
922 const SMDS_MeshNode* aNodes2 [3];
923 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
924 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
925 if ( aNodes2[ i ] == theNode2 )
926 iB2 = i; // node B in tr2
927 else if ( aNodes2[ i ] != theNode1 )
931 // nodes 1 and 2 should not be the same
932 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
936 aNodes1[ iA1 ] = aNodes2[ i2 ];
938 aNodes2[ iB2 ] = aNodes1[ i1 ];
940 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
941 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
946 // check case of quadratic faces
947 return InverseDiag(tr1,tr2);
950 //=======================================================================
951 //function : getQuadrangleNodes
952 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
953 // fusion of triangles tr1 and tr2 having shared link on
954 // theNode1 and theNode2
955 //=======================================================================
957 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
958 const SMDS_MeshNode * theNode1,
959 const SMDS_MeshNode * theNode2,
960 const SMDS_MeshElement * tr1,
961 const SMDS_MeshElement * tr2 )
963 if( tr1->NbNodes() != tr2->NbNodes() )
965 // find the 4-th node to insert into tr1
966 const SMDS_MeshNode* n4 = 0;
967 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
969 while ( !n4 && i<3 ) {
970 const SMDS_MeshNode * n = cast2Node( it->next() );
972 bool isDiag = ( n == theNode1 || n == theNode2 );
976 // Make an array of nodes to be in a quadrangle
977 int iNode = 0, iFirstDiag = -1;
978 it = tr1->nodesIterator();
981 const SMDS_MeshNode * n = cast2Node( it->next() );
983 bool isDiag = ( n == theNode1 || n == theNode2 );
985 if ( iFirstDiag < 0 )
987 else if ( iNode - iFirstDiag == 1 )
988 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
990 else if ( n == n4 ) {
991 return false; // tr1 and tr2 should not have all the same nodes
993 theQuadNodes[ iNode++ ] = n;
995 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
996 theQuadNodes[ iNode ] = n4;
1001 //=======================================================================
1002 //function : DeleteDiag
1003 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
1004 // with a quadrangle built on the same 4 nodes.
1005 // Return false if proper faces not found
1006 //=======================================================================
1008 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1009 const SMDS_MeshNode * theNode2)
1011 myLastCreatedElems.Clear();
1012 myLastCreatedNodes.Clear();
1014 MESSAGE( "::DeleteDiag()" );
1016 const SMDS_MeshElement *tr1, *tr2;
1017 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1020 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1021 if (!F1) return false;
1022 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1023 if (!F2) return false;
1024 SMESHDS_Mesh * aMesh = GetMeshDS();
1026 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1027 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1029 const SMDS_MeshNode* aNodes [ 4 ];
1030 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1033 const SMDS_MeshElement* newElem = 0;
1034 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1035 myLastCreatedElems.Append(newElem);
1036 AddToSameGroups( newElem, tr1, aMesh );
1037 int aShapeId = tr1->getshapeId();
1040 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1042 aMesh->RemoveElement( tr1 );
1043 aMesh->RemoveElement( tr2 );
1048 // check case of quadratic faces
1049 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1051 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1055 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1056 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1064 vector< const SMDS_MeshNode* > N1;
1065 vector< const SMDS_MeshNode* > N2;
1066 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1068 // now we receive following N1 and N2 (using numeration as above image)
1069 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1070 // i.e. first nodes from both arrays determ new diagonal
1072 const SMDS_MeshNode* aNodes[8];
1082 const SMDS_MeshElement* newElem = 0;
1083 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1084 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1085 myLastCreatedElems.Append(newElem);
1086 AddToSameGroups( newElem, tr1, aMesh );
1087 int aShapeId = tr1->getshapeId();
1090 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1092 aMesh->RemoveElement( tr1 );
1093 aMesh->RemoveElement( tr2 );
1095 // remove middle node (9)
1096 GetMeshDS()->RemoveNode( N1[4] );
1101 //=======================================================================
1102 //function : Reorient
1103 //purpose : Reverse theElement orientation
1104 //=======================================================================
1106 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1108 MESSAGE("Reorient");
1109 myLastCreatedElems.Clear();
1110 myLastCreatedNodes.Clear();
1114 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1115 if ( !it || !it->more() )
1118 const SMDSAbs_ElementType type = theElem->GetType();
1119 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1122 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1123 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1125 const SMDS_VtkVolume* aPolyedre =
1126 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1128 MESSAGE("Warning: bad volumic element");
1131 const int nbFaces = aPolyedre->NbFaces();
1132 vector<const SMDS_MeshNode *> poly_nodes;
1133 vector<int> quantities (nbFaces);
1135 // reverse each face of the polyedre
1136 for (int iface = 1; iface <= nbFaces; iface++) {
1137 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1138 quantities[iface - 1] = nbFaceNodes;
1140 for (inode = nbFaceNodes; inode >= 1; inode--) {
1141 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1142 poly_nodes.push_back(curNode);
1145 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1147 else // other elements
1149 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1150 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1151 if ( interlace.empty() )
1153 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1157 SMDS_MeshCell::applyInterlace( interlace, nodes );
1159 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1164 //================================================================================
1166 * \brief Reorient faces.
1167 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1168 * \param theDirection - desired direction of normal of \a theFace
1169 * \param theFace - one of \a theFaces that sould be oriented according to
1170 * \a theDirection and whose orientation defines orientation of other faces
1171 * \return number of reoriented faces.
1173 //================================================================================
1175 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1176 const gp_Dir& theDirection,
1177 const SMDS_MeshElement * theFace)
1180 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1182 if ( theFaces.empty() )
1184 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1185 while ( fIt->more() )
1186 theFaces.insert( theFaces.end(), fIt->next() );
1189 // orient theFace according to theDirection
1191 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1192 if ( normal * theDirection.XYZ() < 0 )
1193 nbReori += Reorient( theFace );
1195 // Orient other faces
1197 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1198 TIDSortedElemSet avoidSet;
1199 set< SMESH_TLink > checkedLinks;
1200 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1202 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1203 theFaces.erase( theFace );
1204 startFaces.insert( theFace );
1206 int nodeInd1, nodeInd2;
1207 const SMDS_MeshElement* otherFace;
1208 vector< const SMDS_MeshElement* > facesNearLink;
1209 vector< std::pair< int, int > > nodeIndsOfFace;
1211 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1212 while ( !startFaces.empty() )
1214 startFace = startFaces.begin();
1215 theFace = *startFace;
1216 startFaces.erase( startFace );
1217 if ( !visitedFaces.insert( theFace ).second )
1221 avoidSet.insert(theFace);
1223 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1225 const int nbNodes = theFace->NbCornerNodes();
1226 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1228 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1229 linkIt_isNew = checkedLinks.insert( link );
1230 if ( !linkIt_isNew.second )
1232 // link has already been checked and won't be encountered more
1233 // if the group (theFaces) is manifold
1234 //checkedLinks.erase( linkIt_isNew.first );
1238 facesNearLink.clear();
1239 nodeIndsOfFace.clear();
1240 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1242 &nodeInd1, &nodeInd2 )))
1243 if ( otherFace != theFace)
1245 facesNearLink.push_back( otherFace );
1246 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1247 avoidSet.insert( otherFace );
1249 if ( facesNearLink.size() > 1 )
1251 // NON-MANIFOLD mesh shell !
1252 // select a face most co-directed with theFace,
1253 // other faces won't be visited this time
1255 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1256 double proj, maxProj = -1;
1257 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1258 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1259 if (( proj = Abs( NF * NOF )) > maxProj ) {
1261 otherFace = facesNearLink[i];
1262 nodeInd1 = nodeIndsOfFace[i].first;
1263 nodeInd2 = nodeIndsOfFace[i].second;
1266 // not to visit rejected faces
1267 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1268 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1269 visitedFaces.insert( facesNearLink[i] );
1271 else if ( facesNearLink.size() == 1 )
1273 otherFace = facesNearLink[0];
1274 nodeInd1 = nodeIndsOfFace.back().first;
1275 nodeInd2 = nodeIndsOfFace.back().second;
1277 if ( otherFace && otherFace != theFace)
1279 // link must be reverse in otherFace if orientation ot otherFace
1280 // is same as that of theFace
1281 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1283 nbReori += Reorient( otherFace );
1285 startFaces.insert( otherFace );
1288 std::swap( link.first, link.second ); // reverse the link
1294 //================================================================================
1296 * \brief Reorient faces basing on orientation of adjacent volumes.
1297 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1298 * \param theVolumes - reference volumes.
1299 * \param theOutsideNormal - to orient faces to have their normal
1300 * pointing either \a outside or \a inside the adjacent volumes.
1301 * \return number of reoriented faces.
1303 //================================================================================
1305 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1306 TIDSortedElemSet & theVolumes,
1307 const bool theOutsideNormal)
1311 SMDS_ElemIteratorPtr faceIt;
1312 if ( theFaces.empty() )
1313 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1315 faceIt = elemSetIterator( theFaces );
1317 vector< const SMDS_MeshNode* > faceNodes;
1318 TIDSortedElemSet checkedVolumes;
1319 set< const SMDS_MeshNode* > faceNodesSet;
1320 SMDS_VolumeTool volumeTool;
1322 while ( faceIt->more() ) // loop on given faces
1324 const SMDS_MeshElement* face = faceIt->next();
1325 if ( face->GetType() != SMDSAbs_Face )
1328 const int nbCornersNodes = face->NbCornerNodes();
1329 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1331 checkedVolumes.clear();
1332 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1333 while ( vIt->more() )
1335 const SMDS_MeshElement* volume = vIt->next();
1337 if ( !checkedVolumes.insert( volume ).second )
1339 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1342 // is volume adjacent?
1343 bool allNodesCommon = true;
1344 for ( int iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1345 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1346 if ( !allNodesCommon )
1349 // get nodes of a corresponding volume facet
1350 faceNodesSet.clear();
1351 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1352 volumeTool.Set( volume );
1353 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1354 if ( facetID < 0 ) continue;
1355 volumeTool.SetExternalNormal();
1356 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1358 // compare order of faceNodes and facetNodes
1359 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1361 for ( int i = 0; i < 2; ++i )
1363 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1364 for ( int iN = 0; iN < nbCornersNodes; ++iN )
1365 if ( faceNodes[ iN ] == n )
1371 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1372 if ( isOutside != theOutsideNormal )
1373 nbReori += Reorient( face );
1375 } // loop on given faces
1380 //=======================================================================
1381 //function : getBadRate
1383 //=======================================================================
1385 static double getBadRate (const SMDS_MeshElement* theElem,
1386 SMESH::Controls::NumericalFunctorPtr& theCrit)
1388 SMESH::Controls::TSequenceOfXYZ P;
1389 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1391 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1392 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1395 //=======================================================================
1396 //function : QuadToTri
1397 //purpose : Cut quadrangles into triangles.
1398 // theCrit is used to select a diagonal to cut
1399 //=======================================================================
1401 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1402 SMESH::Controls::NumericalFunctorPtr theCrit)
1404 myLastCreatedElems.Clear();
1405 myLastCreatedNodes.Clear();
1407 if ( !theCrit.get() )
1410 SMESHDS_Mesh * aMesh = GetMeshDS();
1412 Handle(Geom_Surface) surface;
1413 SMESH_MesherHelper helper( *GetMesh() );
1415 TIDSortedElemSet::iterator itElem;
1416 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1418 const SMDS_MeshElement* elem = *itElem;
1419 if ( !elem || elem->GetType() != SMDSAbs_Face )
1421 if ( elem->NbCornerNodes() != 4 )
1424 // retrieve element nodes
1425 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1427 // compare two sets of possible triangles
1428 double aBadRate1, aBadRate2; // to what extent a set is bad
1429 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1430 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1431 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1433 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1434 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1435 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1437 const int aShapeId = FindShape( elem );
1438 const SMDS_MeshElement* newElem1 = 0;
1439 const SMDS_MeshElement* newElem2 = 0;
1441 if ( !elem->IsQuadratic() ) // split liner quadrangle
1443 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1444 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1445 if ( aBadRate1 <= aBadRate2 ) {
1446 // tr1 + tr2 is better
1447 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1448 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1451 // tr3 + tr4 is better
1452 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1453 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1456 else // split quadratic quadrangle
1458 helper.SetIsQuadratic( true );
1459 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1461 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1462 if ( aNodes.size() == 9 )
1464 helper.SetIsBiQuadratic( true );
1465 if ( aBadRate1 <= aBadRate2 )
1466 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1468 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1470 // create a new element
1471 if ( aBadRate1 <= aBadRate2 ) {
1472 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1473 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1476 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1477 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1481 // care of a new element
1483 myLastCreatedElems.Append(newElem1);
1484 myLastCreatedElems.Append(newElem2);
1485 AddToSameGroups( newElem1, elem, aMesh );
1486 AddToSameGroups( newElem2, elem, aMesh );
1488 // put a new triangle on the same shape
1490 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1491 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1493 aMesh->RemoveElement( elem );
1498 //=======================================================================
1500 * \brief Split each of given quadrangles into 4 triangles.
1501 * \param theElems - The faces to be splitted. If empty all faces are split.
1503 //=======================================================================
1505 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1507 myLastCreatedElems.Clear();
1508 myLastCreatedNodes.Clear();
1510 SMESH_MesherHelper helper( *GetMesh() );
1511 helper.SetElementsOnShape( true );
1513 SMDS_ElemIteratorPtr faceIt;
1514 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1515 else faceIt = elemSetIterator( theElems );
1518 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1520 vector< const SMDS_MeshNode* > nodes;
1521 SMESHDS_SubMesh* subMeshDS;
1523 Handle(Geom_Surface) surface;
1524 TopLoc_Location loc;
1526 while ( faceIt->more() )
1528 const SMDS_MeshElement* quad = faceIt->next();
1529 if ( !quad || quad->NbCornerNodes() != 4 )
1532 // get a surface the quad is on
1534 if ( quad->getshapeId() < 1 )
1537 helper.SetSubShape( 0 );
1540 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1542 helper.SetSubShape( quad->getshapeId() );
1543 if ( !helper.GetSubShape().IsNull() &&
1544 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1546 F = TopoDS::Face( helper.GetSubShape() );
1547 surface = BRep_Tool::Surface( F, loc );
1548 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1552 helper.SetSubShape( 0 );
1557 // create a central node
1559 const SMDS_MeshNode* nCentral;
1560 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1562 if ( nodes.size() == 9 )
1564 nCentral = nodes.back();
1571 for ( ; iN < nodes.size(); ++iN )
1572 xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1574 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1575 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1577 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1578 xyz[0], xyz[1], xyz[2], xyz[3],
1579 xyz[4], xyz[5], xyz[6], xyz[7] );
1583 for ( ; iN < nodes.size(); ++iN )
1584 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1586 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1587 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1589 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1590 uv[0], uv[1], uv[2], uv[3],
1591 uv[4], uv[5], uv[6], uv[7] );
1593 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1597 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1598 uv[8].X(), uv[8].Y() );
1599 myLastCreatedNodes.Append( nCentral );
1602 // create 4 triangles
1604 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1606 helper.SetIsQuadratic ( nodes.size() > 4 );
1607 helper.SetIsBiQuadratic( nodes.size() == 9 );
1608 if ( helper.GetIsQuadratic() )
1609 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1611 for ( int i = 0; i < 4; ++i )
1613 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1616 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1617 myLastCreatedElems.Append( tria );
1622 //=======================================================================
1623 //function : BestSplit
1624 //purpose : Find better diagonal for cutting.
1625 //=======================================================================
1627 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1628 SMESH::Controls::NumericalFunctorPtr theCrit)
1630 myLastCreatedElems.Clear();
1631 myLastCreatedNodes.Clear();
1636 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1639 if( theQuad->NbNodes()==4 ||
1640 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1642 // retrieve element nodes
1643 const SMDS_MeshNode* aNodes [4];
1644 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1646 //while (itN->more())
1648 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1650 // compare two sets of possible triangles
1651 double aBadRate1, aBadRate2; // to what extent a set is bad
1652 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1653 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1654 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1656 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1657 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1658 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1659 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1660 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1661 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1662 return 1; // diagonal 1-3
1664 return 2; // diagonal 2-4
1671 // Methods of splitting volumes into tetra
1673 const int theHexTo5_1[5*4+1] =
1675 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1677 const int theHexTo5_2[5*4+1] =
1679 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1681 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1683 const int theHexTo6_1[6*4+1] =
1685 1, 5, 6, 0, 0, 1, 2, 6, 0, 4, 5, 6, 0, 4, 6, 7, 0, 2, 3, 6, 0, 3, 7, 6, -1
1687 const int theHexTo6_2[6*4+1] =
1689 2, 6, 7, 1, 1, 2, 3, 7, 1, 5, 6, 7, 1, 5, 7, 4, 1, 3, 0, 7, 1, 0, 4, 7, -1
1691 const int theHexTo6_3[6*4+1] =
1693 3, 7, 4, 2, 2, 3, 0, 4, 2, 6, 7, 4, 2, 6, 4, 5, 2, 0, 1, 4, 2, 1, 5, 4, -1
1695 const int theHexTo6_4[6*4+1] =
1697 0, 4, 5, 3, 3, 0, 1, 5, 3, 7, 4, 5, 3, 7, 5, 6, 3, 1, 2, 5, 3, 2, 6, 5, -1
1699 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1701 const int thePyraTo2_1[2*4+1] =
1703 0, 1, 2, 4, 0, 2, 3, 4, -1
1705 const int thePyraTo2_2[2*4+1] =
1707 1, 2, 3, 4, 1, 3, 0, 4, -1
1709 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1711 const int thePentaTo3_1[3*4+1] =
1713 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1715 const int thePentaTo3_2[3*4+1] =
1717 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1719 const int thePentaTo3_3[3*4+1] =
1721 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1723 const int thePentaTo3_4[3*4+1] =
1725 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1727 const int thePentaTo3_5[3*4+1] =
1729 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1731 const int thePentaTo3_6[3*4+1] =
1733 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1735 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1736 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1738 // Methods of splitting hexahedron into prisms
1740 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1742 0, 1, 8, 4, 5, 9, 1, 2, 8, 5, 6, 9, 2, 3, 8, 6, 7, 9, 3, 0, 8, 7, 4, 9, -1
1744 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1746 1, 0, 8, 2, 3, 9, 0, 4, 8, 3, 7, 9, 4, 5, 8, 7, 6, 9, 5, 1, 8, 6, 2, 9, -1
1748 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1750 0, 3, 9, 1, 2, 8, 3, 7, 9, 2, 6, 8, 7, 4, 9, 6, 5, 8, 4, 0, 9, 5, 1, 8, -1
1753 const int theHexTo2Prisms_BT_1[6*2+1] =
1755 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1757 const int theHexTo2Prisms_BT_2[6*2+1] =
1759 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1761 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1763 const int theHexTo2Prisms_LR_1[6*2+1] =
1765 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1767 const int theHexTo2Prisms_LR_2[6*2+1] =
1769 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1771 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1773 const int theHexTo2Prisms_FB_1[6*2+1] =
1775 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1777 const int theHexTo2Prisms_FB_2[6*2+1] =
1779 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1781 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1784 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1787 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1788 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1789 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1790 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1796 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1797 bool _baryNode; //!< additional node is to be created at cell barycenter
1798 bool _ownConn; //!< to delete _connectivity in destructor
1799 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1801 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1802 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1803 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1804 bool hasFacet( const TTriangleFacet& facet ) const
1806 if ( _nbCorners == 4 )
1808 const int* tetConn = _connectivity;
1809 for ( ; tetConn[0] >= 0; tetConn += 4 )
1810 if (( facet.contains( tetConn[0] ) +
1811 facet.contains( tetConn[1] ) +
1812 facet.contains( tetConn[2] ) +
1813 facet.contains( tetConn[3] )) == 3 )
1816 else // prism, _nbCorners == 6
1818 const int* prismConn = _connectivity;
1819 for ( ; prismConn[0] >= 0; prismConn += 6 )
1821 if (( facet.contains( prismConn[0] ) &&
1822 facet.contains( prismConn[1] ) &&
1823 facet.contains( prismConn[2] ))
1825 ( facet.contains( prismConn[3] ) &&
1826 facet.contains( prismConn[4] ) &&
1827 facet.contains( prismConn[5] )))
1835 //=======================================================================
1837 * \brief return TSplitMethod for the given element to split into tetrahedra
1839 //=======================================================================
1841 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1843 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1845 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1846 // an edge and a face barycenter; tertaherdons are based on triangles and
1847 // a volume barycenter
1848 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1850 // Find out how adjacent volumes are split
1852 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1853 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1854 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1856 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1857 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1858 if ( nbNodes < 4 ) continue;
1860 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1861 const int* nInd = vol.GetFaceNodesIndices( iF );
1864 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1865 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1866 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1867 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1871 int iCom = 0; // common node of triangle faces to split into
1872 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1874 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1875 nInd[ iQ * ( (iCom+1)%nbNodes )],
1876 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1877 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1878 nInd[ iQ * ( (iCom+2)%nbNodes )],
1879 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1880 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1882 triaSplits.push_back( t012 );
1883 triaSplits.push_back( t023 );
1888 if ( !triaSplits.empty() )
1889 hasAdjacentSplits = true;
1892 // Among variants of split method select one compliant with adjacent volumes
1894 TSplitMethod method;
1895 if ( !vol.Element()->IsPoly() && !is24TetMode )
1897 int nbVariants = 2, nbTet = 0;
1898 const int** connVariants = 0;
1899 switch ( vol.Element()->GetEntityType() )
1901 case SMDSEntity_Hexa:
1902 case SMDSEntity_Quad_Hexa:
1903 case SMDSEntity_TriQuad_Hexa:
1904 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1905 connVariants = theHexTo5, nbTet = 5;
1907 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1909 case SMDSEntity_Pyramid:
1910 case SMDSEntity_Quad_Pyramid:
1911 connVariants = thePyraTo2; nbTet = 2;
1913 case SMDSEntity_Penta:
1914 case SMDSEntity_Quad_Penta:
1915 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1920 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1922 // check method compliancy with adjacent tetras,
1923 // all found splits must be among facets of tetras described by this method
1924 method = TSplitMethod( nbTet, connVariants[variant] );
1925 if ( hasAdjacentSplits && method._nbSplits > 0 )
1927 bool facetCreated = true;
1928 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1930 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1931 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1932 facetCreated = method.hasFacet( *facet );
1934 if ( !facetCreated )
1935 method = TSplitMethod(0); // incompatible method
1939 if ( method._nbSplits < 1 )
1941 // No standard method is applicable, use a generic solution:
1942 // each facet of a volume is split into triangles and
1943 // each of triangles and a volume barycenter form a tetrahedron.
1945 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1947 int* connectivity = new int[ maxTetConnSize + 1 ];
1948 method._connectivity = connectivity;
1949 method._ownConn = true;
1950 method._baryNode = !isHex27; // to create central node or not
1953 int baryCenInd = vol.NbNodes() - int( isHex27 );
1954 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1956 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1957 const int* nInd = vol.GetFaceNodesIndices( iF );
1958 // find common node of triangle facets of tetra to create
1959 int iCommon = 0; // index in linear numeration
1960 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1961 if ( !triaSplits.empty() )
1964 const TTriangleFacet* facet = &triaSplits.front();
1965 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1966 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1967 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1970 else if ( nbNodes > 3 && !is24TetMode )
1972 // find the best method of splitting into triangles by aspect ratio
1973 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1974 map< double, int > badness2iCommon;
1975 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1976 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1977 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1980 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1982 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1983 nodes[ iQ*((iLast-1)%nbNodes)],
1984 nodes[ iQ*((iLast )%nbNodes)]);
1985 badness += getBadRate( &tria, aspectRatio );
1987 badness2iCommon.insert( make_pair( badness, iCommon ));
1989 // use iCommon with lowest badness
1990 iCommon = badness2iCommon.begin()->second;
1992 if ( iCommon >= nbNodes )
1993 iCommon = 0; // something wrong
1995 // fill connectivity of tetrahedra based on a current face
1996 int nbTet = nbNodes - 2;
1997 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2002 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2003 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2007 method._faceBaryNode[ iF ] = 0;
2008 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2011 for ( int i = 0; i < nbTet; ++i )
2013 int i1 = i, i2 = (i+1) % nbNodes;
2014 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2015 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2016 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2017 connectivity[ connSize++ ] = faceBaryCenInd;
2018 connectivity[ connSize++ ] = baryCenInd;
2023 for ( int i = 0; i < nbTet; ++i )
2025 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2026 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2027 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2028 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2029 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2030 connectivity[ connSize++ ] = baryCenInd;
2033 method._nbSplits += nbTet;
2035 } // loop on volume faces
2037 connectivity[ connSize++ ] = -1;
2039 } // end of generic solution
2043 //=======================================================================
2045 * \brief return TSplitMethod to split haxhedron into prisms
2047 //=======================================================================
2049 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2050 const int methodFlags,
2051 const int facetToSplit)
2053 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2055 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2057 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2059 static TSplitMethod to4methods[4]; // order BT, LR, FB
2060 if ( to4methods[iF]._nbSplits == 0 )
2064 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2065 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2066 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2069 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2070 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2071 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2074 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2075 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2076 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2078 default: return to4methods[3];
2080 to4methods[iF]._nbSplits = 4;
2081 to4methods[iF]._nbCorners = 6;
2083 return to4methods[iF];
2085 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2087 TSplitMethod method;
2089 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2091 const int nbVariants = 2, nbSplits = 2;
2092 const int** connVariants = 0;
2094 case 0: connVariants = theHexTo2Prisms_BT; break;
2095 case 1: connVariants = theHexTo2Prisms_LR; break;
2096 case 2: connVariants = theHexTo2Prisms_FB; break;
2097 default: return method;
2100 // look for prisms adjacent via facetToSplit and an opposite one
2101 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2103 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2104 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2105 if ( nbNodes != 4 ) return method;
2107 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2108 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2109 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2111 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2113 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2118 // there are adjacent prism
2119 for ( int variant = 0; variant < nbVariants; ++variant )
2121 // check method compliancy with adjacent prisms,
2122 // the found prism facets must be among facets of prisms described by current method
2123 method._nbSplits = nbSplits;
2124 method._nbCorners = 6;
2125 method._connectivity = connVariants[ variant ];
2126 if ( method.hasFacet( *t ))
2131 // No adjacent prisms. Select a variant with a best aspect ratio.
2133 double badness[2] = { 0, 0 };
2134 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2135 const SMDS_MeshNode** nodes = vol.GetNodes();
2136 for ( int variant = 0; variant < nbVariants; ++variant )
2137 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2139 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2140 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2142 method._connectivity = connVariants[ variant ];
2143 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2144 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2145 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2147 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2150 badness[ variant ] += getBadRate( &tria, aspectRatio );
2152 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2154 method._nbSplits = nbSplits;
2155 method._nbCorners = 6;
2156 method._connectivity = connVariants[ iBetter ];
2161 //================================================================================
2163 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2165 //================================================================================
2167 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2168 const SMDSAbs_GeometryType geom ) const
2170 // find the tetrahedron including the three nodes of facet
2171 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2172 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2173 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2174 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2175 while ( volIt1->more() )
2177 const SMDS_MeshElement* v = volIt1->next();
2178 if ( v->GetGeomType() != geom )
2180 const int lastCornerInd = v->NbCornerNodes() - 1;
2181 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2182 continue; // medium node not allowed
2183 const int ind2 = v->GetNodeIndex( n2 );
2184 if ( ind2 < 0 || lastCornerInd < ind2 )
2186 const int ind3 = v->GetNodeIndex( n3 );
2187 if ( ind3 < 0 || lastCornerInd < ind3 )
2194 //=======================================================================
2196 * \brief A key of a face of volume
2198 //=======================================================================
2200 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2202 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2204 TIDSortedNodeSet sortedNodes;
2205 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2206 int nbNodes = vol.NbFaceNodes( iF );
2207 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2208 for ( int i = 0; i < nbNodes; i += iQ )
2209 sortedNodes.insert( fNodes[i] );
2210 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2211 first.first = (*(n++))->GetID();
2212 first.second = (*(n++))->GetID();
2213 second.first = (*(n++))->GetID();
2214 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2219 //=======================================================================
2220 //function : SplitVolumes
2221 //purpose : Split volume elements into tetrahedra or prisms.
2222 // If facet ID < 0, element is split into tetrahedra,
2223 // else a hexahedron is split into prisms so that the given facet is
2224 // split into triangles
2225 //=======================================================================
2227 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2228 const int theMethodFlags)
2230 SMDS_VolumeTool volTool;
2231 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2232 fHelper.ToFixNodeParameters( true );
2234 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2235 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2237 SMESH_SequenceOfElemPtr newNodes, newElems;
2239 // map face of volume to it's baricenrtic node
2240 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2242 vector<const SMDS_MeshElement* > splitVols;
2244 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2245 for ( ; elem2facet != theElems.end(); ++elem2facet )
2247 const SMDS_MeshElement* elem = elem2facet->first;
2248 const int facetToSplit = elem2facet->second;
2249 if ( elem->GetType() != SMDSAbs_Volume )
2251 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2252 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2255 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2257 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2258 getTetraSplitMethod( volTool, theMethodFlags ) :
2259 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2260 if ( splitMethod._nbSplits < 1 ) continue;
2262 // find submesh to add new tetras to
2263 if ( !subMesh || !subMesh->Contains( elem ))
2265 int shapeID = FindShape( elem );
2266 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2267 subMesh = GetMeshDS()->MeshElements( shapeID );
2270 if ( elem->IsQuadratic() )
2273 // add quadratic links to the helper
2274 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2276 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2277 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2278 for ( int iN = 0; iN < nbN; iN += iQ )
2279 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2281 helper.SetIsQuadratic( true );
2286 helper.SetIsQuadratic( false );
2288 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2289 volTool.GetNodes() + elem->NbNodes() );
2290 helper.SetElementsOnShape( true );
2291 if ( splitMethod._baryNode )
2293 // make a node at barycenter
2294 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2295 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2296 nodes.push_back( gcNode );
2297 newNodes.Append( gcNode );
2299 if ( !splitMethod._faceBaryNode.empty() )
2301 // make or find baricentric nodes of faces
2302 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2303 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2305 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2306 volFace2BaryNode.insert
2307 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2310 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2311 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2313 nodes.push_back( iF_n->second = f_n->second );
2318 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2319 const int* volConn = splitMethod._connectivity;
2320 if ( splitMethod._nbCorners == 4 ) // tetra
2321 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2322 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2323 nodes[ volConn[1] ],
2324 nodes[ volConn[2] ],
2325 nodes[ volConn[3] ]));
2327 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2328 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2329 nodes[ volConn[1] ],
2330 nodes[ volConn[2] ],
2331 nodes[ volConn[3] ],
2332 nodes[ volConn[4] ],
2333 nodes[ volConn[5] ]));
2335 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2337 // Split faces on sides of the split volume
2339 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2340 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2342 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2343 if ( nbNodes < 4 ) continue;
2345 // find an existing face
2346 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2347 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2348 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2349 /*noMedium=*/false))
2352 helper.SetElementsOnShape( false );
2353 vector< const SMDS_MeshElement* > triangles;
2355 // find submesh to add new triangles in
2356 if ( !fSubMesh || !fSubMesh->Contains( face ))
2358 int shapeID = FindShape( face );
2359 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2361 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2362 if ( iF_n != splitMethod._faceBaryNode.end() )
2364 const SMDS_MeshNode *baryNode = iF_n->second;
2365 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2367 const SMDS_MeshNode* n1 = fNodes[iN];
2368 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2369 const SMDS_MeshNode *n3 = baryNode;
2370 if ( !volTool.IsFaceExternal( iF ))
2372 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2374 if ( fSubMesh ) // update position of the bary node on geometry
2377 subMesh->RemoveNode( baryNode, false );
2378 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2379 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2380 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2382 fHelper.SetSubShape( s );
2383 gp_XY uv( 1e100, 1e100 );
2385 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2386 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2389 // node is too far from the surface
2390 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2391 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2392 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2399 // among possible triangles create ones discribed by split method
2400 const int* nInd = volTool.GetFaceNodesIndices( iF );
2401 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2402 int iCom = 0; // common node of triangle faces to split into
2403 list< TTriangleFacet > facets;
2404 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2406 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2407 nInd[ iQ * ( (iCom+1)%nbNodes )],
2408 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2409 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2410 nInd[ iQ * ( (iCom+2)%nbNodes )],
2411 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2412 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2414 facets.push_back( t012 );
2415 facets.push_back( t023 );
2416 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2417 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2418 nInd[ iQ * ((iLast-1)%nbNodes )],
2419 nInd[ iQ * ((iLast )%nbNodes )]));
2423 list< TTriangleFacet >::iterator facet = facets.begin();
2424 if ( facet == facets.end() )
2426 for ( ; facet != facets.end(); ++facet )
2428 if ( !volTool.IsFaceExternal( iF ))
2429 swap( facet->_n2, facet->_n3 );
2430 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2431 volNodes[ facet->_n2 ],
2432 volNodes[ facet->_n3 ]));
2435 for ( int i = 0; i < triangles.size(); ++i )
2437 if ( !triangles[i] ) continue;
2439 fSubMesh->AddElement( triangles[i]);
2440 newElems.Append( triangles[i] );
2442 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2443 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2445 } // while a face based on facet nodes exists
2446 } // loop on volume faces to split them into triangles
2448 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2450 if ( geomType == SMDSEntity_TriQuad_Hexa )
2452 // remove medium nodes that could become free
2453 for ( int i = 20; i < volTool.NbNodes(); ++i )
2454 if ( volNodes[i]->NbInverseElements() == 0 )
2455 GetMeshDS()->RemoveNode( volNodes[i] );
2457 } // loop on volumes to split
2459 myLastCreatedNodes = newNodes;
2460 myLastCreatedElems = newElems;
2463 //=======================================================================
2464 //function : GetHexaFacetsToSplit
2465 //purpose : For hexahedra that will be split into prisms, finds facets to
2466 // split into triangles. Only hexahedra adjacent to the one closest
2467 // to theFacetNormal.Location() are returned.
2468 //param [in,out] theHexas - the hexahedra
2469 //param [in] theFacetNormal - facet normal
2470 //param [out] theFacets - the hexahedra and found facet IDs
2471 //=======================================================================
2473 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2474 const gp_Ax1& theFacetNormal,
2475 TFacetOfElem & theFacets)
2477 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2479 // Find a hexa closest to the location of theFacetNormal
2481 const SMDS_MeshElement* startHex;
2483 // get SMDS_ElemIteratorPtr on theHexas
2484 typedef const SMDS_MeshElement* TValue;
2485 typedef TIDSortedElemSet::iterator TSetIterator;
2486 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2487 typedef SMDS_MeshElement::GeomFilter TFilter;
2488 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2489 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2490 ( new TElemSetIter( theHexas.begin(),
2492 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2494 SMESH_ElementSearcher* searcher =
2495 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2497 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2502 throw SALOME_Exception( THIS_METHOD "startHex not found");
2505 // Select a facet of startHex by theFacetNormal
2507 SMDS_VolumeTool vTool( startHex );
2508 double norm[3], dot, maxDot = 0;
2510 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2511 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2513 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2521 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2523 // Fill theFacets starting from facetID of startHex
2525 // facets used for seach of volumes adjacent to already treated ones
2526 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2527 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2528 TFacetMap facetsToCheck;
2530 set<const SMDS_MeshNode*> facetNodes;
2531 const SMDS_MeshElement* curHex;
2533 const bool allHex = ( theHexas.size() == myMesh->NbHexas() );
2537 // move in two directions from startHex via facetID
2538 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2541 int curFacet = facetID;
2542 if ( is2nd ) // do not treat startHex twice
2544 vTool.Set( curHex );
2545 if ( vTool.IsFreeFace( curFacet, &curHex ))
2551 vTool.GetFaceNodes( curFacet, facetNodes );
2552 vTool.Set( curHex );
2553 curFacet = vTool.GetFaceIndex( facetNodes );
2558 // store a facet to split
2559 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2561 theFacets.insert( make_pair( curHex, -1 ));
2564 if ( !allHex && !theHexas.count( curHex ))
2567 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2568 theFacets.insert( make_pair( curHex, curFacet ));
2569 if ( !facetIt2isNew.second )
2572 // remember not-to-split facets in facetsToCheck
2573 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2574 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2576 if ( iF == curFacet && iF == oppFacet )
2578 TVolumeFaceKey facetKey ( vTool, iF );
2579 TElemFacets elemFacet( facetIt2isNew.first, iF );
2580 pair< TFacetMap::iterator, bool > it2isnew =
2581 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2582 if ( !it2isnew.second )
2583 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2585 // pass to a volume adjacent via oppFacet
2586 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2592 // get a new curFacet
2593 vTool.GetFaceNodes( oppFacet, facetNodes );
2594 vTool.Set( curHex );
2595 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2598 } // move in two directions from startHex via facetID
2600 // Find a new startHex by facetsToCheck
2604 TFacetMap::iterator fIt = facetsToCheck.begin();
2605 while ( !startHex && fIt != facetsToCheck.end() )
2607 const TElemFacets& elemFacets = fIt->second;
2608 const SMDS_MeshElement* hex = elemFacets.first->first;
2609 int splitFacet = elemFacets.first->second;
2610 int lateralFacet = elemFacets.second;
2611 facetsToCheck.erase( fIt );
2612 fIt = facetsToCheck.begin();
2615 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2616 curHex->GetGeomType() != SMDSGeom_HEXA )
2618 if ( !allHex && !theHexas.count( curHex ))
2623 // find a facet of startHex to split
2625 set<const SMDS_MeshNode*> lateralNodes;
2626 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2627 vTool.GetFaceNodes( splitFacet, facetNodes );
2628 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2629 vTool.Set( startHex );
2630 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2632 // look for a facet of startHex having common nodes with facetNodes
2633 // but not lateralFacet
2634 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2636 if ( iF == lateralFacet )
2638 int nbCommonNodes = 0;
2639 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2640 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2641 nbCommonNodes += facetNodes.count( nn[ iN ]);
2643 if ( nbCommonNodes >= 2 )
2650 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2652 } // while ( startHex )
2659 //================================================================================
2661 * \brief Selects nodes of several elements according to a given interlace
2662 * \param [in] srcNodes - nodes to select from
2663 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2664 * \param [in] interlace - indices of nodes for all elements
2665 * \param [in] nbElems - nb of elements
2666 * \param [in] nbNodes - nb of nodes in each element
2667 * \param [in] mesh - the mesh
2668 * \param [out] elemQueue - a list to push elements found by the selected nodes
2669 * \param [in] type - type of elements to look for
2671 //================================================================================
2673 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2674 vector< const SMDS_MeshNode* >* tgtNodesVec,
2675 const int* interlace,
2678 SMESHDS_Mesh* mesh = 0,
2679 list< const SMDS_MeshElement* >* elemQueue=0,
2680 SMDSAbs_ElementType type=SMDSAbs_All)
2682 for ( int iE = 0; iE < nbElems; ++iE )
2684 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2685 const int* select = & interlace[iE*nbNodes];
2686 elemNodes.resize( nbNodes );
2687 for ( int iN = 0; iN < nbNodes; ++iN )
2688 elemNodes[iN] = srcNodes[ select[ iN ]];
2690 const SMDS_MeshElement* e;
2692 for ( int iE = 0; iE < nbElems; ++iE )
2693 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2694 elemQueue->push_back( e );
2698 //=======================================================================
2700 * Split bi-quadratic elements into linear ones without creation of additional nodes
2701 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2702 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2703 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2704 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2705 * will be split in order to keep the mesh conformal.
2706 * \param elems - elements to split
2708 //=======================================================================
2710 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2712 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2713 vector<const SMDS_MeshElement* > splitElems;
2714 list< const SMDS_MeshElement* > elemQueue;
2715 list< const SMDS_MeshElement* >::iterator elemIt;
2717 SMESHDS_Mesh * mesh = GetMeshDS();
2718 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2719 int nbElems, nbNodes;
2721 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2722 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2725 elemQueue.push_back( *elemSetIt );
2726 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2728 const SMDS_MeshElement* elem = *elemIt;
2729 switch( elem->GetEntityType() )
2731 case SMDSEntity_TriQuad_Hexa: // HEX27
2733 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2734 nbElems = nbNodes = 8;
2735 elemType = & hexaType;
2737 // get nodes for new elements
2738 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2739 { 1,9,20,8, 17,22,26,21 },
2740 { 2,10,20,9, 18,23,26,22 },
2741 { 3,11,20,10, 19,24,26,23 },
2742 { 16,21,26,24, 4,12,25,15 },
2743 { 17,22,26,21, 5,13,25,12 },
2744 { 18,23,26,22, 6,14,25,13 },
2745 { 19,24,26,23, 7,15,25,14 }};
2746 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2748 // add boundary faces to elemQueue
2749 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2750 { 4,5,6,7, 12,13,14,15, 25 },
2751 { 0,1,5,4, 8,17,12,16, 21 },
2752 { 1,2,6,5, 9,18,13,17, 22 },
2753 { 2,3,7,6, 10,19,14,18, 23 },
2754 { 3,0,4,7, 11,16,15,19, 24 }};
2755 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2757 // add boundary segments to elemQueue
2758 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2759 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2760 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2761 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2764 case SMDSEntity_BiQuad_Triangle: // TRIA7
2766 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2769 elemType = & quadType;
2771 // get nodes for new elements
2772 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2773 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2775 // add boundary segments to elemQueue
2776 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2777 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2780 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2782 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2785 elemType = & quadType;
2787 // get nodes for new elements
2788 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2789 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2791 // add boundary segments to elemQueue
2792 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2793 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2796 case SMDSEntity_Quad_Edge:
2798 if ( elemIt == elemQueue.begin() )
2799 continue; // an elem is in theElems
2800 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2803 elemType = & segType;
2805 // get nodes for new elements
2806 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2807 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2811 } // switch( elem->GetEntityType() )
2813 // Create new elements
2815 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2819 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2820 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2821 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2822 //elemType->SetID( -1 );
2824 for ( int iE = 0; iE < nbElems; ++iE )
2825 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2828 ReplaceElemInGroups( elem, splitElems, mesh );
2831 for ( size_t i = 0; i < splitElems.size(); ++i )
2832 subMesh->AddElement( splitElems[i] );
2837 //=======================================================================
2838 //function : AddToSameGroups
2839 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2840 //=======================================================================
2842 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2843 const SMDS_MeshElement* elemInGroups,
2844 SMESHDS_Mesh * aMesh)
2846 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2847 if (!groups.empty()) {
2848 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2849 for ( ; grIt != groups.end(); grIt++ ) {
2850 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2851 if ( group && group->Contains( elemInGroups ))
2852 group->SMDSGroup().Add( elemToAdd );
2858 //=======================================================================
2859 //function : RemoveElemFromGroups
2860 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2861 //=======================================================================
2862 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2863 SMESHDS_Mesh * aMesh)
2865 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2866 if (!groups.empty())
2868 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2869 for (; GrIt != groups.end(); GrIt++)
2871 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2872 if (!grp || grp->IsEmpty()) continue;
2873 grp->SMDSGroup().Remove(removeelem);
2878 //================================================================================
2880 * \brief Replace elemToRm by elemToAdd in the all groups
2882 //================================================================================
2884 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2885 const SMDS_MeshElement* elemToAdd,
2886 SMESHDS_Mesh * aMesh)
2888 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2889 if (!groups.empty()) {
2890 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2891 for ( ; grIt != groups.end(); grIt++ ) {
2892 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2893 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2894 group->SMDSGroup().Add( elemToAdd );
2899 //================================================================================
2901 * \brief Replace elemToRm by elemToAdd in the all groups
2903 //================================================================================
2905 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2906 const vector<const SMDS_MeshElement*>& elemToAdd,
2907 SMESHDS_Mesh * aMesh)
2909 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2910 if (!groups.empty())
2912 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2913 for ( ; grIt != groups.end(); grIt++ ) {
2914 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2915 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2916 for ( int i = 0; i < elemToAdd.size(); ++i )
2917 group->SMDSGroup().Add( elemToAdd[ i ] );
2922 //=======================================================================
2923 //function : QuadToTri
2924 //purpose : Cut quadrangles into triangles.
2925 // theCrit is used to select a diagonal to cut
2926 //=======================================================================
2928 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2929 const bool the13Diag)
2931 myLastCreatedElems.Clear();
2932 myLastCreatedNodes.Clear();
2934 MESSAGE( "::QuadToTri()" );
2936 SMESHDS_Mesh * aMesh = GetMeshDS();
2938 Handle(Geom_Surface) surface;
2939 SMESH_MesherHelper helper( *GetMesh() );
2941 TIDSortedElemSet::iterator itElem;
2942 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2943 const SMDS_MeshElement* elem = *itElem;
2944 if ( !elem || elem->GetType() != SMDSAbs_Face )
2946 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2947 if(!isquad) continue;
2949 if(elem->NbNodes()==4) {
2950 // retrieve element nodes
2951 const SMDS_MeshNode* aNodes [4];
2952 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2954 while ( itN->more() )
2955 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2957 int aShapeId = FindShape( elem );
2958 const SMDS_MeshElement* newElem1 = 0;
2959 const SMDS_MeshElement* newElem2 = 0;
2961 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2962 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2965 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2966 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2968 myLastCreatedElems.Append(newElem1);
2969 myLastCreatedElems.Append(newElem2);
2970 // put a new triangle on the same shape and add to the same groups
2973 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2974 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2976 AddToSameGroups( newElem1, elem, aMesh );
2977 AddToSameGroups( newElem2, elem, aMesh );
2978 aMesh->RemoveElement( elem );
2981 // Quadratic quadrangle
2983 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2985 // get surface elem is on
2986 int aShapeId = FindShape( elem );
2987 if ( aShapeId != helper.GetSubShapeID() ) {
2991 shape = aMesh->IndexToShape( aShapeId );
2992 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2993 TopoDS_Face face = TopoDS::Face( shape );
2994 surface = BRep_Tool::Surface( face );
2995 if ( !surface.IsNull() )
2996 helper.SetSubShape( shape );
3000 const SMDS_MeshNode* aNodes [8];
3001 const SMDS_MeshNode* inFaceNode = 0;
3002 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3004 while ( itN->more() ) {
3005 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3006 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
3007 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
3009 inFaceNode = aNodes[ i-1 ];
3013 // find middle point for (0,1,2,3)
3014 // and create a node in this point;
3016 if ( surface.IsNull() ) {
3018 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
3022 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
3025 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
3027 p = surface->Value( uv.X(), uv.Y() ).XYZ();
3029 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
3030 myLastCreatedNodes.Append(newN);
3032 // create a new element
3033 const SMDS_MeshElement* newElem1 = 0;
3034 const SMDS_MeshElement* newElem2 = 0;
3036 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3037 aNodes[6], aNodes[7], newN );
3038 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3039 newN, aNodes[4], aNodes[5] );
3042 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3043 aNodes[7], aNodes[4], newN );
3044 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3045 newN, aNodes[5], aNodes[6] );
3047 myLastCreatedElems.Append(newElem1);
3048 myLastCreatedElems.Append(newElem2);
3049 // put a new triangle on the same shape and add to the same groups
3052 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3053 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3055 AddToSameGroups( newElem1, elem, aMesh );
3056 AddToSameGroups( newElem2, elem, aMesh );
3057 aMesh->RemoveElement( elem );
3064 //=======================================================================
3065 //function : getAngle
3067 //=======================================================================
3069 double getAngle(const SMDS_MeshElement * tr1,
3070 const SMDS_MeshElement * tr2,
3071 const SMDS_MeshNode * n1,
3072 const SMDS_MeshNode * n2)
3074 double angle = 2. * M_PI; // bad angle
3077 SMESH::Controls::TSequenceOfXYZ P1, P2;
3078 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3079 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3082 if(!tr1->IsQuadratic())
3083 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3085 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3086 if ( N1.SquareMagnitude() <= gp::Resolution() )
3088 if(!tr2->IsQuadratic())
3089 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3091 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3092 if ( N2.SquareMagnitude() <= gp::Resolution() )
3095 // find the first diagonal node n1 in the triangles:
3096 // take in account a diagonal link orientation
3097 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3098 for ( int t = 0; t < 2; t++ ) {
3099 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3100 int i = 0, iDiag = -1;
3101 while ( it->more()) {
3102 const SMDS_MeshElement *n = it->next();
3103 if ( n == n1 || n == n2 ) {
3107 if ( i - iDiag == 1 )
3108 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3117 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3120 angle = N1.Angle( N2 );
3125 // =================================================
3126 // class generating a unique ID for a pair of nodes
3127 // and able to return nodes by that ID
3128 // =================================================
3132 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3133 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3136 long GetLinkID (const SMDS_MeshNode * n1,
3137 const SMDS_MeshNode * n2) const
3139 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3142 bool GetNodes (const long theLinkID,
3143 const SMDS_MeshNode* & theNode1,
3144 const SMDS_MeshNode* & theNode2) const
3146 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3147 if ( !theNode1 ) return false;
3148 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3149 if ( !theNode2 ) return false;
3155 const SMESHDS_Mesh* myMesh;
3160 //=======================================================================
3161 //function : TriToQuad
3162 //purpose : Fuse neighbour triangles into quadrangles.
3163 // theCrit is used to select a neighbour to fuse with.
3164 // theMaxAngle is a max angle between element normals at which
3165 // fusion is still performed.
3166 //=======================================================================
3168 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3169 SMESH::Controls::NumericalFunctorPtr theCrit,
3170 const double theMaxAngle)
3172 myLastCreatedElems.Clear();
3173 myLastCreatedNodes.Clear();
3175 MESSAGE( "::TriToQuad()" );
3177 if ( !theCrit.get() )
3180 SMESHDS_Mesh * aMesh = GetMeshDS();
3182 // Prepare data for algo: build
3183 // 1. map of elements with their linkIDs
3184 // 2. map of linkIDs with their elements
3186 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3187 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3188 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3189 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3191 TIDSortedElemSet::iterator itElem;
3192 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3194 const SMDS_MeshElement* elem = *itElem;
3195 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3196 bool IsTria = ( elem->NbCornerNodes()==3 );
3197 if (!IsTria) continue;
3199 // retrieve element nodes
3200 const SMDS_MeshNode* aNodes [4];
3201 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3204 aNodes[ i++ ] = itN->next();
3205 aNodes[ 3 ] = aNodes[ 0 ];
3208 for ( i = 0; i < 3; i++ ) {
3209 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3210 // check if elements sharing a link can be fused
3211 itLE = mapLi_listEl.find( link );
3212 if ( itLE != mapLi_listEl.end() ) {
3213 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3215 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3216 //if ( FindShape( elem ) != FindShape( elem2 ))
3217 // continue; // do not fuse triangles laying on different shapes
3218 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3219 continue; // avoid making badly shaped quads
3220 (*itLE).second.push_back( elem );
3223 mapLi_listEl[ link ].push_back( elem );
3225 mapEl_setLi [ elem ].insert( link );
3228 // Clean the maps from the links shared by a sole element, ie
3229 // links to which only one element is bound in mapLi_listEl
3231 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3232 int nbElems = (*itLE).second.size();
3233 if ( nbElems < 2 ) {
3234 const SMDS_MeshElement* elem = (*itLE).second.front();
3235 SMESH_TLink link = (*itLE).first;
3236 mapEl_setLi[ elem ].erase( link );
3237 if ( mapEl_setLi[ elem ].empty() )
3238 mapEl_setLi.erase( elem );
3242 // Algo: fuse triangles into quadrangles
3244 while ( ! mapEl_setLi.empty() ) {
3245 // Look for the start element:
3246 // the element having the least nb of shared links
3247 const SMDS_MeshElement* startElem = 0;
3249 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3250 int nbLinks = (*itEL).second.size();
3251 if ( nbLinks < minNbLinks ) {
3252 startElem = (*itEL).first;
3253 minNbLinks = nbLinks;
3254 if ( minNbLinks == 1 )
3259 // search elements to fuse starting from startElem or links of elements
3260 // fused earlyer - startLinks
3261 list< SMESH_TLink > startLinks;
3262 while ( startElem || !startLinks.empty() ) {
3263 while ( !startElem && !startLinks.empty() ) {
3264 // Get an element to start, by a link
3265 SMESH_TLink linkId = startLinks.front();
3266 startLinks.pop_front();
3267 itLE = mapLi_listEl.find( linkId );
3268 if ( itLE != mapLi_listEl.end() ) {
3269 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3270 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3271 for ( ; itE != listElem.end() ; itE++ )
3272 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3274 mapLi_listEl.erase( itLE );
3279 // Get candidates to be fused
3280 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3281 const SMESH_TLink *link12, *link13;
3283 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3284 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3285 ASSERT( !setLi.empty() );
3286 set< SMESH_TLink >::iterator itLi;
3287 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3289 const SMESH_TLink & link = (*itLi);
3290 itLE = mapLi_listEl.find( link );
3291 if ( itLE == mapLi_listEl.end() )
3294 const SMDS_MeshElement* elem = (*itLE).second.front();
3296 elem = (*itLE).second.back();
3297 mapLi_listEl.erase( itLE );
3298 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3309 // add other links of elem to list of links to re-start from
3310 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3311 set< SMESH_TLink >::iterator it;
3312 for ( it = links.begin(); it != links.end(); it++ ) {
3313 const SMESH_TLink& link2 = (*it);
3314 if ( link2 != link )
3315 startLinks.push_back( link2 );
3319 // Get nodes of possible quadrangles
3320 const SMDS_MeshNode *n12 [4], *n13 [4];
3321 bool Ok12 = false, Ok13 = false;
3322 const SMDS_MeshNode *linkNode1, *linkNode2;
3324 linkNode1 = link12->first;
3325 linkNode2 = link12->second;
3326 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3330 linkNode1 = link13->first;
3331 linkNode2 = link13->second;
3332 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3336 // Choose a pair to fuse
3337 if ( Ok12 && Ok13 ) {
3338 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3339 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3340 double aBadRate12 = getBadRate( &quad12, theCrit );
3341 double aBadRate13 = getBadRate( &quad13, theCrit );
3342 if ( aBadRate13 < aBadRate12 )
3349 // and remove fused elems and remove links from the maps
3350 mapEl_setLi.erase( tr1 );
3353 mapEl_setLi.erase( tr2 );
3354 mapLi_listEl.erase( *link12 );
3355 if ( tr1->NbNodes() == 3 )
3357 const SMDS_MeshElement* newElem = 0;
3358 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3359 myLastCreatedElems.Append(newElem);
3360 AddToSameGroups( newElem, tr1, aMesh );
3361 int aShapeId = tr1->getshapeId();
3363 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3364 aMesh->RemoveElement( tr1 );
3365 aMesh->RemoveElement( tr2 );
3368 vector< const SMDS_MeshNode* > N1;
3369 vector< const SMDS_MeshNode* > N2;
3370 getNodesFromTwoTria(tr1,tr2,N1,N2);
3371 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3372 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3373 // i.e. first nodes from both arrays form a new diagonal
3374 const SMDS_MeshNode* aNodes[8];
3383 const SMDS_MeshElement* newElem = 0;
3384 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3385 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3386 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3388 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3389 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3390 myLastCreatedElems.Append(newElem);
3391 AddToSameGroups( newElem, tr1, aMesh );
3392 int aShapeId = tr1->getshapeId();
3394 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3395 aMesh->RemoveElement( tr1 );
3396 aMesh->RemoveElement( tr2 );
3397 // remove middle node (9)
3398 if ( N1[4]->NbInverseElements() == 0 )
3399 aMesh->RemoveNode( N1[4] );
3400 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3401 aMesh->RemoveNode( N1[6] );
3402 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3403 aMesh->RemoveNode( N2[6] );
3408 mapEl_setLi.erase( tr3 );
3409 mapLi_listEl.erase( *link13 );
3410 if ( tr1->NbNodes() == 3 ) {
3411 const SMDS_MeshElement* newElem = 0;
3412 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3413 myLastCreatedElems.Append(newElem);
3414 AddToSameGroups( newElem, tr1, aMesh );
3415 int aShapeId = tr1->getshapeId();
3417 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3418 aMesh->RemoveElement( tr1 );
3419 aMesh->RemoveElement( tr3 );
3422 vector< const SMDS_MeshNode* > N1;
3423 vector< const SMDS_MeshNode* > N2;
3424 getNodesFromTwoTria(tr1,tr3,N1,N2);
3425 // now we receive following N1 and N2 (using numeration as above image)
3426 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3427 // i.e. first nodes from both arrays form a new diagonal
3428 const SMDS_MeshNode* aNodes[8];
3437 const SMDS_MeshElement* newElem = 0;
3438 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3439 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3440 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3442 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3443 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3444 myLastCreatedElems.Append(newElem);
3445 AddToSameGroups( newElem, tr1, aMesh );
3446 int aShapeId = tr1->getshapeId();
3448 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3449 aMesh->RemoveElement( tr1 );
3450 aMesh->RemoveElement( tr3 );
3451 // remove middle node (9)
3452 if ( N1[4]->NbInverseElements() == 0 )
3453 aMesh->RemoveNode( N1[4] );
3454 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3455 aMesh->RemoveNode( N1[6] );
3456 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3457 aMesh->RemoveNode( N2[6] );
3461 // Next element to fuse: the rejected one
3463 startElem = Ok12 ? tr3 : tr2;
3465 } // if ( startElem )
3466 } // while ( startElem || !startLinks.empty() )
3467 } // while ( ! mapEl_setLi.empty() )
3473 /*#define DUMPSO(txt) \
3474 // cout << txt << endl;
3475 //=============================================================================
3479 //=============================================================================
3480 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3484 int tmp = idNodes[ i1 ];
3485 idNodes[ i1 ] = idNodes[ i2 ];
3486 idNodes[ i2 ] = tmp;
3487 gp_Pnt Ptmp = P[ i1 ];
3490 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3493 //=======================================================================
3494 //function : SortQuadNodes
3495 //purpose : Set 4 nodes of a quadrangle face in a good order.
3496 // Swap 1<->2 or 2<->3 nodes and correspondingly return
3498 //=======================================================================
3500 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3505 for ( i = 0; i < 4; i++ ) {
3506 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3508 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3511 gp_Vec V1(P[0], P[1]);
3512 gp_Vec V2(P[0], P[2]);
3513 gp_Vec V3(P[0], P[3]);
3515 gp_Vec Cross1 = V1 ^ V2;
3516 gp_Vec Cross2 = V2 ^ V3;
3519 if (Cross1.Dot(Cross2) < 0)
3524 if (Cross1.Dot(Cross2) < 0)
3528 swap ( i, i + 1, idNodes, P );
3530 // for ( int ii = 0; ii < 4; ii++ ) {
3531 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3532 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3538 //=======================================================================
3539 //function : SortHexaNodes
3540 //purpose : Set 8 nodes of a hexahedron in a good order.
3541 // Return success status
3542 //=======================================================================
3544 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3549 DUMPSO( "INPUT: ========================================");
3550 for ( i = 0; i < 8; i++ ) {
3551 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3552 if ( !n ) return false;
3553 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3554 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3556 DUMPSO( "========================================");
3559 set<int> faceNodes; // ids of bottom face nodes, to be found
3560 set<int> checkedId1; // ids of tried 2-nd nodes
3561 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3562 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
3563 int iMin, iLoop1 = 0;
3565 // Loop to try the 2-nd nodes
3567 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3569 // Find not checked 2-nd node
3570 for ( i = 1; i < 8; i++ )
3571 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3572 int id1 = idNodes[i];
3573 swap ( 1, i, idNodes, P );
3574 checkedId1.insert ( id1 );
3578 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3579 // ie that all but meybe one (id3 which is on the same face) nodes
3580 // lay on the same side from the triangle plane.
3582 bool manyInPlane = false; // more than 4 nodes lay in plane
3584 while ( ++iLoop2 < 6 ) {
3586 // get 1-2-3 plane coeffs
3587 Standard_Real A, B, C, D;
3588 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3589 if ( N.SquareMagnitude() > gp::Resolution() )
3591 gp_Pln pln ( P[0], N );
3592 pln.Coefficients( A, B, C, D );
3594 // find the node (iMin) closest to pln
3595 Standard_Real dist[ 8 ], minDist = DBL_MAX;
3597 for ( i = 3; i < 8; i++ ) {
3598 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3599 if ( fabs( dist[i] ) < minDist ) {
3600 minDist = fabs( dist[i] );
3603 if ( fabs( dist[i] ) <= tol )
3604 idInPln.insert( idNodes[i] );
3607 // there should not be more than 4 nodes in bottom plane
3608 if ( idInPln.size() > 1 )
3610 DUMPSO( "### idInPln.size() = " << idInPln.size());
3611 // idInPlane does not contain the first 3 nodes
3612 if ( manyInPlane || idInPln.size() == 5)
3613 return false; // all nodes in one plane
3616 // set the 1-st node to be not in plane
3617 for ( i = 3; i < 8; i++ ) {
3618 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3619 DUMPSO( "### Reset 0-th node");
3620 swap( 0, i, idNodes, P );
3625 // reset to re-check second nodes
3626 leastDist = DBL_MAX;
3630 break; // from iLoop2;
3633 // check that the other 4 nodes are on the same side
3634 bool sameSide = true;
3635 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3636 for ( i = 3; sameSide && i < 8; i++ ) {
3638 sameSide = ( isNeg == dist[i] <= 0.);
3641 // keep best solution
3642 if ( sameSide && minDist < leastDist ) {
3643 leastDist = minDist;
3645 faceNodes.insert( idNodes[ 1 ] );
3646 faceNodes.insert( idNodes[ 2 ] );
3647 faceNodes.insert( idNodes[ iMin ] );
3648 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3649 << " leastDist = " << leastDist);
3650 if ( leastDist <= DBL_MIN )
3655 // set next 3-d node to check
3656 int iNext = 2 + iLoop2;
3658 DUMPSO( "Try 2-nd");
3659 swap ( 2, iNext, idNodes, P );
3661 } // while ( iLoop2 < 6 )
3664 if ( faceNodes.empty() ) return false;
3666 // Put the faceNodes in proper places
3667 for ( i = 4; i < 8; i++ ) {
3668 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3669 // find a place to put
3671 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3673 DUMPSO( "Set faceNodes");
3674 swap ( iTo, i, idNodes, P );
3679 // Set nodes of the found bottom face in good order
3680 DUMPSO( " Found bottom face: ");
3681 i = SortQuadNodes( theMesh, idNodes );
3683 gp_Pnt Ptmp = P[ i ];
3688 // for ( int ii = 0; ii < 4; ii++ ) {
3689 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3690 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3693 // Gravity center of the top and bottom faces
3694 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3695 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3697 // Get direction from the bottom to the top face
3698 gp_Vec upDir ( aGCb, aGCt );
3699 Standard_Real upDirSize = upDir.Magnitude();
3700 if ( upDirSize <= gp::Resolution() ) return false;
3703 // Assure that the bottom face normal points up
3704 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3705 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3706 if ( Nb.Dot( upDir ) < 0 ) {
3707 DUMPSO( "Reverse bottom face");
3708 swap( 1, 3, idNodes, P );
3711 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3712 Standard_Real minDist = DBL_MAX;
3713 for ( i = 4; i < 8; i++ ) {
3714 // projection of P[i] to the plane defined by P[0] and upDir
3715 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3716 Standard_Real sqDist = P[0].SquareDistance( Pp );
3717 if ( sqDist < minDist ) {
3722 DUMPSO( "Set 4-th");
3723 swap ( 4, iMin, idNodes, P );
3725 // Set nodes of the top face in good order
3726 DUMPSO( "Sort top face");
3727 i = SortQuadNodes( theMesh, &idNodes[4] );
3730 gp_Pnt Ptmp = P[ i ];
3735 // Assure that direction of the top face normal is from the bottom face
3736 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3737 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3738 if ( Nt.Dot( upDir ) < 0 ) {
3739 DUMPSO( "Reverse top face");
3740 swap( 5, 7, idNodes, P );
3743 // DUMPSO( "OUTPUT: ========================================");
3744 // for ( i = 0; i < 8; i++ ) {
3745 // float *p = ugrid->GetPoint(idNodes[i]);
3746 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3752 //================================================================================
3754 * \brief Return nodes linked to the given one
3755 * \param theNode - the node
3756 * \param linkedNodes - the found nodes
3757 * \param type - the type of elements to check
3759 * Medium nodes are ignored
3761 //================================================================================
3763 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3764 TIDSortedElemSet & linkedNodes,
3765 SMDSAbs_ElementType type )
3767 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3768 while ( elemIt->more() )
3770 const SMDS_MeshElement* elem = elemIt->next();
3771 if(elem->GetType() == SMDSAbs_0DElement)
3774 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3775 if ( elem->GetType() == SMDSAbs_Volume )
3777 SMDS_VolumeTool vol( elem );
3778 while ( nodeIt->more() ) {
3779 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3780 if ( theNode != n && vol.IsLinked( theNode, n ))
3781 linkedNodes.insert( n );
3786 for ( int i = 0; nodeIt->more(); ++i ) {
3787 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3788 if ( n == theNode ) {
3789 int iBefore = i - 1;
3791 if ( elem->IsQuadratic() ) {
3792 int nb = elem->NbNodes() / 2;
3793 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3794 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3796 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3797 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3804 //=======================================================================
3805 //function : laplacianSmooth
3806 //purpose : pulls theNode toward the center of surrounding nodes directly
3807 // connected to that node along an element edge
3808 //=======================================================================
3810 void laplacianSmooth(const SMDS_MeshNode* theNode,
3811 const Handle(Geom_Surface)& theSurface,
3812 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3814 // find surrounding nodes
3816 TIDSortedElemSet nodeSet;
3817 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3819 // compute new coodrs
3821 double coord[] = { 0., 0., 0. };
3822 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3823 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3824 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3825 if ( theSurface.IsNull() ) { // smooth in 3D
3826 coord[0] += node->X();
3827 coord[1] += node->Y();
3828 coord[2] += node->Z();
3830 else { // smooth in 2D
3831 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3832 gp_XY* uv = theUVMap[ node ];
3833 coord[0] += uv->X();
3834 coord[1] += uv->Y();
3837 int nbNodes = nodeSet.size();
3840 coord[0] /= nbNodes;
3841 coord[1] /= nbNodes;
3843 if ( !theSurface.IsNull() ) {
3844 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3845 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3846 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3852 coord[2] /= nbNodes;
3856 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3859 //=======================================================================
3860 //function : centroidalSmooth
3861 //purpose : pulls theNode toward the element-area-weighted centroid of the
3862 // surrounding elements
3863 //=======================================================================
3865 void centroidalSmooth(const SMDS_MeshNode* theNode,
3866 const Handle(Geom_Surface)& theSurface,
3867 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3869 gp_XYZ aNewXYZ(0.,0.,0.);
3870 SMESH::Controls::Area anAreaFunc;
3871 double totalArea = 0.;
3876 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3877 while ( elemIt->more() )
3879 const SMDS_MeshElement* elem = elemIt->next();
3882 gp_XYZ elemCenter(0.,0.,0.);
3883 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3884 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3885 int nn = elem->NbNodes();
3886 if(elem->IsQuadratic()) nn = nn/2;
3888 //while ( itN->more() ) {
3890 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3892 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3893 aNodePoints.push_back( aP );
3894 if ( !theSurface.IsNull() ) { // smooth in 2D
3895 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3896 gp_XY* uv = theUVMap[ aNode ];
3897 aP.SetCoord( uv->X(), uv->Y(), 0. );
3901 double elemArea = anAreaFunc.GetValue( aNodePoints );
3902 totalArea += elemArea;
3904 aNewXYZ += elemCenter * elemArea;
3906 aNewXYZ /= totalArea;
3907 if ( !theSurface.IsNull() ) {
3908 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3909 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3914 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3917 //=======================================================================
3918 //function : getClosestUV
3919 //purpose : return UV of closest projection
3920 //=======================================================================
3922 static bool getClosestUV (Extrema_GenExtPS& projector,
3923 const gp_Pnt& point,
3926 projector.Perform( point );
3927 if ( projector.IsDone() ) {
3928 double u, v, minVal = DBL_MAX;
3929 for ( int i = projector.NbExt(); i > 0; i-- )
3930 if ( projector.SquareDistance( i ) < minVal ) {
3931 minVal = projector.SquareDistance( i );
3932 projector.Point( i ).Parameter( u, v );
3934 result.SetCoord( u, v );
3940 //=======================================================================
3942 //purpose : Smooth theElements during theNbIterations or until a worst
3943 // element has aspect ratio <= theTgtAspectRatio.
3944 // Aspect Ratio varies in range [1.0, inf].
3945 // If theElements is empty, the whole mesh is smoothed.
3946 // theFixedNodes contains additionally fixed nodes. Nodes built
3947 // on edges and boundary nodes are always fixed.
3948 //=======================================================================
3950 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3951 set<const SMDS_MeshNode*> & theFixedNodes,
3952 const SmoothMethod theSmoothMethod,
3953 const int theNbIterations,
3954 double theTgtAspectRatio,
3957 myLastCreatedElems.Clear();
3958 myLastCreatedNodes.Clear();
3960 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3962 if ( theTgtAspectRatio < 1.0 )
3963 theTgtAspectRatio = 1.0;
3965 const double disttol = 1.e-16;
3967 SMESH::Controls::AspectRatio aQualityFunc;
3969 SMESHDS_Mesh* aMesh = GetMeshDS();
3971 if ( theElems.empty() ) {
3972 // add all faces to theElems
3973 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3974 while ( fIt->more() ) {
3975 const SMDS_MeshElement* face = fIt->next();
3976 theElems.insert( theElems.end(), face );
3979 // get all face ids theElems are on
3980 set< int > faceIdSet;
3981 TIDSortedElemSet::iterator itElem;
3983 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3984 int fId = FindShape( *itElem );
3985 // check that corresponding submesh exists and a shape is face
3987 faceIdSet.find( fId ) == faceIdSet.end() &&
3988 aMesh->MeshElements( fId )) {
3989 TopoDS_Shape F = aMesh->IndexToShape( fId );
3990 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3991 faceIdSet.insert( fId );
3994 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3996 // ===============================================
3997 // smooth elements on each TopoDS_Face separately
3998 // ===============================================
4000 SMESH_MesherHelper helper( *GetMesh() );
4002 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
4003 for ( ; fId != faceIdSet.rend(); ++fId )
4005 // get face surface and submesh
4006 Handle(Geom_Surface) surface;
4007 SMESHDS_SubMesh* faceSubMesh = 0;
4009 double fToler2 = 0, f,l;
4010 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
4011 bool isUPeriodic = false, isVPeriodic = false;
4014 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
4015 surface = BRep_Tool::Surface( face );
4016 faceSubMesh = aMesh->MeshElements( *fId );
4017 fToler2 = BRep_Tool::Tolerance( face );
4018 fToler2 *= fToler2 * 10.;
4019 isUPeriodic = surface->IsUPeriodic();
4022 isVPeriodic = surface->IsVPeriodic();
4025 surface->Bounds( u1, u2, v1, v2 );
4026 helper.SetSubShape( face );
4028 // ---------------------------------------------------------
4029 // for elements on a face, find movable and fixed nodes and
4030 // compute UV for them
4031 // ---------------------------------------------------------
4032 bool checkBoundaryNodes = false;
4033 bool isQuadratic = false;
4034 set<const SMDS_MeshNode*> setMovableNodes;
4035 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4036 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4037 list< const SMDS_MeshElement* > elemsOnFace;
4039 Extrema_GenExtPS projector;
4040 GeomAdaptor_Surface surfAdaptor;
4041 if ( !surface.IsNull() ) {
4042 surfAdaptor.Load( surface );
4043 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4045 int nbElemOnFace = 0;
4046 itElem = theElems.begin();
4047 // loop on not yet smoothed elements: look for elems on a face
4048 while ( itElem != theElems.end() )
4050 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4051 break; // all elements found
4053 const SMDS_MeshElement* elem = *itElem;
4054 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4055 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4059 elemsOnFace.push_back( elem );
4060 theElems.erase( itElem++ );
4064 isQuadratic = elem->IsQuadratic();
4066 // get movable nodes of elem
4067 const SMDS_MeshNode* node;
4068 SMDS_TypeOfPosition posType;
4069 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4070 int nn = 0, nbn = elem->NbNodes();
4071 if(elem->IsQuadratic())
4073 while ( nn++ < nbn ) {
4074 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4075 const SMDS_PositionPtr& pos = node->GetPosition();
4076 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4077 if (posType != SMDS_TOP_EDGE &&
4078 posType != SMDS_TOP_VERTEX &&
4079 theFixedNodes.find( node ) == theFixedNodes.end())
4081 // check if all faces around the node are on faceSubMesh
4082 // because a node on edge may be bound to face
4083 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4085 if ( faceSubMesh ) {
4086 while ( eIt->more() && all ) {
4087 const SMDS_MeshElement* e = eIt->next();
4088 all = faceSubMesh->Contains( e );
4092 setMovableNodes.insert( node );
4094 checkBoundaryNodes = true;
4096 if ( posType == SMDS_TOP_3DSPACE )
4097 checkBoundaryNodes = true;
4100 if ( surface.IsNull() )
4103 // get nodes to check UV
4104 list< const SMDS_MeshNode* > uvCheckNodes;
4105 const SMDS_MeshNode* nodeInFace = 0;
4106 itN = elem->nodesIterator();
4107 nn = 0; nbn = elem->NbNodes();
4108 if(elem->IsQuadratic())
4110 while ( nn++ < nbn ) {
4111 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4112 if ( node->GetPosition()->GetDim() == 2 )
4114 if ( uvMap.find( node ) == uvMap.end() )
4115 uvCheckNodes.push_back( node );
4116 // add nodes of elems sharing node
4117 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4118 // while ( eIt->more() ) {
4119 // const SMDS_MeshElement* e = eIt->next();
4120 // if ( e != elem ) {
4121 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4122 // while ( nIt->more() ) {
4123 // const SMDS_MeshNode* n =
4124 // static_cast<const SMDS_MeshNode*>( nIt->next() );
4125 // if ( uvMap.find( n ) == uvMap.end() )
4126 // uvCheckNodes.push_back( n );
4132 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4133 for ( ; n != uvCheckNodes.end(); ++n ) {
4136 const SMDS_PositionPtr& pos = node->GetPosition();
4137 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4141 bool toCheck = true;
4142 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4144 // compute not existing UV
4145 bool project = ( posType == SMDS_TOP_3DSPACE );
4146 // double dist1 = DBL_MAX, dist2 = 0;
4147 // if ( posType != SMDS_TOP_3DSPACE ) {
4148 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4149 // project = dist1 > fToler2;
4151 if ( project ) { // compute new UV
4153 gp_Pnt pNode = SMESH_TNodeXYZ( node );
4154 if ( !getClosestUV( projector, pNode, newUV )) {
4155 MESSAGE("Node Projection Failed " << node);
4159 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4161 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4163 // if ( posType != SMDS_TOP_3DSPACE )
4164 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4165 // if ( dist2 < dist1 )
4169 // store UV in the map
4170 listUV.push_back( uv );
4171 uvMap.insert( make_pair( node, &listUV.back() ));
4173 } // loop on not yet smoothed elements
4175 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4176 checkBoundaryNodes = true;
4178 // fix nodes on mesh boundary
4180 if ( checkBoundaryNodes ) {
4181 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4182 map< SMESH_TLink, int >::iterator link_nb;
4183 // put all elements links to linkNbMap
4184 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4185 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4186 const SMDS_MeshElement* elem = (*elemIt);
4187 int nbn = elem->NbCornerNodes();
4188 // loop on elem links: insert them in linkNbMap
4189 for ( int iN = 0; iN < nbn; ++iN ) {
4190 const SMDS_MeshNode* n1 = elem->GetNode( iN );
4191 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4192 SMESH_TLink link( n1, n2 );
4193 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4197 // remove nodes that are in links encountered only once from setMovableNodes
4198 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4199 if ( link_nb->second == 1 ) {
4200 setMovableNodes.erase( link_nb->first.node1() );
4201 setMovableNodes.erase( link_nb->first.node2() );
4206 // -----------------------------------------------------
4207 // for nodes on seam edge, compute one more UV ( uvMap2 );
4208 // find movable nodes linked to nodes on seam and which
4209 // are to be smoothed using the second UV ( uvMap2 )
4210 // -----------------------------------------------------
4212 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4213 if ( !surface.IsNull() ) {
4214 TopExp_Explorer eExp( face, TopAbs_EDGE );
4215 for ( ; eExp.More(); eExp.Next() ) {
4216 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4217 if ( !BRep_Tool::IsClosed( edge, face ))
4219 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4220 if ( !sm ) continue;
4221 // find out which parameter varies for a node on seam
4224 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4225 if ( pcurve.IsNull() ) continue;
4226 uv1 = pcurve->Value( f );
4228 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4229 if ( pcurve.IsNull() ) continue;
4230 uv2 = pcurve->Value( f );
4231 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4233 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4234 std::swap( uv1, uv2 );
4235 // get nodes on seam and its vertices
4236 list< const SMDS_MeshNode* > seamNodes;
4237 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4238 while ( nSeamIt->more() ) {
4239 const SMDS_MeshNode* node = nSeamIt->next();
4240 if ( !isQuadratic || !IsMedium( node ))
4241 seamNodes.push_back( node );
4243 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4244 for ( ; vExp.More(); vExp.Next() ) {
4245 sm = aMesh->MeshElements( vExp.Current() );
4247 nSeamIt = sm->GetNodes();
4248 while ( nSeamIt->more() )
4249 seamNodes.push_back( nSeamIt->next() );
4252 // loop on nodes on seam
4253 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4254 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4255 const SMDS_MeshNode* nSeam = *noSeIt;
4256 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4257 if ( n_uv == uvMap.end() )
4260 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4261 // set the second UV
4262 listUV.push_back( *n_uv->second );
4263 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4264 if ( uvMap2.empty() )
4265 uvMap2 = uvMap; // copy the uvMap contents
4266 uvMap2[ nSeam ] = &listUV.back();
4268 // collect movable nodes linked to ones on seam in nodesNearSeam
4269 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4270 while ( eIt->more() ) {
4271 const SMDS_MeshElement* e = eIt->next();
4272 int nbUseMap1 = 0, nbUseMap2 = 0;
4273 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4274 int nn = 0, nbn = e->NbNodes();
4275 if(e->IsQuadratic()) nbn = nbn/2;
4276 while ( nn++ < nbn )
4278 const SMDS_MeshNode* n =
4279 static_cast<const SMDS_MeshNode*>( nIt->next() );
4281 setMovableNodes.find( n ) == setMovableNodes.end() )
4283 // add only nodes being closer to uv2 than to uv1
4284 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4285 // 0.5 * ( n->Y() + nSeam->Y() ),
4286 // 0.5 * ( n->Z() + nSeam->Z() ));
4288 // getClosestUV( projector, pMid, uv );
4289 double x = uvMap[ n ]->Coord( iPar );
4290 if ( Abs( uv1.Coord( iPar ) - x ) >
4291 Abs( uv2.Coord( iPar ) - x )) {
4292 nodesNearSeam.insert( n );
4298 // for centroidalSmooth all element nodes must
4299 // be on one side of a seam
4300 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4301 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4303 while ( nn++ < nbn ) {
4304 const SMDS_MeshNode* n =
4305 static_cast<const SMDS_MeshNode*>( nIt->next() );
4306 setMovableNodes.erase( n );
4310 } // loop on nodes on seam
4311 } // loop on edge of a face
4312 } // if ( !face.IsNull() )
4314 if ( setMovableNodes.empty() ) {
4315 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4316 continue; // goto next face
4324 double maxRatio = -1., maxDisplacement = -1.;
4325 set<const SMDS_MeshNode*>::iterator nodeToMove;
4326 for ( it = 0; it < theNbIterations; it++ ) {
4327 maxDisplacement = 0.;
4328 nodeToMove = setMovableNodes.begin();
4329 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4330 const SMDS_MeshNode* node = (*nodeToMove);
4331 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4334 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4335 if ( theSmoothMethod == LAPLACIAN )
4336 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4338 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4340 // node displacement
4341 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4342 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4343 if ( aDispl > maxDisplacement )
4344 maxDisplacement = aDispl;
4346 // no node movement => exit
4347 //if ( maxDisplacement < 1.e-16 ) {
4348 if ( maxDisplacement < disttol ) {
4349 MESSAGE("-- no node movement --");
4353 // check elements quality
4355 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4356 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4357 const SMDS_MeshElement* elem = (*elemIt);
4358 if ( !elem || elem->GetType() != SMDSAbs_Face )
4360 SMESH::Controls::TSequenceOfXYZ aPoints;
4361 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4362 double aValue = aQualityFunc.GetValue( aPoints );
4363 if ( aValue > maxRatio )
4367 if ( maxRatio <= theTgtAspectRatio ) {
4368 MESSAGE("-- quality achived --");
4371 if (it+1 == theNbIterations) {
4372 MESSAGE("-- Iteration limit exceeded --");
4374 } // smoothing iterations
4376 MESSAGE(" Face id: " << *fId <<
4377 " Nb iterstions: " << it <<
4378 " Displacement: " << maxDisplacement <<
4379 " Aspect Ratio " << maxRatio);
4381 // ---------------------------------------
4382 // new nodes positions are computed,
4383 // record movement in DS and set new UV
4384 // ---------------------------------------
4385 nodeToMove = setMovableNodes.begin();
4386 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4387 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4388 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4389 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4390 if ( node_uv != uvMap.end() ) {
4391 gp_XY* uv = node_uv->second;
4393 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4397 // move medium nodes of quadratic elements
4400 vector<const SMDS_MeshNode*> nodes;
4402 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4403 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4405 const SMDS_MeshElement* QF = *elemIt;
4406 if ( QF->IsQuadratic() )
4408 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4409 SMDS_MeshElement::iterator() );
4410 nodes.push_back( nodes[0] );
4412 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4414 if ( !surface.IsNull() )
4416 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4417 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4418 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4419 xyz = surface->Value( uv.X(), uv.Y() );
4422 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4424 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4425 // we have to move a medium node
4426 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4432 } // loop on face ids
4438 //=======================================================================
4439 //function : isReverse
4440 //purpose : Return true if normal of prevNodes is not co-directied with
4441 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4442 // iNotSame is where prevNodes and nextNodes are different.
4443 // If result is true then future volume orientation is OK
4444 //=======================================================================
4446 bool isReverse(const SMDS_MeshElement* face,
4447 const vector<const SMDS_MeshNode*>& prevNodes,
4448 const vector<const SMDS_MeshNode*>& nextNodes,
4452 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4453 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4454 gp_XYZ extrDir( pN - pP ), faceNorm;
4455 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4457 return faceNorm * extrDir < 0.0;
4460 //================================================================================
4462 * \brief Assure that theElemSets[0] holds elements, not nodes
4464 //================================================================================
4466 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4468 if ( !theElemSets[0].empty() &&
4469 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4471 std::swap( theElemSets[0], theElemSets[1] );
4473 else if ( !theElemSets[1].empty() &&
4474 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4476 std::swap( theElemSets[0], theElemSets[1] );
4481 //=======================================================================
4483 * \brief Create elements by sweeping an element
4484 * \param elem - element to sweep
4485 * \param newNodesItVec - nodes generated from each node of the element
4486 * \param newElems - generated elements
4487 * \param nbSteps - number of sweeping steps
4488 * \param srcElements - to append elem for each generated element
4490 //=======================================================================
4492 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4493 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4494 list<const SMDS_MeshElement*>& newElems,
4496 SMESH_SequenceOfElemPtr& srcElements)
4498 //MESSAGE("sweepElement " << nbSteps);
4499 SMESHDS_Mesh* aMesh = GetMeshDS();
4501 const int nbNodes = elem->NbNodes();
4502 const int nbCorners = elem->NbCornerNodes();
4503 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4504 polyhedron creation !!! */
4505 // Loop on elem nodes:
4506 // find new nodes and detect same nodes indices
4507 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4508 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4509 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4510 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4512 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4513 vector<int> sames(nbNodes);
4514 vector<bool> isSingleNode(nbNodes);
4516 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4517 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4518 const SMDS_MeshNode* node = nnIt->first;
4519 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4520 if ( listNewNodes.empty() )
4523 itNN [ iNode ] = listNewNodes.begin();
4524 prevNod[ iNode ] = node;
4525 nextNod[ iNode ] = listNewNodes.front();
4527 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4528 corner node of linear */
4529 if ( prevNod[ iNode ] != nextNod [ iNode ])
4530 nbDouble += !isSingleNode[iNode];
4532 if( iNode < nbCorners ) { // check corners only
4533 if ( prevNod[ iNode ] == nextNod [ iNode ])
4534 sames[nbSame++] = iNode;
4536 iNotSameNode = iNode;
4540 if ( nbSame == nbNodes || nbSame > 2) {
4541 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4545 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4547 // fix nodes order to have bottom normal external
4548 if ( baseType == SMDSEntity_Polygon )
4550 std::reverse( itNN.begin(), itNN.end() );
4551 std::reverse( prevNod.begin(), prevNod.end() );
4552 std::reverse( midlNod.begin(), midlNod.end() );
4553 std::reverse( nextNod.begin(), nextNod.end() );
4554 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4558 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4559 SMDS_MeshCell::applyInterlace( ind, itNN );
4560 SMDS_MeshCell::applyInterlace( ind, prevNod );
4561 SMDS_MeshCell::applyInterlace( ind, nextNod );
4562 SMDS_MeshCell::applyInterlace( ind, midlNod );
4563 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4566 sames[nbSame] = iNotSameNode;
4567 for ( int j = 0; j <= nbSame; ++j )
4568 for ( size_t i = 0; i < ind.size(); ++i )
4569 if ( ind[i] == sames[j] )
4574 iNotSameNode = sames[nbSame];
4578 else if ( elem->GetType() == SMDSAbs_Edge )
4580 // orient a new face same as adjacent one
4582 const SMDS_MeshElement* e;
4583 TIDSortedElemSet dummy;
4584 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4585 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4586 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4588 // there is an adjacent face, check order of nodes in it
4589 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4592 std::swap( itNN[0], itNN[1] );
4593 std::swap( prevNod[0], prevNod[1] );
4594 std::swap( nextNod[0], nextNod[1] );
4595 isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4597 sames[0] = 1 - sames[0];
4598 iNotSameNode = 1 - iNotSameNode;
4603 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4605 iSameNode = sames[ nbSame-1 ];
4606 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4607 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4608 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4611 if ( baseType == SMDSEntity_Polygon )
4613 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4614 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4616 else if ( baseType == SMDSEntity_Quad_Polygon )
4618 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4619 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4622 // make new elements
4623 for (int iStep = 0; iStep < nbSteps; iStep++ )
4626 for ( iNode = 0; iNode < nbNodes; iNode++ )
4628 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4629 nextNod[ iNode ] = *itNN[ iNode ]++;
4632 SMDS_MeshElement* aNewElem = 0;
4633 /*if(!elem->IsPoly())*/ {
4634 switch ( baseType ) {
4636 case SMDSEntity_Node: { // sweep NODE
4637 if ( nbSame == 0 ) {
4638 if ( isSingleNode[0] )
4639 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4641 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4647 case SMDSEntity_Edge: { // sweep EDGE
4648 if ( nbDouble == 0 )
4650 if ( nbSame == 0 ) // ---> quadrangle
4651 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4652 nextNod[ 1 ], nextNod[ 0 ] );
4653 else // ---> triangle
4654 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4655 nextNod[ iNotSameNode ] );
4657 else // ---> polygon
4659 vector<const SMDS_MeshNode*> poly_nodes;
4660 poly_nodes.push_back( prevNod[0] );
4661 poly_nodes.push_back( prevNod[1] );
4662 if ( prevNod[1] != nextNod[1] )
4664 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4665 poly_nodes.push_back( nextNod[1] );
4667 if ( prevNod[0] != nextNod[0] )
4669 poly_nodes.push_back( nextNod[0] );
4670 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4672 switch ( poly_nodes.size() ) {
4674 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4677 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4678 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4681 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4686 case SMDSEntity_Triangle: // TRIANGLE --->
4688 if ( nbDouble > 0 ) break;
4689 if ( nbSame == 0 ) // ---> pentahedron
4690 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4691 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4693 else if ( nbSame == 1 ) // ---> pyramid
4694 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4695 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4696 nextNod[ iSameNode ]);
4698 else // 2 same nodes: ---> tetrahedron
4699 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4700 nextNod[ iNotSameNode ]);
4703 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4707 if ( nbDouble+nbSame == 2 )
4709 if(nbSame==0) { // ---> quadratic quadrangle
4710 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4711 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4713 else { //(nbSame==1) // ---> quadratic triangle
4715 return; // medium node on axis
4717 else if(sames[0]==0)
4718 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4719 prevNod[2], midlNod[1], nextNod[2] );
4721 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4722 prevNod[2], nextNod[2], midlNod[0]);
4725 else if ( nbDouble == 3 )
4727 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4728 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4729 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4736 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4737 if ( nbDouble > 0 ) break;
4739 if ( nbSame == 0 ) // ---> hexahedron
4740 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4741 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4743 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4744 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4745 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4746 nextNod[ iSameNode ]);
4747 newElems.push_back( aNewElem );
4748 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4749 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4750 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4752 else if ( nbSame == 2 ) { // ---> pentahedron
4753 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4754 // iBeforeSame is same too
4755 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4756 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4757 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4759 // iAfterSame is same too
4760 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4761 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4762 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4766 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4767 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4768 if ( nbDouble+nbSame != 3 ) break;
4770 // ---> pentahedron with 15 nodes
4771 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4772 nextNod[0], nextNod[1], nextNod[2],
4773 prevNod[3], prevNod[4], prevNod[5],
4774 nextNod[3], nextNod[4], nextNod[5],
4775 midlNod[0], midlNod[1], midlNod[2]);
4777 else if(nbSame==1) {
4778 // ---> 2d order pyramid of 13 nodes
4779 int apex = iSameNode;
4780 int i0 = ( apex + 1 ) % nbCorners;
4781 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4785 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4786 nextNod[i0], nextNod[i1], prevNod[apex],
4787 prevNod[i01], midlNod[i0],
4788 nextNod[i01], midlNod[i1],
4789 prevNod[i1a], prevNod[i0a],
4790 nextNod[i0a], nextNod[i1a]);
4792 else if(nbSame==2) {
4793 // ---> 2d order tetrahedron of 10 nodes
4794 int n1 = iNotSameNode;
4795 int n2 = ( n1 + 1 ) % nbCorners;
4796 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4800 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4801 prevNod[n12], prevNod[n23], prevNod[n31],
4802 midlNod[n1], nextNod[n12], nextNod[n31]);
4806 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4808 if ( nbDouble != 4 ) break;
4809 // ---> hexahedron with 20 nodes
4810 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4811 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4812 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4813 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4814 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4816 else if(nbSame==1) {
4817 // ---> pyramid + pentahedron - can not be created since it is needed
4818 // additional middle node at the center of face
4819 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4822 else if( nbSame == 2 ) {
4823 if ( nbDouble != 2 ) break;
4824 // ---> 2d order Pentahedron with 15 nodes
4826 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4827 // iBeforeSame is same too
4834 // iAfterSame is same too
4844 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4845 prevNod[n4], prevNod[n5], nextNod[n5],
4846 prevNod[n12], midlNod[n2], nextNod[n12],
4847 prevNod[n45], midlNod[n5], nextNod[n45],
4848 prevNod[n14], prevNod[n25], nextNod[n25]);
4852 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4854 if( nbSame == 0 && nbDouble == 9 ) {
4855 // ---> tri-quadratic hexahedron with 27 nodes
4856 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4857 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4858 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4859 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4860 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4861 prevNod[8], // bottom center
4862 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4863 nextNod[8], // top center
4864 midlNod[8]);// elem center
4872 case SMDSEntity_Polygon: { // sweep POLYGON
4874 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4875 // ---> hexagonal prism
4876 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4877 prevNod[3], prevNod[4], prevNod[5],
4878 nextNod[0], nextNod[1], nextNod[2],
4879 nextNod[3], nextNod[4], nextNod[5]);
4883 case SMDSEntity_Ball:
4888 } // switch ( baseType )
4891 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4893 if ( baseType != SMDSEntity_Polygon )
4895 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4896 SMDS_MeshCell::applyInterlace( ind, prevNod );
4897 SMDS_MeshCell::applyInterlace( ind, nextNod );
4898 SMDS_MeshCell::applyInterlace( ind, midlNod );
4899 SMDS_MeshCell::applyInterlace( ind, itNN );
4900 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4901 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4903 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4904 vector<int> quantities (nbNodes + 2);
4905 polyedre_nodes.clear();
4909 for (int inode = 0; inode < nbNodes; inode++)
4910 polyedre_nodes.push_back( prevNod[inode] );
4911 quantities.push_back( nbNodes );
4914 polyedre_nodes.push_back( nextNod[0] );
4915 for (int inode = nbNodes; inode-1; --inode )
4916 polyedre_nodes.push_back( nextNod[inode-1] );
4917 quantities.push_back( nbNodes );
4925 const int iQuad = elem->IsQuadratic();
4926 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4928 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4929 int inextface = (iface+1+iQuad) % nbNodes;
4930 int imid = (iface+1) % nbNodes;
4931 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4932 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4933 polyedre_nodes.push_back( prevNod[iface] ); // 1
4934 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4936 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4937 polyedre_nodes.push_back( nextNod[iface] ); // 2
4939 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4940 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4942 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4943 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4945 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4946 if ( nbFaceNodes > 2 )
4947 quantities.push_back( nbFaceNodes );
4948 else // degenerated face
4949 polyedre_nodes.resize( prevNbNodes );
4951 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4953 } // try to create a polyherdal prism
4956 newElems.push_back( aNewElem );
4957 myLastCreatedElems.Append(aNewElem);
4958 srcElements.Append( elem );
4961 // set new prev nodes
4962 for ( iNode = 0; iNode < nbNodes; iNode++ )
4963 prevNod[ iNode ] = nextNod[ iNode ];
4968 //=======================================================================
4970 * \brief Create 1D and 2D elements around swept elements
4971 * \param mapNewNodes - source nodes and ones generated from them
4972 * \param newElemsMap - source elements and ones generated from them
4973 * \param elemNewNodesMap - nodes generated from each node of each element
4974 * \param elemSet - all swept elements
4975 * \param nbSteps - number of sweeping steps
4976 * \param srcElements - to append elem for each generated element
4978 //=======================================================================
4980 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4981 TTElemOfElemListMap & newElemsMap,
4982 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4983 TIDSortedElemSet& elemSet,
4985 SMESH_SequenceOfElemPtr& srcElements)
4987 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4988 SMESHDS_Mesh* aMesh = GetMeshDS();
4990 // Find nodes belonging to only one initial element - sweep them into edges.
4992 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4993 for ( ; nList != mapNewNodes.end(); nList++ )
4995 const SMDS_MeshNode* node =
4996 static_cast<const SMDS_MeshNode*>( nList->first );
4997 if ( newElemsMap.count( node ))
4998 continue; // node was extruded into edge
4999 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
5000 int nbInitElems = 0;
5001 const SMDS_MeshElement* el = 0;
5002 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
5003 while ( eIt->more() && nbInitElems < 2 ) {
5004 const SMDS_MeshElement* e = eIt->next();
5005 SMDSAbs_ElementType type = e->GetType();
5006 if ( type == SMDSAbs_Volume || type < highType ) continue;
5007 if ( type > highType ) {
5012 nbInitElems += elemSet.count(el);
5014 if ( nbInitElems < 2 ) {
5015 bool NotCreateEdge = el && el->IsMediumNode(node);
5016 if(!NotCreateEdge) {
5017 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5018 list<const SMDS_MeshElement*> newEdges;
5019 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5024 // Make a ceiling for each element ie an equal element of last new nodes.
5025 // Find free links of faces - make edges and sweep them into faces.
5027 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5029 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
5030 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5031 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5033 const SMDS_MeshElement* elem = itElem->first;
5034 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5036 if(itElem->second.size()==0) continue;
5038 const bool isQuadratic = elem->IsQuadratic();
5040 if ( elem->GetType() == SMDSAbs_Edge ) {
5041 // create a ceiling edge
5042 if ( !isQuadratic ) {
5043 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5044 vecNewNodes[ 1 ]->second.back())) {
5045 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5046 vecNewNodes[ 1 ]->second.back()));
5047 srcElements.Append( elem );
5051 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5052 vecNewNodes[ 1 ]->second.back(),
5053 vecNewNodes[ 2 ]->second.back())) {
5054 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5055 vecNewNodes[ 1 ]->second.back(),
5056 vecNewNodes[ 2 ]->second.back()));
5057 srcElements.Append( elem );
5061 if ( elem->GetType() != SMDSAbs_Face )
5064 bool hasFreeLinks = false;
5066 TIDSortedElemSet avoidSet;
5067 avoidSet.insert( elem );
5069 set<const SMDS_MeshNode*> aFaceLastNodes;
5070 int iNode, nbNodes = vecNewNodes.size();
5071 if ( !isQuadratic ) {
5072 // loop on the face nodes
5073 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5074 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5075 // look for free links of the face
5076 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5077 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5078 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5079 // check if a link n1-n2 is free
5080 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5081 hasFreeLinks = true;
5082 // make a new edge and a ceiling for a new edge
5083 const SMDS_MeshElement* edge;
5084 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5085 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5086 srcElements.Append( myLastCreatedElems.Last() );
5088 n1 = vecNewNodes[ iNode ]->second.back();
5089 n2 = vecNewNodes[ iNext ]->second.back();
5090 if ( !aMesh->FindEdge( n1, n2 )) {
5091 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5092 srcElements.Append( edge );
5097 else { // elem is quadratic face
5098 int nbn = nbNodes/2;
5099 for ( iNode = 0; iNode < nbn; iNode++ ) {
5100 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5101 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5102 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5103 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5104 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5105 // check if a link is free
5106 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5107 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5108 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5109 hasFreeLinks = true;
5110 // make an edge and a ceiling for a new edge
5112 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5113 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5114 srcElements.Append( elem );
5116 n1 = vecNewNodes[ iNode ]->second.back();
5117 n2 = vecNewNodes[ iNext ]->second.back();
5118 n3 = vecNewNodes[ iNode+nbn ]->second.back();
5119 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5120 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5121 srcElements.Append( elem );
5125 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5126 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5130 // sweep free links into faces
5132 if ( hasFreeLinks ) {
5133 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5134 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5136 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5137 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5138 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5139 initNodeSet.insert( vecNewNodes[ iNode ]->first );
5140 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5142 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
5143 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5144 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5146 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5147 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5148 std::advance( v, volNb );
5149 // find indices of free faces of a volume and their source edges
5150 list< int > freeInd;
5151 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5152 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5153 int iF, nbF = vTool.NbFaces();
5154 for ( iF = 0; iF < nbF; iF ++ ) {
5155 if (vTool.IsFreeFace( iF ) &&
5156 vTool.GetFaceNodes( iF, faceNodeSet ) &&
5157 initNodeSet != faceNodeSet) // except an initial face
5159 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5161 if ( faceNodeSet == initNodeSetNoCenter )
5163 freeInd.push_back( iF );
5164 // find source edge of a free face iF
5165 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5166 vector<const SMDS_MeshNode*>::iterator lastCommom;
5167 commonNodes.resize( nbNodes, 0 );
5168 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5169 initNodeSet.begin(), initNodeSet.end(),
5170 commonNodes.begin());
5171 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5172 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5174 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5176 if ( !srcEdges.back() )
5178 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5179 << iF << " of volume #" << vTool.ID() << endl;
5184 if ( freeInd.empty() )
5187 // create wall faces for all steps;
5188 // if such a face has been already created by sweep of edge,
5189 // assure that its orientation is OK
5190 for ( int iStep = 0; iStep < nbSteps; iStep++ )
5192 vTool.Set( *v, /*ignoreCentralNodes=*/false );
5193 vTool.SetExternalNormal();
5194 const int nextShift = vTool.IsForward() ? +1 : -1;
5195 list< int >::iterator ind = freeInd.begin();
5196 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5197 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5199 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5200 int nbn = vTool.NbFaceNodes( *ind );
5201 const SMDS_MeshElement * f = 0;
5202 if ( nbn == 3 ) ///// triangle
5204 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5206 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5208 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5210 nodes[ 1 + nextShift ] };
5212 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5214 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5218 else if ( nbn == 4 ) ///// quadrangle
5220 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5222 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5224 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5225 nodes[ 2 ], nodes[ 2+nextShift ] };
5227 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5229 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5230 newOrder[ 2 ], newOrder[ 3 ]));
5233 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5235 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5237 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5239 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5241 nodes[2 + 2*nextShift],
5242 nodes[3 - 2*nextShift],
5244 nodes[3 + 2*nextShift]};
5246 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5248 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5256 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5258 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5259 nodes[1], nodes[3], nodes[5], nodes[7] );
5261 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5263 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5264 nodes[4 - 2*nextShift],
5266 nodes[4 + 2*nextShift],
5268 nodes[5 - 2*nextShift],
5270 nodes[5 + 2*nextShift] };
5272 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5274 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5275 newOrder[ 2 ], newOrder[ 3 ],
5276 newOrder[ 4 ], newOrder[ 5 ],
5277 newOrder[ 6 ], newOrder[ 7 ]));
5280 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5282 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5283 SMDSAbs_Face, /*noMedium=*/false);
5285 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5287 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5288 nodes[4 - 2*nextShift],
5290 nodes[4 + 2*nextShift],
5292 nodes[5 - 2*nextShift],
5294 nodes[5 + 2*nextShift],
5297 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5299 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5300 newOrder[ 2 ], newOrder[ 3 ],
5301 newOrder[ 4 ], newOrder[ 5 ],
5302 newOrder[ 6 ], newOrder[ 7 ],
5306 else //////// polygon
5308 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5309 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5311 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5313 if ( !vTool.IsForward() )
5314 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5316 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5318 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5322 while ( srcElements.Length() < myLastCreatedElems.Length() )
5323 srcElements.Append( *srcEdge );
5325 } // loop on free faces
5327 // go to the next volume
5329 while ( iVol++ < nbVolumesByStep ) v++;
5332 } // loop on volumes of one step
5333 } // sweep free links into faces
5335 // Make a ceiling face with a normal external to a volume
5337 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5338 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5339 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5341 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5342 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5343 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5347 lastVol.SetExternalNormal();
5348 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5349 const int nbn = lastVol.NbFaceNodes( iF );
5350 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5351 if ( !hasFreeLinks ||
5352 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5354 const vector<int>& interlace =
5355 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5356 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5358 AddElement( nodeVec, anyFace.Init( elem ));
5360 while ( srcElements.Length() < myLastCreatedElems.Length() )
5361 srcElements.Append( elem );
5364 } // loop on swept elements
5367 //=======================================================================
5368 //function : RotationSweep
5370 //=======================================================================
5372 SMESH_MeshEditor::PGroupIDs
5373 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5374 const gp_Ax1& theAxis,
5375 const double theAngle,
5376 const int theNbSteps,
5377 const double theTol,
5378 const bool theMakeGroups,
5379 const bool theMakeWalls)
5381 myLastCreatedElems.Clear();
5382 myLastCreatedNodes.Clear();
5384 // source elements for each generated one
5385 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5387 MESSAGE( "RotationSweep()");
5389 aTrsf.SetRotation( theAxis, theAngle );
5391 aTrsf2.SetRotation( theAxis, theAngle/2. );
5393 gp_Lin aLine( theAxis );
5394 double aSqTol = theTol * theTol;
5396 SMESHDS_Mesh* aMesh = GetMeshDS();
5398 TNodeOfNodeListMap mapNewNodes;
5399 TElemOfVecOfNnlmiMap mapElemNewNodes;
5400 TTElemOfElemListMap newElemsMap;
5402 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5403 myMesh->NbFaces(ORDER_QUADRATIC) +
5404 myMesh->NbVolumes(ORDER_QUADRATIC) );
5405 // loop on theElemSets
5406 setElemsFirst( theElemSets );
5407 TIDSortedElemSet::iterator itElem;
5408 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5410 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5411 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5412 const SMDS_MeshElement* elem = *itElem;
5413 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5415 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5416 newNodesItVec.reserve( elem->NbNodes() );
5418 // loop on elem nodes
5419 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5420 while ( itN->more() )
5422 const SMDS_MeshNode* node = cast2Node( itN->next() );
5424 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5426 aXYZ.Coord( coord[0], coord[1], coord[2] );
5427 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5429 // check if a node has been already sweeped
5430 TNodeOfNodeListMapItr nIt =
5431 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5432 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5433 if ( listNewNodes.empty() )
5435 // check if we are to create medium nodes between corner ones
5436 bool needMediumNodes = false;
5437 if ( isQuadraticMesh )
5439 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5440 while (it->more() && !needMediumNodes )
5442 const SMDS_MeshElement* invElem = it->next();
5443 if ( invElem != elem && !theElems.count( invElem )) continue;
5444 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5445 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5446 needMediumNodes = true;
5451 const SMDS_MeshNode * newNode = node;
5452 for ( int i = 0; i < theNbSteps; i++ ) {
5454 if ( needMediumNodes ) // create a medium node
5456 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5457 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5458 myLastCreatedNodes.Append(newNode);
5459 srcNodes.Append( node );
5460 listNewNodes.push_back( newNode );
5461 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5464 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5466 // create a corner node
5467 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5468 myLastCreatedNodes.Append(newNode);
5469 srcNodes.Append( node );
5470 listNewNodes.push_back( newNode );
5473 listNewNodes.push_back( newNode );
5474 // if ( needMediumNodes )
5475 // listNewNodes.push_back( newNode );
5479 newNodesItVec.push_back( nIt );
5481 // make new elements
5482 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5487 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5489 PGroupIDs newGroupIDs;
5490 if ( theMakeGroups )
5491 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5496 //=======================================================================
5497 //function : ExtrusParam
5498 //purpose : standard construction
5499 //=======================================================================
5501 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5502 const int theNbSteps,
5504 const double theTolerance):
5506 myFlags( theFlags ),
5507 myTolerance( theTolerance ),
5508 myElemsToUse( NULL )
5510 mySteps = new TColStd_HSequenceOfReal;
5511 const double stepSize = theStep.Magnitude();
5512 for (int i=1; i<=theNbSteps; i++ )
5513 mySteps->Append( stepSize );
5515 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5516 ( theTolerance > 0 ))
5518 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5522 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5526 //=======================================================================
5527 //function : ExtrusParam
5528 //purpose : steps are given explicitly
5529 //=======================================================================
5531 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5532 Handle(TColStd_HSequenceOfReal) theSteps,
5534 const double theTolerance):
5536 mySteps( theSteps ),
5537 myFlags( theFlags ),
5538 myTolerance( theTolerance ),
5539 myElemsToUse( NULL )
5541 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5542 ( theTolerance > 0 ))
5544 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5548 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5552 //=======================================================================
5553 //function : ExtrusParam
5554 //purpose : for extrusion by normal
5555 //=======================================================================
5557 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5558 const int theNbSteps,
5562 mySteps( new TColStd_HSequenceOfReal ),
5563 myFlags( theFlags ),
5565 myElemsToUse( NULL )
5567 for (int i = 0; i < theNbSteps; i++ )
5568 mySteps->Append( theStepSize );
5572 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5576 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5580 //=======================================================================
5581 //function : ExtrusParam::SetElementsToUse
5582 //purpose : stores elements to use for extrusion by normal, depending on
5583 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5584 //=======================================================================
5586 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5588 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5591 //=======================================================================
5592 //function : ExtrusParam::beginStepIter
5593 //purpose : prepare iteration on steps
5594 //=======================================================================
5596 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5598 myWithMediumNodes = withMediumNodes;
5602 //=======================================================================
5603 //function : ExtrusParam::moreSteps
5604 //purpose : are there more steps?
5605 //=======================================================================
5607 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5609 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5611 //=======================================================================
5612 //function : ExtrusParam::nextStep
5613 //purpose : returns the next step
5614 //=======================================================================
5616 double SMESH_MeshEditor::ExtrusParam::nextStep()
5619 if ( !myCurSteps.empty() )
5621 res = myCurSteps.back();
5622 myCurSteps.pop_back();
5624 else if ( myNextStep <= mySteps->Length() )
5626 myCurSteps.push_back( mySteps->Value( myNextStep ));
5628 if ( myWithMediumNodes )
5630 myCurSteps.back() /= 2.;
5631 myCurSteps.push_back( myCurSteps.back() );
5638 //=======================================================================
5639 //function : ExtrusParam::makeNodesByDir
5640 //purpose : create nodes for standard extrusion
5641 //=======================================================================
5643 int SMESH_MeshEditor::ExtrusParam::
5644 makeNodesByDir( SMESHDS_Mesh* mesh,
5645 const SMDS_MeshNode* srcNode,
5646 std::list<const SMDS_MeshNode*> & newNodes,
5647 const bool makeMediumNodes)
5649 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5652 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5654 p += myDir.XYZ() * nextStep();
5655 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5656 newNodes.push_back( newNode );
5661 //=======================================================================
5662 //function : ExtrusParam::makeNodesByDirAndSew
5663 //purpose : create nodes for standard extrusion with sewing
5664 //=======================================================================
5666 int SMESH_MeshEditor::ExtrusParam::
5667 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5668 const SMDS_MeshNode* srcNode,
5669 std::list<const SMDS_MeshNode*> & newNodes,
5670 const bool makeMediumNodes)
5672 gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5675 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5677 P1 += myDir.XYZ() * nextStep();
5679 // try to search in sequence of existing nodes
5680 // if myNodes.Length()>0 we 'nave to use given sequence
5681 // else - use all nodes of mesh
5682 const SMDS_MeshNode * node = 0;
5683 if ( myNodes.Length() > 0 ) {
5685 for(i=1; i<=myNodes.Length(); i++) {
5686 gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5687 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5689 node = myNodes.Value(i);
5695 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5696 while(itn->more()) {
5697 SMESH_TNodeXYZ P2( itn->next() );
5698 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5707 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5709 newNodes.push_back( node );
5716 //=======================================================================
5717 //function : ExtrusParam::makeNodesByNormal2D
5718 //purpose : create nodes for extrusion using normals of faces
5719 //=======================================================================
5721 int SMESH_MeshEditor::ExtrusParam::
5722 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5723 const SMDS_MeshNode* srcNode,
5724 std::list<const SMDS_MeshNode*> & newNodes,
5725 const bool makeMediumNodes)
5727 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5729 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5731 // get normals to faces sharing srcNode
5732 vector< gp_XYZ > norms, baryCenters;
5733 gp_XYZ norm, avgNorm( 0,0,0 );
5734 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5735 while ( faceIt->more() )
5737 const SMDS_MeshElement* face = faceIt->next();
5738 if ( myElemsToUse && !myElemsToUse->count( face ))
5740 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5742 norms.push_back( norm );
5744 if ( !alongAvgNorm )
5748 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5749 bc += SMESH_TNodeXYZ( nIt->next() );
5750 baryCenters.push_back( bc / nbN );
5755 if ( norms.empty() ) return 0;
5757 double normSize = avgNorm.Modulus();
5758 if ( normSize < std::numeric_limits<double>::min() )
5761 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5764 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5767 avgNorm /= normSize;
5770 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5773 double stepSize = nextStep();
5775 if ( norms.size() > 1 )
5777 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5779 // translate plane of a face
5780 baryCenters[ iF ] += norms[ iF ] * stepSize;
5782 // find point of intersection of the face plane located at baryCenters[ iF ]
5783 // and avgNorm located at pNew
5784 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5785 double dot = ( norms[ iF ] * avgNorm );
5786 if ( dot < std::numeric_limits<double>::min() )
5787 dot = stepSize * 1e-3;
5788 double step = -( norms[ iF ] * pNew + d ) / dot;
5789 pNew += step * avgNorm;
5794 pNew += stepSize * avgNorm;
5798 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5799 newNodes.push_back( newNode );
5804 //=======================================================================
5805 //function : ExtrusParam::makeNodesByNormal1D
5806 //purpose : create nodes for extrusion using normals of edges
5807 //=======================================================================
5809 int SMESH_MeshEditor::ExtrusParam::
5810 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5811 const SMDS_MeshNode* srcNode,
5812 std::list<const SMDS_MeshNode*> & newNodes,
5813 const bool makeMediumNodes)
5815 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5819 //=======================================================================
5820 //function : ExtrusionSweep
5822 //=======================================================================
5824 SMESH_MeshEditor::PGroupIDs
5825 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5826 const gp_Vec& theStep,
5827 const int theNbSteps,
5828 TTElemOfElemListMap& newElemsMap,
5830 const double theTolerance)
5832 ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5833 return ExtrusionSweep( theElems, aParams, newElemsMap );
5837 //=======================================================================
5838 //function : ExtrusionSweep
5840 //=======================================================================
5842 SMESH_MeshEditor::PGroupIDs
5843 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5844 ExtrusParam& theParams,
5845 TTElemOfElemListMap& newElemsMap)
5847 myLastCreatedElems.Clear();
5848 myLastCreatedNodes.Clear();
5850 // source elements for each generated one
5851 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5853 SMESHDS_Mesh* aMesh = GetMeshDS();
5855 setElemsFirst( theElemSets );
5856 const int nbSteps = theParams.NbSteps();
5857 theParams.SetElementsToUse( theElemSets[0] );
5859 TNodeOfNodeListMap mapNewNodes;
5860 //TNodeOfNodeVecMap mapNewNodes;
5861 TElemOfVecOfNnlmiMap mapElemNewNodes;
5862 //TElemOfVecOfMapNodesMap mapElemNewNodes;
5864 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5865 myMesh->NbFaces(ORDER_QUADRATIC) +
5866 myMesh->NbVolumes(ORDER_QUADRATIC) );
5868 TIDSortedElemSet::iterator itElem;
5869 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5871 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5872 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5874 // check element type
5875 const SMDS_MeshElement* elem = *itElem;
5876 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5879 const size_t nbNodes = elem->NbNodes();
5880 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5881 newNodesItVec.reserve( nbNodes );
5883 // loop on elem nodes
5884 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5885 while ( itN->more() )
5887 // check if a node has been already sweeped
5888 const SMDS_MeshNode* node = cast2Node( itN->next() );
5889 TNodeOfNodeListMap::iterator nIt =
5890 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5891 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5892 if ( listNewNodes.empty() )
5896 // check if we are to create medium nodes between corner ones
5897 bool needMediumNodes = false;
5898 if ( isQuadraticMesh )
5900 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5901 while (it->more() && !needMediumNodes )
5903 const SMDS_MeshElement* invElem = it->next();
5904 if ( invElem != elem && !theElems.count( invElem )) continue;
5905 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5906 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5907 needMediumNodes = true;
5910 // create nodes for all steps
5911 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5913 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5914 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5916 myLastCreatedNodes.Append( *newNodesIt );
5917 srcNodes.Append( node );
5922 break; // newNodesItVec will be shorter than nbNodes
5925 newNodesItVec.push_back( nIt );
5927 // make new elements
5928 if ( newNodesItVec.size() == nbNodes )
5929 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5933 if ( theParams.ToMakeBoundary() ) {
5934 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5936 PGroupIDs newGroupIDs;
5937 if ( theParams.ToMakeGroups() )
5938 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5943 //=======================================================================
5944 //function : ExtrusionAlongTrack
5946 //=======================================================================
5947 SMESH_MeshEditor::Extrusion_Error
5948 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5949 SMESH_subMesh* theTrack,
5950 const SMDS_MeshNode* theN1,
5951 const bool theHasAngles,
5952 list<double>& theAngles,
5953 const bool theLinearVariation,
5954 const bool theHasRefPoint,
5955 const gp_Pnt& theRefPoint,
5956 const bool theMakeGroups)
5958 MESSAGE("ExtrusionAlongTrack");
5959 myLastCreatedElems.Clear();
5960 myLastCreatedNodes.Clear();
5963 std::list<double> aPrms;
5964 TIDSortedElemSet::iterator itElem;
5967 TopoDS_Edge aTrackEdge;
5968 TopoDS_Vertex aV1, aV2;
5970 SMDS_ElemIteratorPtr aItE;
5971 SMDS_NodeIteratorPtr aItN;
5972 SMDSAbs_ElementType aTypeE;
5974 TNodeOfNodeListMap mapNewNodes;
5977 aNbE = theElements[0].size() + theElements[1].size();
5980 return EXTR_NO_ELEMENTS;
5982 // 1.1 Track Pattern
5985 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5987 aItE = pSubMeshDS->GetElements();
5988 while ( aItE->more() ) {
5989 const SMDS_MeshElement* pE = aItE->next();
5990 aTypeE = pE->GetType();
5991 // Pattern must contain links only
5992 if ( aTypeE != SMDSAbs_Edge )
5993 return EXTR_PATH_NOT_EDGE;
5996 list<SMESH_MeshEditor_PathPoint> fullList;
5998 const TopoDS_Shape& aS = theTrack->GetSubShape();
5999 // Sub-shape for the Pattern must be an Edge or Wire
6000 if( aS.ShapeType() == TopAbs_EDGE ) {
6001 aTrackEdge = TopoDS::Edge( aS );
6002 // the Edge must not be degenerated
6003 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6004 return EXTR_BAD_PATH_SHAPE;
6005 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6006 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
6007 const SMDS_MeshNode* aN1 = aItN->next();
6008 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
6009 const SMDS_MeshNode* aN2 = aItN->next();
6010 // starting node must be aN1 or aN2
6011 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6012 return EXTR_BAD_STARTING_NODE;
6013 aItN = pSubMeshDS->GetNodes();
6014 while ( aItN->more() ) {
6015 const SMDS_MeshNode* pNode = aItN->next();
6016 const SMDS_EdgePosition* pEPos =
6017 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6018 double aT = pEPos->GetUParameter();
6019 aPrms.push_back( aT );
6021 //Extrusion_Error err =
6022 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6023 } else if( aS.ShapeType() == TopAbs_WIRE ) {
6024 list< SMESH_subMesh* > LSM;
6025 TopTools_SequenceOfShape Edges;
6026 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6027 while(itSM->more()) {
6028 SMESH_subMesh* SM = itSM->next();
6030 const TopoDS_Shape& aS = SM->GetSubShape();
6033 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6034 int startNid = theN1->GetID();
6035 TColStd_MapOfInteger UsedNums;
6037 int NbEdges = Edges.Length();
6039 for(; i<=NbEdges; i++) {
6041 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6042 for(; itLSM!=LSM.end(); itLSM++) {
6044 if(UsedNums.Contains(k)) continue;
6045 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6046 SMESH_subMesh* locTrack = *itLSM;
6047 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6048 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6049 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6050 const SMDS_MeshNode* aN1 = aItN->next();
6051 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6052 const SMDS_MeshNode* aN2 = aItN->next();
6053 // starting node must be aN1 or aN2
6054 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6055 // 2. Collect parameters on the track edge
6057 aItN = locMeshDS->GetNodes();
6058 while ( aItN->more() ) {
6059 const SMDS_MeshNode* pNode = aItN->next();
6060 const SMDS_EdgePosition* pEPos =
6061 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6062 double aT = pEPos->GetUParameter();
6063 aPrms.push_back( aT );
6065 list<SMESH_MeshEditor_PathPoint> LPP;
6066 //Extrusion_Error err =
6067 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6068 LLPPs.push_back(LPP);
6070 // update startN for search following egde
6071 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6072 else startNid = aN1->GetID();
6076 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6077 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6078 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6079 for(; itPP!=firstList.end(); itPP++) {
6080 fullList.push_back( *itPP );
6082 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6083 fullList.pop_back();
6085 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6086 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6087 itPP = currList.begin();
6088 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6089 gp_Dir D1 = PP1.Tangent();
6090 gp_Dir D2 = PP2.Tangent();
6091 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6092 (D1.Z()+D2.Z())/2 ) );
6093 PP1.SetTangent(Dnew);
6094 fullList.push_back(PP1);
6096 for(; itPP!=firstList.end(); itPP++) {
6097 fullList.push_back( *itPP );
6099 PP1 = fullList.back();
6100 fullList.pop_back();
6102 // if wire not closed
6103 fullList.push_back(PP1);
6107 return EXTR_BAD_PATH_SHAPE;
6110 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6111 theHasRefPoint, theRefPoint, theMakeGroups);
6115 //=======================================================================
6116 //function : ExtrusionAlongTrack
6118 //=======================================================================
6119 SMESH_MeshEditor::Extrusion_Error
6120 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6121 SMESH_Mesh* theTrack,
6122 const SMDS_MeshNode* theN1,
6123 const bool theHasAngles,
6124 list<double>& theAngles,
6125 const bool theLinearVariation,
6126 const bool theHasRefPoint,
6127 const gp_Pnt& theRefPoint,
6128 const bool theMakeGroups)
6130 myLastCreatedElems.Clear();
6131 myLastCreatedNodes.Clear();
6134 std::list<double> aPrms;
6135 TIDSortedElemSet::iterator itElem;
6138 TopoDS_Edge aTrackEdge;
6139 TopoDS_Vertex aV1, aV2;
6141 SMDS_ElemIteratorPtr aItE;
6142 SMDS_NodeIteratorPtr aItN;
6143 SMDSAbs_ElementType aTypeE;
6145 TNodeOfNodeListMap mapNewNodes;
6148 aNbE = theElements[0].size() + theElements[1].size();
6151 return EXTR_NO_ELEMENTS;
6153 // 1.1 Track Pattern
6156 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6158 aItE = pMeshDS->elementsIterator();
6159 while ( aItE->more() ) {
6160 const SMDS_MeshElement* pE = aItE->next();
6161 aTypeE = pE->GetType();
6162 // Pattern must contain links only
6163 if ( aTypeE != SMDSAbs_Edge )
6164 return EXTR_PATH_NOT_EDGE;
6167 list<SMESH_MeshEditor_PathPoint> fullList;
6169 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6171 if ( !theTrack->HasShapeToMesh() ) {
6172 //Mesh without shape
6173 const SMDS_MeshNode* currentNode = NULL;
6174 const SMDS_MeshNode* prevNode = theN1;
6175 std::vector<const SMDS_MeshNode*> aNodesList;
6176 aNodesList.push_back(theN1);
6177 int nbEdges = 0, conn=0;
6178 const SMDS_MeshElement* prevElem = NULL;
6179 const SMDS_MeshElement* currentElem = NULL;
6180 int totalNbEdges = theTrack->NbEdges();
6181 SMDS_ElemIteratorPtr nIt;
6184 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6185 return EXTR_BAD_STARTING_NODE;
6188 conn = nbEdgeConnectivity(theN1);
6190 return EXTR_PATH_NOT_EDGE;
6192 aItE = theN1->GetInverseElementIterator();
6193 prevElem = aItE->next();
6194 currentElem = prevElem;
6196 if(totalNbEdges == 1 ) {
6197 nIt = currentElem->nodesIterator();
6198 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6199 if(currentNode == prevNode)
6200 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6201 aNodesList.push_back(currentNode);
6203 nIt = currentElem->nodesIterator();
6204 while( nIt->more() ) {
6205 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6206 if(currentNode == prevNode)
6207 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6208 aNodesList.push_back(currentNode);
6210 //case of the closed mesh
6211 if(currentNode == theN1) {
6216 conn = nbEdgeConnectivity(currentNode);
6218 return EXTR_PATH_NOT_EDGE;
6219 }else if( conn == 1 && nbEdges > 0 ) {
6224 prevNode = currentNode;
6225 aItE = currentNode->GetInverseElementIterator();
6226 currentElem = aItE->next();
6227 if( currentElem == prevElem)
6228 currentElem = aItE->next();
6229 nIt = currentElem->nodesIterator();
6230 prevElem = currentElem;
6236 if(nbEdges != totalNbEdges)
6237 return EXTR_PATH_NOT_EDGE;
6239 TopTools_SequenceOfShape Edges;
6240 double x1,x2,y1,y2,z1,z2;
6241 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6242 int startNid = theN1->GetID();
6243 for(int i = 1; i < aNodesList.size(); i++) {
6244 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
6245 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
6246 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
6247 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
6248 list<SMESH_MeshEditor_PathPoint> LPP;
6250 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6251 LLPPs.push_back(LPP);
6252 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
6253 else startNid = aNodesList[i-1]->GetID();
6257 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6258 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6259 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6260 for(; itPP!=firstList.end(); itPP++) {
6261 fullList.push_back( *itPP );
6264 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6265 SMESH_MeshEditor_PathPoint PP2;
6266 fullList.pop_back();
6268 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6269 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6270 itPP = currList.begin();
6271 PP2 = currList.front();
6272 gp_Dir D1 = PP1.Tangent();
6273 gp_Dir D2 = PP2.Tangent();
6274 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6275 (D1.Z()+D2.Z())/2 ) );
6276 PP1.SetTangent(Dnew);
6277 fullList.push_back(PP1);
6279 for(; itPP!=currList.end(); itPP++) {
6280 fullList.push_back( *itPP );
6282 PP1 = fullList.back();
6283 fullList.pop_back();
6285 fullList.push_back(PP1);
6287 } // Sub-shape for the Pattern must be an Edge or Wire
6288 else if( aS.ShapeType() == TopAbs_EDGE ) {
6289 aTrackEdge = TopoDS::Edge( aS );
6290 // the Edge must not be degenerated
6291 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6292 return EXTR_BAD_PATH_SHAPE;
6293 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6294 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6295 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6296 // starting node must be aN1 or aN2
6297 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6298 return EXTR_BAD_STARTING_NODE;
6299 aItN = pMeshDS->nodesIterator();
6300 while ( aItN->more() ) {
6301 const SMDS_MeshNode* pNode = aItN->next();
6302 if( pNode==aN1 || pNode==aN2 ) continue;
6303 const SMDS_EdgePosition* pEPos =
6304 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6305 double aT = pEPos->GetUParameter();
6306 aPrms.push_back( aT );
6308 //Extrusion_Error err =
6309 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6311 else if( aS.ShapeType() == TopAbs_WIRE ) {
6312 list< SMESH_subMesh* > LSM;
6313 TopTools_SequenceOfShape Edges;
6314 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6315 for(; eExp.More(); eExp.Next()) {
6316 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6317 if( SMESH_Algo::isDegenerated(E) ) continue;
6318 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6324 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6325 TopoDS_Vertex aVprev;
6326 TColStd_MapOfInteger UsedNums;
6327 int NbEdges = Edges.Length();
6329 for(; i<=NbEdges; i++) {
6331 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6332 for(; itLSM!=LSM.end(); itLSM++) {
6334 if(UsedNums.Contains(k)) continue;
6335 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6336 SMESH_subMesh* locTrack = *itLSM;
6337 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6338 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6339 bool aN1isOK = false, aN2isOK = false;
6340 if ( aVprev.IsNull() ) {
6341 // if previous vertex is not yet defined, it means that we in the beginning of wire
6342 // and we have to find initial vertex corresponding to starting node theN1
6343 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6344 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6345 // starting node must be aN1 or aN2
6346 aN1isOK = ( aN1 && aN1 == theN1 );
6347 aN2isOK = ( aN2 && aN2 == theN1 );
6350 // we have specified ending vertex of the previous edge on the previous iteration
6351 // and we have just to check that it corresponds to any vertex in current segment
6352 aN1isOK = aVprev.IsSame( aV1 );
6353 aN2isOK = aVprev.IsSame( aV2 );
6355 if ( !aN1isOK && !aN2isOK ) continue;
6356 // 2. Collect parameters on the track edge
6358 aItN = locMeshDS->GetNodes();
6359 while ( aItN->more() ) {
6360 const SMDS_MeshNode* pNode = aItN->next();
6361 const SMDS_EdgePosition* pEPos =
6362 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6363 double aT = pEPos->GetUParameter();
6364 aPrms.push_back( aT );
6366 list<SMESH_MeshEditor_PathPoint> LPP;
6367 //Extrusion_Error err =
6368 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6369 LLPPs.push_back(LPP);
6371 // update startN for search following egde
6372 if ( aN1isOK ) aVprev = aV2;
6377 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6378 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6379 fullList.splice( fullList.end(), firstList );
6381 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6382 fullList.pop_back();
6384 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6385 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6386 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6387 gp_Dir D1 = PP1.Tangent();
6388 gp_Dir D2 = PP2.Tangent();
6389 gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
6390 PP1.SetTangent(Dnew);
6391 fullList.push_back(PP1);
6392 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6393 PP1 = fullList.back();
6394 fullList.pop_back();
6396 // if wire not closed
6397 fullList.push_back(PP1);
6401 return EXTR_BAD_PATH_SHAPE;
6404 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6405 theHasRefPoint, theRefPoint, theMakeGroups);
6409 //=======================================================================
6410 //function : MakeEdgePathPoints
6411 //purpose : auxilary for ExtrusionAlongTrack
6412 //=======================================================================
6413 SMESH_MeshEditor::Extrusion_Error
6414 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
6415 const TopoDS_Edge& aTrackEdge,
6417 list<SMESH_MeshEditor_PathPoint>& LPP)
6419 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6421 aTolVec2=aTolVec*aTolVec;
6423 TopoDS_Vertex aV1, aV2;
6424 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6425 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6426 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6427 // 2. Collect parameters on the track edge
6428 aPrms.push_front( aT1 );
6429 aPrms.push_back( aT2 );
6432 if( FirstIsStart ) {
6443 SMESH_MeshEditor_PathPoint aPP;
6444 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6445 std::list<double>::iterator aItD = aPrms.begin();
6446 for(; aItD != aPrms.end(); ++aItD) {
6450 aC3D->D1( aT, aP3D, aVec );
6451 aL2 = aVec.SquareMagnitude();
6452 if ( aL2 < aTolVec2 )
6453 return EXTR_CANT_GET_TANGENT;
6454 gp_Dir aTgt( aVec );
6456 aPP.SetTangent( aTgt );
6457 aPP.SetParameter( aT );
6464 //=======================================================================
6465 //function : MakeExtrElements
6466 //purpose : auxilary for ExtrusionAlongTrack
6467 //=======================================================================
6468 SMESH_MeshEditor::Extrusion_Error
6469 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet theElemSets[2],
6470 list<SMESH_MeshEditor_PathPoint>& fullList,
6471 const bool theHasAngles,
6472 list<double>& theAngles,
6473 const bool theLinearVariation,
6474 const bool theHasRefPoint,
6475 const gp_Pnt& theRefPoint,
6476 const bool theMakeGroups)
6478 const int aNbTP = fullList.size();
6480 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6481 LinearAngleVariation(aNbTP-1, theAngles);
6482 // fill vector of path points with angles
6483 vector<SMESH_MeshEditor_PathPoint> aPPs;
6484 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6485 list<double>::iterator itAngles = theAngles.begin();
6486 aPPs.push_back( *itPP++ );
6487 for( ; itPP != fullList.end(); itPP++) {
6488 aPPs.push_back( *itPP );
6489 if ( theHasAngles && itAngles != theAngles.end() )
6490 aPPs.back().SetAngle( *itAngles++ );
6493 TNodeOfNodeListMap mapNewNodes;
6494 TElemOfVecOfNnlmiMap mapElemNewNodes;
6495 TTElemOfElemListMap newElemsMap;
6496 TIDSortedElemSet::iterator itElem;
6497 // source elements for each generated one
6498 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6500 // 3. Center of rotation aV0
6501 gp_Pnt aV0 = theRefPoint;
6502 if ( !theHasRefPoint )
6504 gp_XYZ aGC( 0.,0.,0. );
6505 TIDSortedElemSet newNodes;
6507 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6509 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6510 itElem = theElements.begin();
6511 for ( ; itElem != theElements.end(); itElem++ ) {
6512 const SMDS_MeshElement* elem = *itElem;
6514 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6515 while ( itN->more() ) {
6516 const SMDS_MeshElement* node = itN->next();
6517 if ( newNodes.insert( node ).second )
6518 aGC += SMESH_TNodeXYZ( node );
6522 aGC /= newNodes.size();
6524 } // if (!theHasRefPoint) {
6526 // 4. Processing the elements
6527 SMESHDS_Mesh* aMesh = GetMeshDS();
6529 setElemsFirst( theElemSets );
6530 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6532 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6533 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
6534 // check element type
6535 const SMDS_MeshElement* elem = *itElem;
6538 // SMDSAbs_ElementType aTypeE = elem->GetType();
6539 // if ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge )
6542 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6543 newNodesItVec.reserve( elem->NbNodes() );
6545 // loop on elem nodes
6547 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6548 while ( itN->more() )
6551 // check if a node has been already processed
6552 const SMDS_MeshNode* node =
6553 static_cast<const SMDS_MeshNode*>( itN->next() );
6554 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
6555 if ( nIt == mapNewNodes.end() ) {
6556 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6557 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6560 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6561 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6562 gp_Ax1 anAx1, anAxT1T0;
6563 gp_Dir aDT1x, aDT0x, aDT1T0;
6568 aPN0 = SMESH_TNodeXYZ( node );
6570 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6572 aDT0x= aPP0.Tangent();
6573 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
6575 for ( int j = 1; j < aNbTP; ++j ) {
6576 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6578 aDT1x = aPP1.Tangent();
6579 aAngle1x = aPP1.Angle();
6581 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6583 gp_Vec aV01x( aP0x, aP1x );
6584 aTrsf.SetTranslation( aV01x );
6587 aV1x = aV0x.Transformed( aTrsf );
6588 aPN1 = aPN0.Transformed( aTrsf );
6590 // rotation 1 [ T1,T0 ]
6591 aAngleT1T0=-aDT1x.Angle( aDT0x );
6592 if (fabs(aAngleT1T0) > aTolAng) {
6594 anAxT1T0.SetLocation( aV1x );
6595 anAxT1T0.SetDirection( aDT1T0 );
6596 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6598 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6602 if ( theHasAngles ) {
6603 anAx1.SetLocation( aV1x );
6604 anAx1.SetDirection( aDT1x );
6605 aTrsfRot.SetRotation( anAx1, aAngle1x );
6607 aPN1 = aPN1.Transformed( aTrsfRot );
6611 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
6612 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6613 // create additional node
6614 double x = ( aPN1.X() + aPN0.X() )/2.;
6615 double y = ( aPN1.Y() + aPN0.Y() )/2.;
6616 double z = ( aPN1.Z() + aPN0.Z() )/2.;
6617 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6618 myLastCreatedNodes.Append(newNode);
6619 srcNodes.Append( node );
6620 listNewNodes.push_back( newNode );
6622 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6623 myLastCreatedNodes.Append(newNode);
6624 srcNodes.Append( node );
6625 listNewNodes.push_back( newNode );
6635 // if current elem is quadratic and current node is not medium
6636 // we have to check - may be it is needed to insert additional nodes
6637 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6638 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6639 if(listNewNodes.size()==aNbTP-1) {
6640 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6641 gp_XYZ P(node->X(), node->Y(), node->Z());
6642 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6644 for(i=0; i<aNbTP-1; i++) {
6645 const SMDS_MeshNode* N = *it;
6646 double x = ( N->X() + P.X() )/2.;
6647 double y = ( N->Y() + P.Y() )/2.;
6648 double z = ( N->Z() + P.Z() )/2.;
6649 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6650 srcNodes.Append( node );
6651 myLastCreatedNodes.Append(newN);
6654 P = gp_XYZ(N->X(),N->Y(),N->Z());
6656 listNewNodes.clear();
6657 for(i=0; i<2*(aNbTP-1); i++) {
6658 listNewNodes.push_back(aNodes[i]);
6664 newNodesItVec.push_back( nIt );
6666 // make new elements
6667 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6668 // newNodesItVec[0]->second.size(), myLastCreatedElems );
6669 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6673 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6675 if ( theMakeGroups )
6676 generateGroups( srcNodes, srcElems, "extruded");
6682 //=======================================================================
6683 //function : LinearAngleVariation
6684 //purpose : auxilary for ExtrusionAlongTrack
6685 //=======================================================================
6686 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6687 list<double>& Angles)
6689 int nbAngles = Angles.size();
6690 if( nbSteps > nbAngles ) {
6691 vector<double> theAngles(nbAngles);
6692 list<double>::iterator it = Angles.begin();
6694 for(; it!=Angles.end(); it++) {
6696 theAngles[i] = (*it);
6699 double rAn2St = double( nbAngles ) / double( nbSteps );
6700 double angPrev = 0, angle;
6701 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6702 double angCur = rAn2St * ( iSt+1 );
6703 double angCurFloor = floor( angCur );
6704 double angPrevFloor = floor( angPrev );
6705 if ( angPrevFloor == angCurFloor )
6706 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6708 int iP = int( angPrevFloor );
6709 double angPrevCeil = ceil(angPrev);
6710 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6712 int iC = int( angCurFloor );
6713 if ( iC < nbAngles )
6714 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6716 iP = int( angPrevCeil );
6718 angle += theAngles[ iC ];
6720 res.push_back(angle);
6725 for(; it!=res.end(); it++)
6726 Angles.push_back( *it );
6731 //================================================================================
6733 * \brief Move or copy theElements applying theTrsf to their nodes
6734 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6735 * \param theTrsf - transformation to apply
6736 * \param theCopy - if true, create translated copies of theElems
6737 * \param theMakeGroups - if true and theCopy, create translated groups
6738 * \param theTargetMesh - mesh to copy translated elements into
6739 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6741 //================================================================================
6743 SMESH_MeshEditor::PGroupIDs
6744 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6745 const gp_Trsf& theTrsf,
6747 const bool theMakeGroups,
6748 SMESH_Mesh* theTargetMesh)
6750 myLastCreatedElems.Clear();
6751 myLastCreatedNodes.Clear();
6753 bool needReverse = false;
6754 string groupPostfix;
6755 switch ( theTrsf.Form() ) {
6757 MESSAGE("gp_PntMirror");
6759 groupPostfix = "mirrored";
6762 MESSAGE("gp_Ax1Mirror");
6763 groupPostfix = "mirrored";
6766 MESSAGE("gp_Ax2Mirror");
6768 groupPostfix = "mirrored";
6771 MESSAGE("gp_Rotation");
6772 groupPostfix = "rotated";
6774 case gp_Translation:
6775 MESSAGE("gp_Translation");
6776 groupPostfix = "translated";
6779 MESSAGE("gp_Scale");
6780 groupPostfix = "scaled";
6782 case gp_CompoundTrsf: // different scale by axis
6783 MESSAGE("gp_CompoundTrsf");
6784 groupPostfix = "scaled";
6788 needReverse = false;
6789 groupPostfix = "transformed";
6792 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6793 SMESHDS_Mesh* aMesh = GetMeshDS();
6795 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6796 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6797 SMESH_MeshEditor::ElemFeatures elemType;
6799 // map old node to new one
6800 TNodeNodeMap nodeMap;
6802 // elements sharing moved nodes; those of them which have all
6803 // nodes mirrored but are not in theElems are to be reversed
6804 TIDSortedElemSet inverseElemSet;
6806 // source elements for each generated one
6807 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6809 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6810 TIDSortedElemSet orphanNode;
6812 if ( theElems.empty() ) // transform the whole mesh
6815 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6816 while ( eIt->more() ) theElems.insert( eIt->next() );
6818 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6819 while ( nIt->more() )
6821 const SMDS_MeshNode* node = nIt->next();
6822 if ( node->NbInverseElements() == 0)
6823 orphanNode.insert( node );
6827 // loop on elements to transform nodes : first orphan nodes then elems
6828 TIDSortedElemSet::iterator itElem;
6829 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6830 for (int i=0; i<2; i++)
6831 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6833 const SMDS_MeshElement* elem = *itElem;
6837 // loop on elem nodes
6839 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6840 while ( itN->more() )
6842 const SMDS_MeshNode* node = cast2Node( itN->next() );
6843 // check if a node has been already transformed
6844 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6845 nodeMap.insert( make_pair ( node, node ));
6846 if ( !n2n_isnew.second )
6849 node->GetXYZ( coord );
6850 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6851 if ( theTargetMesh ) {
6852 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6853 n2n_isnew.first->second = newNode;
6854 myLastCreatedNodes.Append(newNode);
6855 srcNodes.Append( node );
6857 else if ( theCopy ) {
6858 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6859 n2n_isnew.first->second = newNode;
6860 myLastCreatedNodes.Append(newNode);
6861 srcNodes.Append( node );
6864 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6865 // node position on shape becomes invalid
6866 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6867 ( SMDS_SpacePosition::originSpacePosition() );
6870 // keep inverse elements
6871 if ( !theCopy && !theTargetMesh && needReverse ) {
6872 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6873 while ( invElemIt->more() ) {
6874 const SMDS_MeshElement* iel = invElemIt->next();
6875 inverseElemSet.insert( iel );
6879 } // loop on elems in { &orphanNode, &theElems };
6881 // either create new elements or reverse mirrored ones
6882 if ( !theCopy && !needReverse && !theTargetMesh )
6885 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6887 // Replicate or reverse elements
6889 std::vector<int> iForw;
6890 vector<const SMDS_MeshNode*> nodes;
6891 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6893 const SMDS_MeshElement* elem = *itElem;
6894 if ( !elem ) continue;
6896 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6897 int nbNodes = elem->NbNodes();
6898 if ( geomType == SMDSGeom_NONE ) continue; // node
6900 nodes.resize( nbNodes );
6902 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6904 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6908 bool allTransformed = true;
6909 int nbFaces = aPolyedre->NbFaces();
6910 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6912 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6913 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6915 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6916 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6917 if ( nodeMapIt == nodeMap.end() )
6918 allTransformed = false; // not all nodes transformed
6920 nodes.push_back((*nodeMapIt).second);
6922 if ( needReverse && allTransformed )
6923 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6925 if ( !allTransformed )
6926 continue; // not all nodes transformed
6928 else // ----------------------- the rest element types
6930 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6931 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6932 const vector<int>& i = needReverse ? iRev : iForw;
6934 // find transformed nodes
6936 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6937 while ( itN->more() ) {
6938 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6939 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6940 if ( nodeMapIt == nodeMap.end() )
6941 break; // not all nodes transformed
6942 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6944 if ( iNode != nbNodes )
6945 continue; // not all nodes transformed
6949 // copy in this or a new mesh
6950 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6951 srcElems.Append( elem );
6954 // reverse element as it was reversed by transformation
6956 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6959 } // loop on elements
6961 if ( editor && editor != this )
6962 myLastCreatedElems = editor->myLastCreatedElems;
6964 PGroupIDs newGroupIDs;
6966 if ( ( theMakeGroups && theCopy ) ||
6967 ( theMakeGroups && theTargetMesh ) )
6968 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6973 //=======================================================================
6975 * \brief Create groups of elements made during transformation
6976 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6977 * \param elemGens - elements making corresponding myLastCreatedElems
6978 * \param postfix - to append to names of new groups
6979 * \param targetMesh - mesh to create groups in
6980 * \param topPresent - is there "top" elements that are created by sweeping
6982 //=======================================================================
6984 SMESH_MeshEditor::PGroupIDs
6985 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6986 const SMESH_SequenceOfElemPtr& elemGens,
6987 const std::string& postfix,
6988 SMESH_Mesh* targetMesh,
6989 const bool topPresent)
6991 PGroupIDs newGroupIDs( new list<int> );
6992 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6994 // Sort existing groups by types and collect their names
6996 // containers to store an old group and generated new ones;
6997 // 1st new group is for result elems of different type than a source one;
6998 // 2nd new group is for same type result elems ("top" group at extrusion)
7000 using boost::make_tuple;
7001 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
7002 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
7003 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7005 set< string > groupNames;
7007 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7008 if ( !groupIt->more() ) return newGroupIDs;
7010 int newGroupID = mesh->GetGroupIds().back()+1;
7011 while ( groupIt->more() )
7013 SMESH_Group * group = groupIt->next();
7014 if ( !group ) continue;
7015 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7016 if ( !groupDS || groupDS->IsEmpty() ) continue;
7017 groupNames.insert ( group->GetName() );
7018 groupDS->SetStoreName( group->GetName() );
7019 const SMDSAbs_ElementType type = groupDS->GetType();
7020 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7021 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7022 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7023 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7026 // Loop on nodes and elements to add them in new groups
7028 vector< const SMDS_MeshElement* > resultElems;
7029 for ( int isNodes = 0; isNodes < 2; ++isNodes )
7031 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
7032 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7033 if ( gens.Length() != elems.Length() )
7034 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7036 // loop on created elements
7037 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7039 const SMDS_MeshElement* sourceElem = gens( iElem );
7040 if ( !sourceElem ) {
7041 MESSAGE("generateGroups(): NULL source element");
7044 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7045 if ( groupsOldNew.empty() ) { // no groups of this type at all
7046 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7047 ++iElem; // skip all elements made by sourceElem
7050 // collect all elements made by the iElem-th sourceElem
7051 resultElems.clear();
7052 if ( const SMDS_MeshElement* resElem = elems( iElem ))
7053 if ( resElem != sourceElem )
7054 resultElems.push_back( resElem );
7055 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7056 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7057 if ( resElem != sourceElem )
7058 resultElems.push_back( resElem );
7060 const SMDS_MeshElement* topElem = 0;
7061 if ( isNodes ) // there must be a top element
7063 topElem = resultElems.back();
7064 resultElems.pop_back();
7068 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7069 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7070 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7072 topElem = *resElemIt;
7073 *resElemIt = 0; // erase *resElemIt
7077 // add resultElems to groups originted from ones the sourceElem belongs to
7078 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7079 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7081 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7082 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7084 // fill in a new group
7085 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7086 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7087 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7089 newGroup.Add( *resElemIt );
7091 // fill a "top" group
7094 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7095 newTopGroup.Add( topElem );
7099 } // loop on created elements
7100 }// loop on nodes and elements
7102 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7104 list<int> topGrouIds;
7105 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7107 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
7108 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7109 orderedOldNewGroups[i]->get<2>() };
7110 for ( int is2nd = 0; is2nd < 2; ++is2nd )
7112 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7113 if ( newGroupDS->IsEmpty() )
7115 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7120 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7123 const bool isTop = ( topPresent &&
7124 newGroupDS->GetType() == oldGroupDS->GetType() &&
7127 string name = oldGroupDS->GetStoreName();
7128 { // remove trailing whitespaces (issue 22599)
7129 size_t size = name.size();
7130 while ( size > 1 && isspace( name[ size-1 ]))
7132 if ( size != name.size() )
7134 name.resize( size );
7135 oldGroupDS->SetStoreName( name.c_str() );
7138 if ( !targetMesh ) {
7139 string suffix = ( isTop ? "top": postfix.c_str() );
7143 while ( !groupNames.insert( name ).second ) // name exists
7144 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7149 newGroupDS->SetStoreName( name.c_str() );
7151 // make a SMESH_Groups
7152 mesh->AddGroup( newGroupDS );
7154 topGrouIds.push_back( newGroupDS->GetID() );
7156 newGroupIDs->push_back( newGroupDS->GetID() );
7160 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7165 //================================================================================
7167 * * \brief Return list of group of nodes close to each other within theTolerance
7168 * * Search among theNodes or in the whole mesh if theNodes is empty using
7169 * * an Octree algorithm
7170 * \param [in,out] theNodes - the nodes to treat
7171 * \param [in] theTolerance - the tolerance
7172 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7173 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7174 * corner and medium nodes in separate groups
7176 //================================================================================
7178 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7179 const double theTolerance,
7180 TListOfListOfNodes & theGroupsOfNodes,
7181 bool theSeparateCornersAndMedium)
7183 myLastCreatedElems.Clear();
7184 myLastCreatedNodes.Clear();
7186 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7187 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7188 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7189 theSeparateCornersAndMedium = false;
7191 TIDSortedNodeSet& corners = theNodes;
7192 TIDSortedNodeSet medium;
7194 if ( theNodes.empty() ) // get all nodes in the mesh
7196 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7197 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7198 if ( theSeparateCornersAndMedium )
7199 while ( nIt->more() )
7201 const SMDS_MeshNode* n = nIt->next();
7202 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7203 nodeSet->insert( nodeSet->end(), n );
7206 while ( nIt->more() )
7207 theNodes.insert( theNodes.end(),nIt->next() );
7209 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7211 TIDSortedNodeSet::iterator nIt = corners.begin();
7212 while ( nIt != corners.end() )
7213 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7215 medium.insert( medium.end(), *nIt );
7216 corners.erase( nIt++ );
7224 if ( !corners.empty() )
7225 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7226 if ( !medium.empty() )
7227 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7230 //=======================================================================
7231 //function : SimplifyFace
7232 //purpose : split a chain of nodes into several closed chains
7233 //=======================================================================
7235 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7236 vector<const SMDS_MeshNode *>& poly_nodes,
7237 vector<int>& quantities) const
7239 int nbNodes = faceNodes.size();
7244 set<const SMDS_MeshNode*> nodeSet;
7246 // get simple seq of nodes
7247 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7248 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7249 int iSimple = 0, nbUnique = 0;
7251 simpleNodes[iSimple++] = faceNodes[0];
7253 for (int iCur = 1; iCur < nbNodes; iCur++) {
7254 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7255 simpleNodes[iSimple++] = faceNodes[iCur];
7256 if (nodeSet.insert( faceNodes[iCur] ).second)
7260 int nbSimple = iSimple;
7261 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7271 bool foundLoop = (nbSimple > nbUnique);
7274 set<const SMDS_MeshNode*> loopSet;
7275 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7276 const SMDS_MeshNode* n = simpleNodes[iSimple];
7277 if (!loopSet.insert( n ).second) {
7281 int iC = 0, curLast = iSimple;
7282 for (; iC < curLast; iC++) {
7283 if (simpleNodes[iC] == n) break;
7285 int loopLen = curLast - iC;
7287 // create sub-element
7289 quantities.push_back(loopLen);
7290 for (; iC < curLast; iC++) {
7291 poly_nodes.push_back(simpleNodes[iC]);
7294 // shift the rest nodes (place from the first loop position)
7295 for (iC = curLast + 1; iC < nbSimple; iC++) {
7296 simpleNodes[iC - loopLen] = simpleNodes[iC];
7298 nbSimple -= loopLen;
7301 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7302 } // while (foundLoop)
7306 quantities.push_back(iSimple);
7307 for (int i = 0; i < iSimple; i++)
7308 poly_nodes.push_back(simpleNodes[i]);
7314 //=======================================================================
7315 //function : MergeNodes
7316 //purpose : In each group, the cdr of nodes are substituted by the first one
7318 //=======================================================================
7320 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7322 MESSAGE("MergeNodes");
7323 myLastCreatedElems.Clear();
7324 myLastCreatedNodes.Clear();
7326 SMESHDS_Mesh* aMesh = GetMeshDS();
7328 TNodeNodeMap nodeNodeMap; // node to replace - new node
7329 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7330 list< int > rmElemIds, rmNodeIds;
7332 // Fill nodeNodeMap and elems
7334 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7335 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7337 list<const SMDS_MeshNode*>& nodes = *grIt;
7338 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7339 const SMDS_MeshNode* nToKeep = *nIt;
7340 for ( ++nIt; nIt != nodes.end(); nIt++ )
7342 const SMDS_MeshNode* nToRemove = *nIt;
7343 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7344 if ( nToRemove != nToKeep )
7346 rmNodeIds.push_back( nToRemove->GetID() );
7347 AddToSameGroups( nToKeep, nToRemove, aMesh );
7348 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7349 // after MergeNodes() w/o creating node in place of merged ones.
7350 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7351 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7352 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7353 sm->SetIsAlwaysComputed( true );
7355 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7356 while ( invElemIt->more() ) {
7357 const SMDS_MeshElement* elem = invElemIt->next();
7362 // Change element nodes or remove an element
7364 set<const SMDS_MeshNode*> nodeSet;
7365 vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7367 ElemFeatures elemType;
7369 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7370 for ( ; eIt != elems.end(); eIt++ )
7372 const SMDS_MeshElement* elem = *eIt;
7373 int nbNodes = elem->NbNodes();
7374 int aShapeId = FindShape( elem );
7377 curNodes.resize( nbNodes );
7378 uniqueNodes.resize( nbNodes );
7379 iRepl.resize( nbNodes );
7380 int iUnique = 0, iCur = 0, nbRepl = 0;
7382 // get new seq of nodes
7383 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7384 while ( itN->more() )
7386 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7388 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7389 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7391 { ////////// BUG 0020185: begin
7392 bool stopRecur = false;
7393 set<const SMDS_MeshNode*> nodesRecur;
7394 nodesRecur.insert(n);
7395 while (!stopRecur) {
7396 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7397 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7398 n = (*nnIt_i).second;
7399 if (!nodesRecur.insert(n).second) {
7400 // error: recursive dependancy
7407 } ////////// BUG 0020185: end
7409 curNodes[ iCur ] = n;
7410 bool isUnique = nodeSet.insert( n ).second;
7412 uniqueNodes[ iUnique++ ] = n;
7414 iRepl[ nbRepl++ ] = iCur;
7418 // Analyse element topology after replacement
7421 int nbUniqueNodes = nodeSet.size();
7422 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7424 if (elem->IsPoly()) // Polygons and Polyhedral volumes
7426 if (elem->GetType() == SMDSAbs_Face) // Polygon
7428 elemType.Init( elem );
7429 const bool isQuad = elemType.myIsQuad;
7431 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7432 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7434 // a polygon can divide into several elements
7435 vector<const SMDS_MeshNode *> polygons_nodes;
7436 vector<int> quantities;
7437 int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7440 vector<const SMDS_MeshNode *> face_nodes;
7442 for (int iface = 0; iface < nbNew; iface++)
7444 int nbNewNodes = quantities[iface];
7445 face_nodes.assign( polygons_nodes.begin() + inode,
7446 polygons_nodes.begin() + inode + nbNewNodes );
7447 inode += nbNewNodes;
7448 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7450 bool isValid = ( nbNewNodes % 2 == 0 );
7451 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7452 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7453 elemType.SetQuad( isValid );
7454 if ( isValid ) // put medium nodes after corners
7455 SMDS_MeshCell::applyInterlaceRev
7456 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7457 nbNewNodes ), face_nodes );
7459 SMDS_MeshElement* newElem = AddElement( face_nodes, elemType );
7461 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7464 rmElemIds.push_back(elem->GetID());
7468 else if (elem->GetType() == SMDSAbs_Volume) // Polyhedral volume
7470 if (nbUniqueNodes < 4) {
7471 rmElemIds.push_back(elem->GetID());
7474 // each face has to be analyzed in order to check volume validity
7475 const SMDS_VtkVolume* aPolyedre =
7476 dynamic_cast<const SMDS_VtkVolume*>( elem );
7478 int nbFaces = aPolyedre->NbFaces();
7480 vector<const SMDS_MeshNode *> poly_nodes;
7481 vector<int> quantities;
7483 for (int iface = 1; iface <= nbFaces; iface++) {
7484 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7485 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7487 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7488 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7489 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7490 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7491 faceNode = (*nnIt).second;
7493 faceNodes[inode - 1] = faceNode;
7496 SimplifyFace(faceNodes, poly_nodes, quantities);
7499 if (quantities.size() > 3) {
7500 // to be done: remove coincident faces
7503 if (quantities.size() > 3)
7505 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7506 const SMDS_MeshElement* newElem =
7507 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7508 myLastCreatedElems.Append(newElem);
7509 if ( aShapeId && newElem )
7510 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7511 rmElemIds.push_back(elem->GetID());
7515 rmElemIds.push_back(elem->GetID());
7526 // TODO not all the possible cases are solved. Find something more generic?
7527 switch ( nbNodes ) {
7528 case 2: ///////////////////////////////////// EDGE
7529 isOk = false; break;
7530 case 3: ///////////////////////////////////// TRIANGLE
7531 isOk = false; break;
7533 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7535 else { //////////////////////////////////// QUADRANGLE
7536 if ( nbUniqueNodes < 3 )
7538 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7539 isOk = false; // opposite nodes stick
7540 //MESSAGE("isOk " << isOk);
7543 case 6: ///////////////////////////////////// PENTAHEDRON
7544 if ( nbUniqueNodes == 4 ) {
7545 // ---------------------------------> tetrahedron
7547 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7548 // all top nodes stick: reverse a bottom
7549 uniqueNodes[ 0 ] = curNodes [ 1 ];
7550 uniqueNodes[ 1 ] = curNodes [ 0 ];
7552 else if (nbRepl == 3 &&
7553 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7554 // all bottom nodes stick: set a top before
7555 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7556 uniqueNodes[ 0 ] = curNodes [ 3 ];
7557 uniqueNodes[ 1 ] = curNodes [ 4 ];
7558 uniqueNodes[ 2 ] = curNodes [ 5 ];
7560 else if (nbRepl == 4 &&
7561 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7562 // a lateral face turns into a line: reverse a bottom
7563 uniqueNodes[ 0 ] = curNodes [ 1 ];
7564 uniqueNodes[ 1 ] = curNodes [ 0 ];
7569 else if ( nbUniqueNodes == 5 ) {
7570 // PENTAHEDRON --------------------> 2 tetrahedrons
7571 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7572 // a bottom node sticks with a linked top one
7574 SMDS_MeshElement* newElem =
7575 aMesh->AddVolume(curNodes[ 3 ],
7578 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7579 myLastCreatedElems.Append(newElem);
7581 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7582 // 2. : reverse a bottom
7583 uniqueNodes[ 0 ] = curNodes [ 1 ];
7584 uniqueNodes[ 1 ] = curNodes [ 0 ];
7594 if(elem->IsQuadratic()) { // Quadratic quadrangle
7606 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7609 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7611 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7612 uniqueNodes[0] = curNodes[0];
7613 uniqueNodes[1] = curNodes[2];
7614 uniqueNodes[2] = curNodes[3];
7615 uniqueNodes[3] = curNodes[5];
7616 uniqueNodes[4] = curNodes[6];
7617 uniqueNodes[5] = curNodes[7];
7620 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7621 uniqueNodes[0] = curNodes[0];
7622 uniqueNodes[1] = curNodes[1];
7623 uniqueNodes[2] = curNodes[2];
7624 uniqueNodes[3] = curNodes[4];
7625 uniqueNodes[4] = curNodes[5];
7626 uniqueNodes[5] = curNodes[6];
7629 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7630 uniqueNodes[0] = curNodes[1];
7631 uniqueNodes[1] = curNodes[2];
7632 uniqueNodes[2] = curNodes[3];
7633 uniqueNodes[3] = curNodes[5];
7634 uniqueNodes[4] = curNodes[6];
7635 uniqueNodes[5] = curNodes[0];
7638 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7639 uniqueNodes[0] = curNodes[0];
7640 uniqueNodes[1] = curNodes[1];
7641 uniqueNodes[2] = curNodes[3];
7642 uniqueNodes[3] = curNodes[4];
7643 uniqueNodes[4] = curNodes[6];
7644 uniqueNodes[5] = curNodes[7];
7647 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7648 uniqueNodes[0] = curNodes[0];
7649 uniqueNodes[1] = curNodes[2];
7650 uniqueNodes[2] = curNodes[3];
7651 uniqueNodes[3] = curNodes[1];
7652 uniqueNodes[4] = curNodes[6];
7653 uniqueNodes[5] = curNodes[7];
7656 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7657 uniqueNodes[0] = curNodes[0];
7658 uniqueNodes[1] = curNodes[1];
7659 uniqueNodes[2] = curNodes[2];
7660 uniqueNodes[3] = curNodes[4];
7661 uniqueNodes[4] = curNodes[5];
7662 uniqueNodes[5] = curNodes[7];
7665 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7666 uniqueNodes[0] = curNodes[0];
7667 uniqueNodes[1] = curNodes[1];
7668 uniqueNodes[2] = curNodes[3];
7669 uniqueNodes[3] = curNodes[4];
7670 uniqueNodes[4] = curNodes[2];
7671 uniqueNodes[5] = curNodes[7];
7674 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7675 uniqueNodes[0] = curNodes[0];
7676 uniqueNodes[1] = curNodes[1];
7677 uniqueNodes[2] = curNodes[2];
7678 uniqueNodes[3] = curNodes[4];
7679 uniqueNodes[4] = curNodes[5];
7680 uniqueNodes[5] = curNodes[3];
7685 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7688 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7692 //////////////////////////////////// HEXAHEDRON
7694 SMDS_VolumeTool hexa (elem);
7695 hexa.SetExternalNormal();
7696 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7697 //////////////////////// HEX ---> 1 tetrahedron
7698 for ( int iFace = 0; iFace < 6; iFace++ ) {
7699 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7700 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7701 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7702 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7703 // one face turns into a point ...
7704 int iOppFace = hexa.GetOppFaceIndex( iFace );
7705 ind = hexa.GetFaceNodesIndices( iOppFace );
7707 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7708 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7711 if ( nbStick == 1 ) {
7712 // ... and the opposite one - into a triangle.
7714 ind = hexa.GetFaceNodesIndices( iFace );
7715 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7722 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7723 //////////////////////// HEX ---> 1 prism
7724 int nbTria = 0, iTria[3];
7725 const int *ind; // indices of face nodes
7726 // look for triangular faces
7727 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7728 ind = hexa.GetFaceNodesIndices( iFace );
7729 TIDSortedNodeSet faceNodes;
7730 for ( iCur = 0; iCur < 4; iCur++ )
7731 faceNodes.insert( curNodes[ind[iCur]] );
7732 if ( faceNodes.size() == 3 )
7733 iTria[ nbTria++ ] = iFace;
7735 // check if triangles are opposite
7736 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7739 // set nodes of the bottom triangle
7740 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7742 for ( iCur = 0; iCur < 4; iCur++ )
7743 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7744 indB.push_back( ind[iCur] );
7745 if ( !hexa.IsForward() )
7746 std::swap( indB[0], indB[2] );
7747 for ( iCur = 0; iCur < 3; iCur++ )
7748 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7749 // set nodes of the top triangle
7750 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7751 for ( iCur = 0; iCur < 3; ++iCur )
7752 for ( int j = 0; j < 4; ++j )
7753 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7755 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7761 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7762 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7763 for ( int iFace = 0; iFace < 6; iFace++ ) {
7764 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7765 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7766 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7767 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7768 // one face turns into a point ...
7769 int iOppFace = hexa.GetOppFaceIndex( iFace );
7770 ind = hexa.GetFaceNodesIndices( iOppFace );
7772 iUnique = 2; // reverse a tetrahedron 1 bottom
7773 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7774 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7776 else if ( iUnique >= 0 )
7777 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7779 if ( nbStick == 0 ) {
7780 // ... and the opposite one is a quadrangle
7782 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7783 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7786 SMDS_MeshElement* newElem =
7787 aMesh->AddVolume(curNodes[ind[ 0 ]],
7790 curNodes[indTop[ 0 ]]);
7791 myLastCreatedElems.Append(newElem);
7793 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7800 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7801 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7802 // find indices of quad and tri faces
7803 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7804 for ( iFace = 0; iFace < 6; iFace++ ) {
7805 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7807 for ( iCur = 0; iCur < 4; iCur++ )
7808 nodeSet.insert( curNodes[ind[ iCur ]] );
7809 nbUniqueNodes = nodeSet.size();
7810 if ( nbUniqueNodes == 3 )
7811 iTriFace[ nbTri++ ] = iFace;
7812 else if ( nbUniqueNodes == 4 )
7813 iQuadFace[ nbQuad++ ] = iFace;
7815 if (nbQuad == 2 && nbTri == 4 &&
7816 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7817 // 2 opposite quadrangles stuck with a diagonal;
7818 // sample groups of merged indices: (0-4)(2-6)
7819 // --------------------------------------------> 2 tetrahedrons
7820 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7821 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7822 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7823 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7824 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7825 // stuck with 0-2 diagonal
7833 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7834 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7835 // stuck with 1-3 diagonal
7847 uniqueNodes[ 0 ] = curNodes [ i0 ];
7848 uniqueNodes[ 1 ] = curNodes [ i1d ];
7849 uniqueNodes[ 2 ] = curNodes [ i3d ];
7850 uniqueNodes[ 3 ] = curNodes [ i0t ];
7853 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7857 myLastCreatedElems.Append(newElem);
7859 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7862 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7863 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7864 // --------------------------------------------> prism
7865 // find 2 opposite triangles
7867 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7868 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7869 // find indices of kept and replaced nodes
7870 // and fill unique nodes of 2 opposite triangles
7871 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7872 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7873 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7874 // fill unique nodes
7877 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7878 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7879 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7881 // iCur of a linked node of the opposite face (make normals co-directed):
7882 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7883 // check that correspondent corners of triangles are linked
7884 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7887 uniqueNodes[ iUnique ] = n;
7888 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7897 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7900 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7907 } // switch ( nbNodes )
7909 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7911 if ( isOk ) // the non-poly elem remains valid after sticking nodes
7913 elemType.Init( elem ).SetID( elem->GetID() );
7915 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7916 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7918 uniqueNodes.resize(nbUniqueNodes);
7919 SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7920 if ( sm && newElem )
7921 sm->AddElement( newElem );
7922 if ( elem != newElem )
7923 ReplaceElemInGroups( elem, newElem, aMesh );
7926 // Remove invalid regular element or invalid polygon
7927 rmElemIds.push_back( elem->GetID() );
7930 } // loop on elements
7932 // Remove bad elements, then equal nodes (order important)
7934 Remove( rmElemIds, false );
7935 Remove( rmNodeIds, true );
7941 // ========================================================
7942 // class : SortableElement
7943 // purpose : allow sorting elements basing on their nodes
7944 // ========================================================
7945 class SortableElement : public set <const SMDS_MeshElement*>
7949 SortableElement( const SMDS_MeshElement* theElem )
7952 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7953 while ( nodeIt->more() )
7954 this->insert( nodeIt->next() );
7957 const SMDS_MeshElement* Get() const
7960 void Set(const SMDS_MeshElement* e) const
7965 mutable const SMDS_MeshElement* myElem;
7968 //=======================================================================
7969 //function : FindEqualElements
7970 //purpose : Return list of group of elements built on the same nodes.
7971 // Search among theElements or in the whole mesh if theElements is empty
7972 //=======================================================================
7974 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7975 TListOfListOfElementsID & theGroupsOfElementsID)
7977 myLastCreatedElems.Clear();
7978 myLastCreatedNodes.Clear();
7980 typedef map< SortableElement, int > TMapOfNodeSet;
7981 typedef list<int> TGroupOfElems;
7983 if ( theElements.empty() )
7984 { // get all elements in the mesh
7985 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7986 while ( eIt->more() )
7987 theElements.insert( theElements.end(), eIt->next() );
7990 vector< TGroupOfElems > arrayOfGroups;
7991 TGroupOfElems groupOfElems;
7992 TMapOfNodeSet mapOfNodeSet;
7994 TIDSortedElemSet::iterator elemIt = theElements.begin();
7995 for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7997 const SMDS_MeshElement* curElem = *elemIt;
7998 SortableElement SE(curElem);
8000 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8001 if ( !pp.second ) { // one more coincident elem
8002 TMapOfNodeSet::iterator& itSE = pp.first;
8003 int ind = (*itSE).second;
8004 arrayOfGroups[ind].push_back( curElem->GetID() );
8007 arrayOfGroups.push_back( groupOfElems );
8008 arrayOfGroups.back().push_back( curElem->GetID() );
8013 groupOfElems.clear();
8014 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8015 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8017 if ( groupIt->size() > 1 ) {
8018 //groupOfElems.sort(); -- theElements is sorted already
8019 theGroupsOfElementsID.push_back( groupOfElems );
8020 theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8025 //=======================================================================
8026 //function : MergeElements
8027 //purpose : In each given group, substitute all elements by the first one.
8028 //=======================================================================
8030 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8032 myLastCreatedElems.Clear();
8033 myLastCreatedNodes.Clear();
8035 typedef list<int> TListOfIDs;
8036 TListOfIDs rmElemIds; // IDs of elems to remove
8038 SMESHDS_Mesh* aMesh = GetMeshDS();
8040 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8041 while ( groupsIt != theGroupsOfElementsID.end() ) {
8042 TListOfIDs& aGroupOfElemID = *groupsIt;
8043 aGroupOfElemID.sort();
8044 int elemIDToKeep = aGroupOfElemID.front();
8045 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8046 aGroupOfElemID.pop_front();
8047 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8048 while ( idIt != aGroupOfElemID.end() ) {
8049 int elemIDToRemove = *idIt;
8050 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8051 // add the kept element in groups of removed one (PAL15188)
8052 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8053 rmElemIds.push_back( elemIDToRemove );
8059 Remove( rmElemIds, false );
8062 //=======================================================================
8063 //function : MergeEqualElements
8064 //purpose : Remove all but one of elements built on the same nodes.
8065 //=======================================================================
8067 void SMESH_MeshEditor::MergeEqualElements()
8069 TIDSortedElemSet aMeshElements; /* empty input ==
8070 to merge equal elements in the whole mesh */
8071 TListOfListOfElementsID aGroupsOfElementsID;
8072 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8073 MergeElements(aGroupsOfElementsID);
8076 //=======================================================================
8077 //function : findAdjacentFace
8079 //=======================================================================
8081 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8082 const SMDS_MeshNode* n2,
8083 const SMDS_MeshElement* elem)
8085 TIDSortedElemSet elemSet, avoidSet;
8087 avoidSet.insert ( elem );
8088 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8091 //=======================================================================
8092 //function : findSegment
8093 //purpose : Return a mesh segment by two nodes one of which can be medium
8094 //=======================================================================
8096 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8097 const SMDS_MeshNode* n2)
8099 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8100 while ( it->more() )
8102 const SMDS_MeshElement* seg = it->next();
8103 if ( seg->GetNodeIndex( n2 ) >= 0 )
8109 //=======================================================================
8110 //function : FindFreeBorder
8112 //=======================================================================
8114 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8116 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8117 const SMDS_MeshNode* theSecondNode,
8118 const SMDS_MeshNode* theLastNode,
8119 list< const SMDS_MeshNode* > & theNodes,
8120 list< const SMDS_MeshElement* >& theFaces)
8122 if ( !theFirstNode || !theSecondNode )
8124 // find border face between theFirstNode and theSecondNode
8125 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8129 theFaces.push_back( curElem );
8130 theNodes.push_back( theFirstNode );
8131 theNodes.push_back( theSecondNode );
8133 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8134 TIDSortedElemSet foundElems;
8135 bool needTheLast = ( theLastNode != 0 );
8137 while ( nStart != theLastNode ) {
8138 if ( nStart == theFirstNode )
8139 return !needTheLast;
8141 // find all free border faces sharing form nStart
8143 list< const SMDS_MeshElement* > curElemList;
8144 list< const SMDS_MeshNode* > nStartList;
8145 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8146 while ( invElemIt->more() ) {
8147 const SMDS_MeshElement* e = invElemIt->next();
8148 if ( e == curElem || foundElems.insert( e ).second ) {
8150 int iNode = 0, nbNodes = e->NbNodes();
8151 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8153 if ( e->IsQuadratic() ) {
8154 const SMDS_VtkFace* F =
8155 dynamic_cast<const SMDS_VtkFace*>(e);
8156 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8157 // use special nodes iterator
8158 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8159 while( anIter->more() ) {
8160 nodes[ iNode++ ] = cast2Node(anIter->next());
8164 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8165 while ( nIt->more() )
8166 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8168 nodes[ iNode ] = nodes[ 0 ];
8170 for ( iNode = 0; iNode < nbNodes; iNode++ )
8171 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8172 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8173 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8175 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8176 curElemList.push_back( e );
8180 // analyse the found
8182 int nbNewBorders = curElemList.size();
8183 if ( nbNewBorders == 0 ) {
8184 // no free border furthermore
8185 return !needTheLast;
8187 else if ( nbNewBorders == 1 ) {
8188 // one more element found
8190 nStart = nStartList.front();
8191 curElem = curElemList.front();
8192 theFaces.push_back( curElem );
8193 theNodes.push_back( nStart );
8196 // several continuations found
8197 list< const SMDS_MeshElement* >::iterator curElemIt;
8198 list< const SMDS_MeshNode* >::iterator nStartIt;
8199 // check if one of them reached the last node
8200 if ( needTheLast ) {
8201 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8202 curElemIt!= curElemList.end();
8203 curElemIt++, nStartIt++ )
8204 if ( *nStartIt == theLastNode ) {
8205 theFaces.push_back( *curElemIt );
8206 theNodes.push_back( *nStartIt );
8210 // find the best free border by the continuations
8211 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8212 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8213 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8214 curElemIt!= curElemList.end();
8215 curElemIt++, nStartIt++ )
8217 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8218 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8219 // find one more free border
8220 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8224 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8225 // choice: clear a worse one
8226 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8227 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8228 contNodes[ iWorse ].clear();
8229 contFaces[ iWorse ].clear();
8232 if ( contNodes[0].empty() && contNodes[1].empty() )
8235 // append the best free border
8236 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8237 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8238 theNodes.pop_back(); // remove nIgnore
8239 theNodes.pop_back(); // remove nStart
8240 theFaces.pop_back(); // remove curElem
8241 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8242 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8243 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8244 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8247 } // several continuations found
8248 } // while ( nStart != theLastNode )
8253 //=======================================================================
8254 //function : CheckFreeBorderNodes
8255 //purpose : Return true if the tree nodes are on a free border
8256 //=======================================================================
8258 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8259 const SMDS_MeshNode* theNode2,
8260 const SMDS_MeshNode* theNode3)
8262 list< const SMDS_MeshNode* > nodes;
8263 list< const SMDS_MeshElement* > faces;
8264 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8267 //=======================================================================
8268 //function : SewFreeBorder
8270 //=======================================================================
8272 SMESH_MeshEditor::Sew_Error
8273 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8274 const SMDS_MeshNode* theBordSecondNode,
8275 const SMDS_MeshNode* theBordLastNode,
8276 const SMDS_MeshNode* theSideFirstNode,
8277 const SMDS_MeshNode* theSideSecondNode,
8278 const SMDS_MeshNode* theSideThirdNode,
8279 const bool theSideIsFreeBorder,
8280 const bool toCreatePolygons,
8281 const bool toCreatePolyedrs)
8283 myLastCreatedElems.Clear();
8284 myLastCreatedNodes.Clear();
8286 MESSAGE("::SewFreeBorder()");
8287 Sew_Error aResult = SEW_OK;
8289 // ====================================
8290 // find side nodes and elements
8291 // ====================================
8293 list< const SMDS_MeshNode* > nSide[ 2 ];
8294 list< const SMDS_MeshElement* > eSide[ 2 ];
8295 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8296 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8300 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8301 nSide[0], eSide[0])) {
8302 MESSAGE(" Free Border 1 not found " );
8303 aResult = SEW_BORDER1_NOT_FOUND;
8305 if (theSideIsFreeBorder) {
8308 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8309 nSide[1], eSide[1])) {
8310 MESSAGE(" Free Border 2 not found " );
8311 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8314 if ( aResult != SEW_OK )
8317 if (!theSideIsFreeBorder) {
8321 // -------------------------------------------------------------------------
8323 // 1. If nodes to merge are not coincident, move nodes of the free border
8324 // from the coord sys defined by the direction from the first to last
8325 // nodes of the border to the correspondent sys of the side 2
8326 // 2. On the side 2, find the links most co-directed with the correspondent
8327 // links of the free border
8328 // -------------------------------------------------------------------------
8330 // 1. Since sewing may break if there are volumes to split on the side 2,
8331 // we wont move nodes but just compute new coordinates for them
8332 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8333 TNodeXYZMap nBordXYZ;
8334 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8335 list< const SMDS_MeshNode* >::iterator nBordIt;
8337 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8338 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8339 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8340 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8341 double tol2 = 1.e-8;
8342 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8343 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8344 // Need node movement.
8346 // find X and Z axes to create trsf
8347 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8349 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8351 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8354 gp_Ax3 toBordAx( Pb1, Zb, X );
8355 gp_Ax3 fromSideAx( Ps1, Zs, X );
8356 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8358 gp_Trsf toBordSys, fromSide2Sys;
8359 toBordSys.SetTransformation( toBordAx );
8360 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8361 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8364 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8365 const SMDS_MeshNode* n = *nBordIt;
8366 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8367 toBordSys.Transforms( xyz );
8368 fromSide2Sys.Transforms( xyz );
8369 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8373 // just insert nodes XYZ in the nBordXYZ map
8374 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8375 const SMDS_MeshNode* n = *nBordIt;
8376 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8380 // 2. On the side 2, find the links most co-directed with the correspondent
8381 // links of the free border
8383 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8384 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8385 sideNodes.push_back( theSideFirstNode );
8387 bool hasVolumes = false;
8388 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8389 set<long> foundSideLinkIDs, checkedLinkIDs;
8390 SMDS_VolumeTool volume;
8391 //const SMDS_MeshNode* faceNodes[ 4 ];
8393 const SMDS_MeshNode* sideNode;
8394 const SMDS_MeshElement* sideElem;
8395 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8396 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8397 nBordIt = bordNodes.begin();
8399 // border node position and border link direction to compare with
8400 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8401 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8402 // choose next side node by link direction or by closeness to
8403 // the current border node:
8404 bool searchByDir = ( *nBordIt != theBordLastNode );
8406 // find the next node on the Side 2
8408 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8410 checkedLinkIDs.clear();
8411 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8413 // loop on inverse elements of current node (prevSideNode) on the Side 2
8414 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8415 while ( invElemIt->more() )
8417 const SMDS_MeshElement* elem = invElemIt->next();
8418 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8419 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8420 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8421 bool isVolume = volume.Set( elem );
8422 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8423 if ( isVolume ) // --volume
8425 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8426 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8427 if(elem->IsQuadratic()) {
8428 const SMDS_VtkFace* F =
8429 dynamic_cast<const SMDS_VtkFace*>(elem);
8430 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8431 // use special nodes iterator
8432 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8433 while( anIter->more() ) {
8434 nodes[ iNode ] = cast2Node(anIter->next());
8435 if ( nodes[ iNode++ ] == prevSideNode )
8436 iPrevNode = iNode - 1;
8440 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8441 while ( nIt->more() ) {
8442 nodes[ iNode ] = cast2Node( nIt->next() );
8443 if ( nodes[ iNode++ ] == prevSideNode )
8444 iPrevNode = iNode - 1;
8447 // there are 2 links to check
8452 // loop on links, to be precise, on the second node of links
8453 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8454 const SMDS_MeshNode* n = nodes[ iNode ];
8456 if ( !volume.IsLinked( n, prevSideNode ))
8460 if ( iNode ) // a node before prevSideNode
8461 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8462 else // a node after prevSideNode
8463 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8465 // check if this link was already used
8466 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8467 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8468 if (!isJustChecked &&
8469 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8471 // test a link geometrically
8472 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8473 bool linkIsBetter = false;
8474 double dot = 0.0, dist = 0.0;
8475 if ( searchByDir ) { // choose most co-directed link
8476 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8477 linkIsBetter = ( dot > maxDot );
8479 else { // choose link with the node closest to bordPos
8480 dist = ( nextXYZ - bordPos ).SquareModulus();
8481 linkIsBetter = ( dist < minDist );
8483 if ( linkIsBetter ) {
8492 } // loop on inverse elements of prevSideNode
8495 MESSAGE(" Cant find path by links of the Side 2 ");
8496 return SEW_BAD_SIDE_NODES;
8498 sideNodes.push_back( sideNode );
8499 sideElems.push_back( sideElem );
8500 foundSideLinkIDs.insert ( linkID );
8501 prevSideNode = sideNode;
8503 if ( *nBordIt == theBordLastNode )
8504 searchByDir = false;
8506 // find the next border link to compare with
8507 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8508 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8509 // move to next border node if sideNode is before forward border node (bordPos)
8510 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8511 prevBordNode = *nBordIt;
8513 bordPos = nBordXYZ[ *nBordIt ];
8514 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8515 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8519 while ( sideNode != theSideSecondNode );
8521 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8522 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8523 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8525 } // end nodes search on the side 2
8527 // ============================
8528 // sew the border to the side 2
8529 // ============================
8531 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8532 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8534 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8535 if ( toMergeConformal && toCreatePolygons )
8537 // do not merge quadrangles if polygons are OK (IPAL0052824)
8538 eIt[0] = eSide[0].begin();
8539 eIt[1] = eSide[1].begin();
8540 bool allQuads[2] = { true, true };
8541 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8542 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8543 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8545 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8548 TListOfListOfNodes nodeGroupsToMerge;
8549 if (( toMergeConformal ) ||
8550 ( theSideIsFreeBorder && !theSideThirdNode )) {
8552 // all nodes are to be merged
8554 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8555 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8556 nIt[0]++, nIt[1]++ )
8558 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8559 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8560 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8565 // insert new nodes into the border and the side to get equal nb of segments
8567 // get normalized parameters of nodes on the borders
8568 vector< double > param[ 2 ];
8569 param[0].resize( maxNbNodes );
8570 param[1].resize( maxNbNodes );
8572 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8573 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8574 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8575 const SMDS_MeshNode* nPrev = *nIt;
8576 double bordLength = 0;
8577 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8578 const SMDS_MeshNode* nCur = *nIt;
8579 gp_XYZ segment (nCur->X() - nPrev->X(),
8580 nCur->Y() - nPrev->Y(),
8581 nCur->Z() - nPrev->Z());
8582 double segmentLen = segment.Modulus();
8583 bordLength += segmentLen;
8584 param[ iBord ][ iNode ] = bordLength;
8587 // normalize within [0,1]
8588 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8589 param[ iBord ][ iNode ] /= bordLength;
8593 // loop on border segments
8594 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8595 int i[ 2 ] = { 0, 0 };
8596 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8597 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8599 TElemOfNodeListMap insertMap;
8600 TElemOfNodeListMap::iterator insertMapIt;
8602 // key: elem to insert nodes into
8603 // value: 2 nodes to insert between + nodes to be inserted
8605 bool next[ 2 ] = { false, false };
8607 // find min adjacent segment length after sewing
8608 double nextParam = 10., prevParam = 0;
8609 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8610 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8611 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8612 if ( i[ iBord ] > 0 )
8613 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8615 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8616 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8617 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8619 // choose to insert or to merge nodes
8620 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8621 if ( Abs( du ) <= minSegLen * 0.2 ) {
8624 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8625 const SMDS_MeshNode* n0 = *nIt[0];
8626 const SMDS_MeshNode* n1 = *nIt[1];
8627 nodeGroupsToMerge.back().push_back( n1 );
8628 nodeGroupsToMerge.back().push_back( n0 );
8629 // position of node of the border changes due to merge
8630 param[ 0 ][ i[0] ] += du;
8631 // move n1 for the sake of elem shape evaluation during insertion.
8632 // n1 will be removed by MergeNodes() anyway
8633 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8634 next[0] = next[1] = true;
8639 int intoBord = ( du < 0 ) ? 0 : 1;
8640 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8641 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8642 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8643 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8644 if ( intoBord == 1 ) {
8645 // move node of the border to be on a link of elem of the side
8646 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8647 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8648 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8649 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8650 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8652 insertMapIt = insertMap.find( elem );
8653 bool notFound = ( insertMapIt == insertMap.end() );
8654 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8656 // insert into another link of the same element:
8657 // 1. perform insertion into the other link of the elem
8658 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8659 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8660 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8661 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8662 // 2. perform insertion into the link of adjacent faces
8663 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8664 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8666 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8667 InsertNodesIntoLink( seg, n12, n22, nodeList );
8669 if (toCreatePolyedrs) {
8670 // perform insertion into the links of adjacent volumes
8671 UpdateVolumes(n12, n22, nodeList);
8673 // 3. find an element appeared on n1 and n2 after the insertion
8674 insertMap.erase( elem );
8675 elem = findAdjacentFace( n1, n2, 0 );
8677 if ( notFound || otherLink ) {
8678 // add element and nodes of the side into the insertMap
8679 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8680 (*insertMapIt).second.push_back( n1 );
8681 (*insertMapIt).second.push_back( n2 );
8683 // add node to be inserted into elem
8684 (*insertMapIt).second.push_back( nIns );
8685 next[ 1 - intoBord ] = true;
8688 // go to the next segment
8689 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8690 if ( next[ iBord ] ) {
8691 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8693 nPrev[ iBord ] = *nIt[ iBord ];
8694 nIt[ iBord ]++; i[ iBord ]++;
8698 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8700 // perform insertion of nodes into elements
8702 for (insertMapIt = insertMap.begin();
8703 insertMapIt != insertMap.end();
8706 const SMDS_MeshElement* elem = (*insertMapIt).first;
8707 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8708 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8709 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8711 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8713 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8714 InsertNodesIntoLink( seg, n1, n2, nodeList );
8717 if ( !theSideIsFreeBorder ) {
8718 // look for and insert nodes into the faces adjacent to elem
8719 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8720 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8723 if (toCreatePolyedrs) {
8724 // perform insertion into the links of adjacent volumes
8725 UpdateVolumes(n1, n2, nodeList);
8728 } // end: insert new nodes
8730 MergeNodes ( nodeGroupsToMerge );
8733 // Remove coincident segments
8736 TIDSortedElemSet segments;
8737 SMESH_SequenceOfElemPtr newFaces;
8738 for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8740 if ( !myLastCreatedElems(i) ) continue;
8741 if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8742 segments.insert( segments.end(), myLastCreatedElems(i) );
8744 newFaces.Append( myLastCreatedElems(i) );
8747 TListOfListOfElementsID equalGroups;
8748 FindEqualElements( segments, equalGroups );
8749 if ( !equalGroups.empty() )
8751 // remove from segments those that will be removed
8752 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8753 for ( ; itGroups != equalGroups.end(); ++itGroups )
8755 list< int >& group = *itGroups;
8756 list< int >::iterator id = group.begin();
8757 for ( ++id; id != group.end(); ++id )
8758 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8759 segments.erase( seg );
8761 // remove equal segments
8762 MergeElements( equalGroups );
8764 // restore myLastCreatedElems
8765 myLastCreatedElems = newFaces;
8766 TIDSortedElemSet::iterator seg = segments.begin();
8767 for ( ; seg != segments.end(); ++seg )
8768 myLastCreatedElems.Append( *seg );
8774 //=======================================================================
8775 //function : InsertNodesIntoLink
8776 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8777 // and theBetweenNode2 and split theElement
8778 //=======================================================================
8780 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8781 const SMDS_MeshNode* theBetweenNode1,
8782 const SMDS_MeshNode* theBetweenNode2,
8783 list<const SMDS_MeshNode*>& theNodesToInsert,
8784 const bool toCreatePoly)
8786 if ( !theElement ) return;
8788 SMESHDS_Mesh *aMesh = GetMeshDS();
8789 vector<const SMDS_MeshElement*> newElems;
8791 if ( theElement->GetType() == SMDSAbs_Edge )
8793 theNodesToInsert.push_front( theBetweenNode1 );
8794 theNodesToInsert.push_back ( theBetweenNode2 );
8795 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8796 const SMDS_MeshNode* n1 = *n;
8797 for ( ++n; n != theNodesToInsert.end(); ++n )
8799 const SMDS_MeshNode* n2 = *n;
8800 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8801 AddToSameGroups( seg, theElement, aMesh );
8803 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8806 theNodesToInsert.pop_front();
8807 theNodesToInsert.pop_back();
8809 if ( theElement->IsQuadratic() ) // add a not split part
8811 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8812 theElement->end_nodes() );
8813 int iOther = 0, nbN = nodes.size();
8814 for ( ; iOther < nbN; ++iOther )
8815 if ( nodes[iOther] != theBetweenNode1 &&
8816 nodes[iOther] != theBetweenNode2 )
8820 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8821 AddToSameGroups( seg, theElement, aMesh );
8823 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8825 else if ( iOther == 2 )
8827 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8828 AddToSameGroups( seg, theElement, aMesh );
8830 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8833 // treat new elements
8834 for ( size_t i = 0; i < newElems.size(); ++i )
8837 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8838 myLastCreatedElems.Append( newElems[i] );
8840 ReplaceElemInGroups( theElement, newElems, aMesh );
8841 aMesh->RemoveElement( theElement );
8844 } // if ( theElement->GetType() == SMDSAbs_Edge )
8846 const SMDS_MeshElement* theFace = theElement;
8847 if ( theFace->GetType() != SMDSAbs_Face ) return;
8849 // find indices of 2 link nodes and of the rest nodes
8850 int iNode = 0, il1, il2, i3, i4;
8851 il1 = il2 = i3 = i4 = -1;
8852 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8854 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8855 while ( nodeIt->more() ) {
8856 const SMDS_MeshNode* n = nodeIt->next();
8857 if ( n == theBetweenNode1 )
8859 else if ( n == theBetweenNode2 )
8865 nodes[ iNode++ ] = n;
8867 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8870 // arrange link nodes to go one after another regarding the face orientation
8871 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8872 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8877 aNodesToInsert.reverse();
8879 // check that not link nodes of a quadrangles are in good order
8880 int nbFaceNodes = theFace->NbNodes();
8881 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8887 if (toCreatePoly || theFace->IsPoly()) {
8890 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8892 // add nodes of face up to first node of link
8895 if ( theFace->IsQuadratic() ) {
8896 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8897 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8898 // use special nodes iterator
8899 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8900 while( anIter->more() && !isFLN ) {
8901 const SMDS_MeshNode* n = cast2Node(anIter->next());
8902 poly_nodes[iNode++] = n;
8903 if (n == nodes[il1]) {
8907 // add nodes to insert
8908 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8909 for (; nIt != aNodesToInsert.end(); nIt++) {
8910 poly_nodes[iNode++] = *nIt;
8912 // add nodes of face starting from last node of link
8913 while ( anIter->more() ) {
8914 poly_nodes[iNode++] = cast2Node(anIter->next());
8918 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8919 while ( nodeIt->more() && !isFLN ) {
8920 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8921 poly_nodes[iNode++] = n;
8922 if (n == nodes[il1]) {
8926 // add nodes to insert
8927 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8928 for (; nIt != aNodesToInsert.end(); nIt++) {
8929 poly_nodes[iNode++] = *nIt;
8931 // add nodes of face starting from last node of link
8932 while ( nodeIt->more() ) {
8933 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8934 poly_nodes[iNode++] = n;
8939 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8942 else if ( !theFace->IsQuadratic() )
8944 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8945 int nbLinkNodes = 2 + aNodesToInsert.size();
8946 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8947 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8948 linkNodes[ 0 ] = nodes[ il1 ];
8949 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8950 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8951 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8952 linkNodes[ iNode++ ] = *nIt;
8954 // decide how to split a quadrangle: compare possible variants
8955 // and choose which of splits to be a quadrangle
8956 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8957 if ( nbFaceNodes == 3 ) {
8958 iBestQuad = nbSplits;
8961 else if ( nbFaceNodes == 4 ) {
8962 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8963 double aBestRate = DBL_MAX;
8964 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8966 double aBadRate = 0;
8967 // evaluate elements quality
8968 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8969 if ( iSplit == iQuad ) {
8970 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8974 aBadRate += getBadRate( &quad, aCrit );
8977 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8979 nodes[ iSplit < iQuad ? i4 : i3 ]);
8980 aBadRate += getBadRate( &tria, aCrit );
8984 if ( aBadRate < aBestRate ) {
8986 aBestRate = aBadRate;
8991 // create new elements
8993 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8994 SMDS_MeshElement* newElem = 0;
8995 if ( iSplit == iBestQuad )
8996 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9001 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9003 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
9006 const SMDS_MeshNode* newNodes[ 4 ];
9007 newNodes[ 0 ] = linkNodes[ i1 ];
9008 newNodes[ 1 ] = linkNodes[ i2 ];
9009 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9010 newNodes[ 3 ] = nodes[ i4 ];
9011 if (iSplit == iBestQuad)
9012 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
9014 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
9016 } // end if(!theFace->IsQuadratic())
9018 else { // theFace is quadratic
9019 // we have to split theFace on simple triangles and one simple quadrangle
9021 int nbshift = tmp*2;
9022 // shift nodes in nodes[] by nbshift
9024 for(i=0; i<nbshift; i++) {
9025 const SMDS_MeshNode* n = nodes[0];
9026 for(j=0; j<nbFaceNodes-1; j++) {
9027 nodes[j] = nodes[j+1];
9029 nodes[nbFaceNodes-1] = n;
9031 il1 = il1 - nbshift;
9032 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9033 // n0 n1 n2 n0 n1 n2
9034 // +-----+-----+ +-----+-----+
9043 // create new elements
9045 if ( nbFaceNodes == 6 ) { // quadratic triangle
9046 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9047 if ( theFace->IsMediumNode(nodes[il1]) ) {
9048 // create quadrangle
9049 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9055 // create quadrangle
9056 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9062 else { // nbFaceNodes==8 - quadratic quadrangle
9063 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9064 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9065 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9066 if ( theFace->IsMediumNode( nodes[ il1 ])) {
9067 // create quadrangle
9068 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9074 // create quadrangle
9075 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9081 // create needed triangles using n1,n2,n3 and inserted nodes
9082 int nbn = 2 + aNodesToInsert.size();
9083 vector<const SMDS_MeshNode*> aNodes(nbn);
9084 aNodes[0 ] = nodes[n1];
9085 aNodes[nbn-1] = nodes[n2];
9086 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9087 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9088 aNodes[iNode++] = *nIt;
9090 for ( i = 1; i < nbn; i++ )
9091 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9094 // remove the old face
9095 for ( size_t i = 0; i < newElems.size(); ++i )
9098 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9099 myLastCreatedElems.Append( newElems[i] );
9101 ReplaceElemInGroups( theFace, newElems, aMesh );
9102 aMesh->RemoveElement(theFace);
9104 } // InsertNodesIntoLink()
9106 //=======================================================================
9107 //function : UpdateVolumes
9109 //=======================================================================
9111 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9112 const SMDS_MeshNode* theBetweenNode2,
9113 list<const SMDS_MeshNode*>& theNodesToInsert)
9115 myLastCreatedElems.Clear();
9116 myLastCreatedNodes.Clear();
9118 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9119 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9120 const SMDS_MeshElement* elem = invElemIt->next();
9122 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9123 SMDS_VolumeTool aVolume (elem);
9124 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9127 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9128 int iface, nbFaces = aVolume.NbFaces();
9129 vector<const SMDS_MeshNode *> poly_nodes;
9130 vector<int> quantities (nbFaces);
9132 for (iface = 0; iface < nbFaces; iface++) {
9133 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9134 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9135 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9137 for (int inode = 0; inode < nbFaceNodes; inode++) {
9138 poly_nodes.push_back(faceNodes[inode]);
9140 if (nbInserted == 0) {
9141 if (faceNodes[inode] == theBetweenNode1) {
9142 if (faceNodes[inode + 1] == theBetweenNode2) {
9143 nbInserted = theNodesToInsert.size();
9145 // add nodes to insert
9146 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9147 for (; nIt != theNodesToInsert.end(); nIt++) {
9148 poly_nodes.push_back(*nIt);
9152 else if (faceNodes[inode] == theBetweenNode2) {
9153 if (faceNodes[inode + 1] == theBetweenNode1) {
9154 nbInserted = theNodesToInsert.size();
9156 // add nodes to insert in reversed order
9157 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9159 for (; nIt != theNodesToInsert.begin(); nIt--) {
9160 poly_nodes.push_back(*nIt);
9162 poly_nodes.push_back(*nIt);
9169 quantities[iface] = nbFaceNodes + nbInserted;
9172 // Replace the volume
9173 SMESHDS_Mesh *aMesh = GetMeshDS();
9175 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9177 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9178 myLastCreatedElems.Append( newElem );
9179 ReplaceElemInGroups( elem, newElem, aMesh );
9181 aMesh->RemoveElement( elem );
9187 //================================================================================
9189 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9191 //================================================================================
9193 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9194 vector<const SMDS_MeshNode *> & nodes,
9195 vector<int> & nbNodeInFaces )
9198 nbNodeInFaces.clear();
9199 SMDS_VolumeTool vTool ( elem );
9200 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9202 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9203 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9204 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9209 //=======================================================================
9211 * \brief Convert elements contained in a sub-mesh to quadratic
9212 * \return int - nb of checked elements
9214 //=======================================================================
9216 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9217 SMESH_MesherHelper& theHelper,
9218 const bool theForce3d)
9221 if( !theSm ) return nbElem;
9223 vector<int> nbNodeInFaces;
9224 vector<const SMDS_MeshNode *> nodes;
9225 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9226 while(ElemItr->more())
9229 const SMDS_MeshElement* elem = ElemItr->next();
9230 if( !elem ) continue;
9232 // analyse a necessity of conversion
9233 const SMDSAbs_ElementType aType = elem->GetType();
9234 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9236 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9237 bool hasCentralNodes = false;
9238 if ( elem->IsQuadratic() )
9241 switch ( aGeomType ) {
9242 case SMDSEntity_Quad_Triangle:
9243 case SMDSEntity_Quad_Quadrangle:
9244 case SMDSEntity_Quad_Hexa:
9245 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9247 case SMDSEntity_BiQuad_Triangle:
9248 case SMDSEntity_BiQuad_Quadrangle:
9249 case SMDSEntity_TriQuad_Hexa:
9250 alreadyOK = theHelper.GetIsBiQuadratic();
9251 hasCentralNodes = true;
9256 // take into account already present modium nodes
9258 case SMDSAbs_Volume:
9259 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9261 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9263 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9269 // get elem data needed to re-create it
9271 const int id = elem->GetID();
9272 const int nbNodes = elem->NbCornerNodes();
9273 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9274 if ( aGeomType == SMDSEntity_Polyhedra )
9275 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9276 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9277 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9279 // remove a linear element
9280 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9282 // remove central nodes of biquadratic elements (biquad->quad convertion)
9283 if ( hasCentralNodes )
9284 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9285 if ( nodes[i]->NbInverseElements() == 0 )
9286 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9288 const SMDS_MeshElement* NewElem = 0;
9294 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9302 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9305 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9308 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9312 case SMDSAbs_Volume :
9316 case SMDSEntity_Tetra:
9317 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9319 case SMDSEntity_Pyramid:
9320 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9322 case SMDSEntity_Penta:
9323 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9325 case SMDSEntity_Hexa:
9326 case SMDSEntity_Quad_Hexa:
9327 case SMDSEntity_TriQuad_Hexa:
9328 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9329 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9331 case SMDSEntity_Hexagonal_Prism:
9333 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9340 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9341 if( NewElem && NewElem->getshapeId() < 1 )
9342 theSm->AddElement( NewElem );
9346 //=======================================================================
9347 //function : ConvertToQuadratic
9349 //=======================================================================
9351 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9353 SMESHDS_Mesh* meshDS = GetMeshDS();
9355 SMESH_MesherHelper aHelper(*myMesh);
9357 aHelper.SetIsQuadratic( true );
9358 aHelper.SetIsBiQuadratic( theToBiQuad );
9359 aHelper.SetElementsOnShape(true);
9360 aHelper.ToFixNodeParameters( true );
9362 // convert elements assigned to sub-meshes
9363 int nbCheckedElems = 0;
9364 if ( myMesh->HasShapeToMesh() )
9366 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9368 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9369 while ( smIt->more() ) {
9370 SMESH_subMesh* sm = smIt->next();
9371 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9372 aHelper.SetSubShape( sm->GetSubShape() );
9373 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9379 // convert elements NOT assigned to sub-meshes
9380 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9381 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9383 aHelper.SetElementsOnShape(false);
9384 SMESHDS_SubMesh *smDS = 0;
9387 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9388 while( aEdgeItr->more() )
9390 const SMDS_MeshEdge* edge = aEdgeItr->next();
9391 if ( !edge->IsQuadratic() )
9393 int id = edge->GetID();
9394 const SMDS_MeshNode* n1 = edge->GetNode(0);
9395 const SMDS_MeshNode* n2 = edge->GetNode(1);
9397 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9399 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9400 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9404 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9409 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9410 while( aFaceItr->more() )
9412 const SMDS_MeshFace* face = aFaceItr->next();
9413 if ( !face ) continue;
9415 const SMDSAbs_EntityType type = face->GetEntityType();
9419 case SMDSEntity_Quad_Triangle:
9420 case SMDSEntity_Quad_Quadrangle:
9421 alreadyOK = !theToBiQuad;
9422 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9424 case SMDSEntity_BiQuad_Triangle:
9425 case SMDSEntity_BiQuad_Quadrangle:
9426 alreadyOK = theToBiQuad;
9427 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9429 default: alreadyOK = false;
9434 const int id = face->GetID();
9435 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9437 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9439 SMDS_MeshFace * NewFace = 0;
9442 case SMDSEntity_Triangle:
9443 case SMDSEntity_Quad_Triangle:
9444 case SMDSEntity_BiQuad_Triangle:
9445 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9446 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9447 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9450 case SMDSEntity_Quadrangle:
9451 case SMDSEntity_Quad_Quadrangle:
9452 case SMDSEntity_BiQuad_Quadrangle:
9453 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9454 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9455 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9459 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9461 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9465 vector<int> nbNodeInFaces;
9466 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9467 while(aVolumeItr->more())
9469 const SMDS_MeshVolume* volume = aVolumeItr->next();
9470 if ( !volume ) continue;
9472 const SMDSAbs_EntityType type = volume->GetEntityType();
9473 if ( volume->IsQuadratic() )
9478 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9479 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9480 default: alreadyOK = true;
9484 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9488 const int id = volume->GetID();
9489 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9490 if ( type == SMDSEntity_Polyhedra )
9491 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9492 else if ( type == SMDSEntity_Hexagonal_Prism )
9493 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9495 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9497 SMDS_MeshVolume * NewVolume = 0;
9500 case SMDSEntity_Tetra:
9501 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9503 case SMDSEntity_Hexa:
9504 case SMDSEntity_Quad_Hexa:
9505 case SMDSEntity_TriQuad_Hexa:
9506 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9507 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9508 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9509 if ( nodes[i]->NbInverseElements() == 0 )
9510 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9512 case SMDSEntity_Pyramid:
9513 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9514 nodes[3], nodes[4], id, theForce3d);
9516 case SMDSEntity_Penta:
9517 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9518 nodes[3], nodes[4], nodes[5], id, theForce3d);
9520 case SMDSEntity_Hexagonal_Prism:
9522 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9524 ReplaceElemInGroups(volume, NewVolume, meshDS);
9529 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9530 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9531 // aHelper.FixQuadraticElements(myError);
9532 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9536 //================================================================================
9538 * \brief Makes given elements quadratic
9539 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9540 * \param theElements - elements to make quadratic
9542 //================================================================================
9544 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9545 TIDSortedElemSet& theElements,
9546 const bool theToBiQuad)
9548 if ( theElements.empty() ) return;
9550 // we believe that all theElements are of the same type
9551 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9553 // get all nodes shared by theElements
9554 TIDSortedNodeSet allNodes;
9555 TIDSortedElemSet::iterator eIt = theElements.begin();
9556 for ( ; eIt != theElements.end(); ++eIt )
9557 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9559 // complete theElements with elements of lower dim whose all nodes are in allNodes
9561 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9562 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9563 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9564 for ( ; nIt != allNodes.end(); ++nIt )
9566 const SMDS_MeshNode* n = *nIt;
9567 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9568 while ( invIt->more() )
9570 const SMDS_MeshElement* e = invIt->next();
9571 const SMDSAbs_ElementType type = e->GetType();
9572 if ( e->IsQuadratic() )
9574 quadAdjacentElems[ type ].insert( e );
9577 switch ( e->GetEntityType() ) {
9578 case SMDSEntity_Quad_Triangle:
9579 case SMDSEntity_Quad_Quadrangle:
9580 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9581 case SMDSEntity_BiQuad_Triangle:
9582 case SMDSEntity_BiQuad_Quadrangle:
9583 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9584 default: alreadyOK = true;
9589 if ( type >= elemType )
9590 continue; // same type or more complex linear element
9592 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9593 continue; // e is already checked
9597 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9598 while ( nodeIt->more() && allIn )
9599 allIn = allNodes.count( nodeIt->next() );
9601 theElements.insert(e );
9605 SMESH_MesherHelper helper(*myMesh);
9606 helper.SetIsQuadratic( true );
9607 helper.SetIsBiQuadratic( theToBiQuad );
9609 // add links of quadratic adjacent elements to the helper
9611 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9612 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9613 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9615 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9617 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9618 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9619 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9621 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9623 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9624 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9625 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9627 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9630 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9632 SMESHDS_Mesh* meshDS = GetMeshDS();
9633 SMESHDS_SubMesh* smDS = 0;
9634 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9636 const SMDS_MeshElement* elem = *eIt;
9639 int nbCentralNodes = 0;
9640 switch ( elem->GetEntityType() ) {
9641 // linear convertible
9642 case SMDSEntity_Edge:
9643 case SMDSEntity_Triangle:
9644 case SMDSEntity_Quadrangle:
9645 case SMDSEntity_Tetra:
9646 case SMDSEntity_Pyramid:
9647 case SMDSEntity_Hexa:
9648 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9649 // quadratic that can become bi-quadratic
9650 case SMDSEntity_Quad_Triangle:
9651 case SMDSEntity_Quad_Quadrangle:
9652 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9654 case SMDSEntity_BiQuad_Triangle:
9655 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9656 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9658 default: alreadyOK = true;
9660 if ( alreadyOK ) continue;
9662 const SMDSAbs_ElementType type = elem->GetType();
9663 const int id = elem->GetID();
9664 const int nbNodes = elem->NbCornerNodes();
9665 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9667 helper.SetSubShape( elem->getshapeId() );
9669 if ( !smDS || !smDS->Contains( elem ))
9670 smDS = meshDS->MeshElements( elem->getshapeId() );
9671 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9673 SMDS_MeshElement * newElem = 0;
9676 case 4: // cases for most frequently used element types go first (for optimization)
9677 if ( type == SMDSAbs_Volume )
9678 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9680 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9683 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9684 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9687 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9690 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9693 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9694 nodes[4], id, theForce3d);
9697 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9698 nodes[4], nodes[5], id, theForce3d);
9702 ReplaceElemInGroups( elem, newElem, meshDS);
9703 if( newElem && smDS )
9704 smDS->AddElement( newElem );
9706 // remove central nodes
9707 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9708 if ( nodes[i]->NbInverseElements() == 0 )
9709 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9711 } // loop on theElements
9714 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9715 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9716 // helper.FixQuadraticElements( myError );
9717 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9721 //=======================================================================
9723 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9724 * \return int - nb of checked elements
9726 //=======================================================================
9728 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9729 SMDS_ElemIteratorPtr theItr,
9730 const int theShapeID)
9733 SMESHDS_Mesh* meshDS = GetMeshDS();
9734 ElemFeatures elemType;
9735 vector<const SMDS_MeshNode *> nodes;
9737 while( theItr->more() )
9739 const SMDS_MeshElement* elem = theItr->next();
9741 if( elem && elem->IsQuadratic())
9744 int nbCornerNodes = elem->NbCornerNodes();
9745 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9747 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9749 //remove a quadratic element
9750 if ( !theSm || !theSm->Contains( elem ))
9751 theSm = meshDS->MeshElements( elem->getshapeId() );
9752 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9754 // remove medium nodes
9755 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9756 if ( nodes[i]->NbInverseElements() == 0 )
9757 meshDS->RemoveFreeNode( nodes[i], theSm );
9759 // add a linear element
9760 nodes.resize( nbCornerNodes );
9761 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9762 ReplaceElemInGroups(elem, newElem, meshDS);
9763 if( theSm && newElem )
9764 theSm->AddElement( newElem );
9770 //=======================================================================
9771 //function : ConvertFromQuadratic
9773 //=======================================================================
9775 bool SMESH_MeshEditor::ConvertFromQuadratic()
9777 int nbCheckedElems = 0;
9778 if ( myMesh->HasShapeToMesh() )
9780 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9782 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9783 while ( smIt->more() ) {
9784 SMESH_subMesh* sm = smIt->next();
9785 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9786 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9792 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9793 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9795 SMESHDS_SubMesh *aSM = 0;
9796 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9804 //================================================================================
9806 * \brief Return true if all medium nodes of the element are in the node set
9808 //================================================================================
9810 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9812 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9813 if ( !nodeSet.count( elem->GetNode(i) ))
9819 //================================================================================
9821 * \brief Makes given elements linear
9823 //================================================================================
9825 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9827 if ( theElements.empty() ) return;
9829 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9830 set<int> mediumNodeIDs;
9831 TIDSortedElemSet::iterator eIt = theElements.begin();
9832 for ( ; eIt != theElements.end(); ++eIt )
9834 const SMDS_MeshElement* e = *eIt;
9835 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9836 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9839 // replace given elements by linear ones
9840 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9841 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9843 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9844 // except those elements sharing medium nodes of quadratic element whose medium nodes
9845 // are not all in mediumNodeIDs
9847 // get remaining medium nodes
9848 TIDSortedNodeSet mediumNodes;
9849 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9850 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9851 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9852 mediumNodes.insert( mediumNodes.end(), n );
9854 // find more quadratic elements to convert
9855 TIDSortedElemSet moreElemsToConvert;
9856 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9857 for ( ; nIt != mediumNodes.end(); ++nIt )
9859 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9860 while ( invIt->more() )
9862 const SMDS_MeshElement* e = invIt->next();
9863 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9865 // find a more complex element including e and
9866 // whose medium nodes are not in mediumNodes
9867 bool complexFound = false;
9868 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9870 SMDS_ElemIteratorPtr invIt2 =
9871 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9872 while ( invIt2->more() )
9874 const SMDS_MeshElement* eComplex = invIt2->next();
9875 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9877 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9878 if ( nbCommonNodes == e->NbNodes())
9880 complexFound = true;
9881 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9887 if ( !complexFound )
9888 moreElemsToConvert.insert( e );
9892 elemIt = elemSetIterator( moreElemsToConvert );
9893 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9896 //=======================================================================
9897 //function : SewSideElements
9899 //=======================================================================
9901 SMESH_MeshEditor::Sew_Error
9902 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9903 TIDSortedElemSet& theSide2,
9904 const SMDS_MeshNode* theFirstNode1,
9905 const SMDS_MeshNode* theFirstNode2,
9906 const SMDS_MeshNode* theSecondNode1,
9907 const SMDS_MeshNode* theSecondNode2)
9909 myLastCreatedElems.Clear();
9910 myLastCreatedNodes.Clear();
9912 MESSAGE ("::::SewSideElements()");
9913 if ( theSide1.size() != theSide2.size() )
9914 return SEW_DIFF_NB_OF_ELEMENTS;
9916 Sew_Error aResult = SEW_OK;
9918 // 1. Build set of faces representing each side
9919 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9920 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9922 // =======================================================================
9923 // 1. Build set of faces representing each side:
9924 // =======================================================================
9925 // a. build set of nodes belonging to faces
9926 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9927 // c. create temporary faces representing side of volumes if correspondent
9928 // face does not exist
9930 SMESHDS_Mesh* aMesh = GetMeshDS();
9931 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9932 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9933 TIDSortedElemSet faceSet1, faceSet2;
9934 set<const SMDS_MeshElement*> volSet1, volSet2;
9935 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9936 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9937 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9938 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9939 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9940 int iSide, iFace, iNode;
9942 list<const SMDS_MeshElement* > tempFaceList;
9943 for ( iSide = 0; iSide < 2; iSide++ ) {
9944 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9945 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9946 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9947 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9948 set<const SMDS_MeshElement*>::iterator vIt;
9949 TIDSortedElemSet::iterator eIt;
9950 set<const SMDS_MeshNode*>::iterator nIt;
9952 // check that given nodes belong to given elements
9953 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9954 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9955 int firstIndex = -1, secondIndex = -1;
9956 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9957 const SMDS_MeshElement* elem = *eIt;
9958 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9959 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9960 if ( firstIndex > -1 && secondIndex > -1 ) break;
9962 if ( firstIndex < 0 || secondIndex < 0 ) {
9963 // we can simply return until temporary faces created
9964 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9967 // -----------------------------------------------------------
9968 // 1a. Collect nodes of existing faces
9969 // and build set of face nodes in order to detect missing
9970 // faces corresponding to sides of volumes
9971 // -----------------------------------------------------------
9973 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9975 // loop on the given element of a side
9976 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9977 //const SMDS_MeshElement* elem = *eIt;
9978 const SMDS_MeshElement* elem = *eIt;
9979 if ( elem->GetType() == SMDSAbs_Face ) {
9980 faceSet->insert( elem );
9981 set <const SMDS_MeshNode*> faceNodeSet;
9982 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9983 while ( nodeIt->more() ) {
9984 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9985 nodeSet->insert( n );
9986 faceNodeSet.insert( n );
9988 setOfFaceNodeSet.insert( faceNodeSet );
9990 else if ( elem->GetType() == SMDSAbs_Volume )
9991 volSet->insert( elem );
9993 // ------------------------------------------------------------------------------
9994 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9995 // ------------------------------------------------------------------------------
9997 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9998 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9999 while ( fIt->more() ) { // loop on faces sharing a node
10000 const SMDS_MeshElement* f = fIt->next();
10001 if ( faceSet->find( f ) == faceSet->end() ) {
10002 // check if all nodes are in nodeSet and
10003 // complete setOfFaceNodeSet if they are
10004 set <const SMDS_MeshNode*> faceNodeSet;
10005 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10006 bool allInSet = true;
10007 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10008 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10009 if ( nodeSet->find( n ) == nodeSet->end() )
10012 faceNodeSet.insert( n );
10015 faceSet->insert( f );
10016 setOfFaceNodeSet.insert( faceNodeSet );
10022 // -------------------------------------------------------------------------
10023 // 1c. Create temporary faces representing sides of volumes if correspondent
10024 // face does not exist
10025 // -------------------------------------------------------------------------
10027 if ( !volSet->empty() ) {
10028 //int nodeSetSize = nodeSet->size();
10030 // loop on given volumes
10031 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10032 SMDS_VolumeTool vol (*vIt);
10033 // loop on volume faces: find free faces
10034 // --------------------------------------
10035 list<const SMDS_MeshElement* > freeFaceList;
10036 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10037 if ( !vol.IsFreeFace( iFace ))
10039 // check if there is already a face with same nodes in a face set
10040 const SMDS_MeshElement* aFreeFace = 0;
10041 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10042 int nbNodes = vol.NbFaceNodes( iFace );
10043 set <const SMDS_MeshNode*> faceNodeSet;
10044 vol.GetFaceNodes( iFace, faceNodeSet );
10045 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10047 // no such a face is given but it still can exist, check it
10048 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10049 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10051 if ( !aFreeFace ) {
10052 // create a temporary face
10053 if ( nbNodes == 3 ) {
10054 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10055 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10057 else if ( nbNodes == 4 ) {
10058 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10059 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10062 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10063 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10064 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10067 tempFaceList.push_back( aFreeFace );
10071 freeFaceList.push_back( aFreeFace );
10073 } // loop on faces of a volume
10075 // choose one of several free faces of a volume
10076 // --------------------------------------------
10077 if ( freeFaceList.size() > 1 ) {
10078 // choose a face having max nb of nodes shared by other elems of a side
10079 int maxNbNodes = -1;
10080 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10081 while ( fIt != freeFaceList.end() ) { // loop on free faces
10082 int nbSharedNodes = 0;
10083 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10084 while ( nodeIt->more() ) { // loop on free face nodes
10085 const SMDS_MeshNode* n =
10086 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10087 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10088 while ( invElemIt->more() ) {
10089 const SMDS_MeshElement* e = invElemIt->next();
10090 nbSharedNodes += faceSet->count( e );
10091 nbSharedNodes += elemSet->count( e );
10094 if ( nbSharedNodes > maxNbNodes ) {
10095 maxNbNodes = nbSharedNodes;
10096 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10098 else if ( nbSharedNodes == maxNbNodes ) {
10102 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10105 if ( freeFaceList.size() > 1 )
10107 // could not choose one face, use another way
10108 // choose a face most close to the bary center of the opposite side
10109 gp_XYZ aBC( 0., 0., 0. );
10110 set <const SMDS_MeshNode*> addedNodes;
10111 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10112 eIt = elemSet2->begin();
10113 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10114 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10115 while ( nodeIt->more() ) { // loop on free face nodes
10116 const SMDS_MeshNode* n =
10117 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10118 if ( addedNodes.insert( n ).second )
10119 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10122 aBC /= addedNodes.size();
10123 double minDist = DBL_MAX;
10124 fIt = freeFaceList.begin();
10125 while ( fIt != freeFaceList.end() ) { // loop on free faces
10127 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10128 while ( nodeIt->more() ) { // loop on free face nodes
10129 const SMDS_MeshNode* n =
10130 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10131 gp_XYZ p( n->X(),n->Y(),n->Z() );
10132 dist += ( aBC - p ).SquareModulus();
10134 if ( dist < minDist ) {
10136 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10139 fIt = freeFaceList.erase( fIt++ );
10142 } // choose one of several free faces of a volume
10144 if ( freeFaceList.size() == 1 ) {
10145 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10146 faceSet->insert( aFreeFace );
10147 // complete a node set with nodes of a found free face
10148 // for ( iNode = 0; iNode < ; iNode++ )
10149 // nodeSet->insert( fNodes[ iNode ] );
10152 } // loop on volumes of a side
10154 // // complete a set of faces if new nodes in a nodeSet appeared
10155 // // ----------------------------------------------------------
10156 // if ( nodeSetSize != nodeSet->size() ) {
10157 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10158 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10159 // while ( fIt->more() ) { // loop on faces sharing a node
10160 // const SMDS_MeshElement* f = fIt->next();
10161 // if ( faceSet->find( f ) == faceSet->end() ) {
10162 // // check if all nodes are in nodeSet and
10163 // // complete setOfFaceNodeSet if they are
10164 // set <const SMDS_MeshNode*> faceNodeSet;
10165 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10166 // bool allInSet = true;
10167 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10168 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10169 // if ( nodeSet->find( n ) == nodeSet->end() )
10170 // allInSet = false;
10172 // faceNodeSet.insert( n );
10174 // if ( allInSet ) {
10175 // faceSet->insert( f );
10176 // setOfFaceNodeSet.insert( faceNodeSet );
10182 } // Create temporary faces, if there are volumes given
10185 if ( faceSet1.size() != faceSet2.size() ) {
10186 // delete temporary faces: they are in reverseElements of actual nodes
10187 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10188 // while ( tmpFaceIt->more() )
10189 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10190 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10191 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10192 // aMesh->RemoveElement(*tmpFaceIt);
10193 MESSAGE("Diff nb of faces");
10194 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10197 // ============================================================
10198 // 2. Find nodes to merge:
10199 // bind a node to remove to a node to put instead
10200 // ============================================================
10202 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10203 if ( theFirstNode1 != theFirstNode2 )
10204 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10205 if ( theSecondNode1 != theSecondNode2 )
10206 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10208 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10209 set< long > linkIdSet; // links to process
10210 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10212 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10213 list< NLink > linkList[2];
10214 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10215 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10216 // loop on links in linkList; find faces by links and append links
10217 // of the found faces to linkList
10218 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10219 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10221 NLink link[] = { *linkIt[0], *linkIt[1] };
10222 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10223 if ( !linkIdSet.count( linkID ) )
10226 // by links, find faces in the face sets,
10227 // and find indices of link nodes in the found faces;
10228 // in a face set, there is only one or no face sharing a link
10229 // ---------------------------------------------------------------
10231 const SMDS_MeshElement* face[] = { 0, 0 };
10232 vector<const SMDS_MeshNode*> fnodes[2];
10233 int iLinkNode[2][2];
10234 TIDSortedElemSet avoidSet;
10235 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10236 const SMDS_MeshNode* n1 = link[iSide].first;
10237 const SMDS_MeshNode* n2 = link[iSide].second;
10238 //cout << "Side " << iSide << " ";
10239 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10240 // find a face by two link nodes
10241 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10242 *faceSetPtr[ iSide ], avoidSet,
10243 &iLinkNode[iSide][0],
10244 &iLinkNode[iSide][1] );
10245 if ( face[ iSide ])
10247 //cout << " F " << face[ iSide]->GetID() <<endl;
10248 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10249 // put face nodes to fnodes
10250 if ( face[ iSide ]->IsQuadratic() )
10252 // use interlaced nodes iterator
10253 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10254 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10255 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10256 while ( nIter->more() )
10257 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10261 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10262 face[ iSide ]->end_nodes() );
10264 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10268 // check similarity of elements of the sides
10269 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10270 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10271 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10272 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10275 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10277 break; // do not return because it's necessary to remove tmp faces
10280 // set nodes to merge
10281 // -------------------
10283 if ( face[0] && face[1] ) {
10284 const int nbNodes = face[0]->NbNodes();
10285 if ( nbNodes != face[1]->NbNodes() ) {
10286 MESSAGE("Diff nb of face nodes");
10287 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10288 break; // do not return because it s necessary to remove tmp faces
10290 bool reverse[] = { false, false }; // order of nodes in the link
10291 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10292 // analyse link orientation in faces
10293 int i1 = iLinkNode[ iSide ][ 0 ];
10294 int i2 = iLinkNode[ iSide ][ 1 ];
10295 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10297 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10298 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10299 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10301 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10302 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10305 // add other links of the faces to linkList
10306 // -----------------------------------------
10308 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10309 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10310 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10311 if ( !iter_isnew.second ) { // already in a set: no need to process
10312 linkIdSet.erase( iter_isnew.first );
10314 else // new in set == encountered for the first time: add
10316 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10317 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10318 linkList[0].push_back ( NLink( n1, n2 ));
10319 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10324 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10327 } // loop on link lists
10329 if ( aResult == SEW_OK &&
10330 ( //linkIt[0] != linkList[0].end() ||
10331 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10332 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10333 " " << (faceSetPtr[1]->empty()));
10334 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10337 // ====================================================================
10338 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10339 // ====================================================================
10341 // delete temporary faces
10342 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10343 // while ( tmpFaceIt->more() )
10344 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10345 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10346 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10347 aMesh->RemoveElement(*tmpFaceIt);
10349 if ( aResult != SEW_OK)
10352 list< int > nodeIDsToRemove;
10353 vector< const SMDS_MeshNode*> nodes;
10354 ElemFeatures elemType;
10356 // loop on nodes replacement map
10357 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10358 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10359 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10361 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10362 nodeIDsToRemove.push_back( nToRemove->GetID() );
10363 // loop on elements sharing nToRemove
10364 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10365 while ( invElemIt->more() ) {
10366 const SMDS_MeshElement* e = invElemIt->next();
10367 // get a new suite of nodes: make replacement
10368 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10369 nodes.resize( nbNodes );
10370 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10371 while ( nIt->more() ) {
10372 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10373 nnIt = nReplaceMap.find( n );
10374 if ( nnIt != nReplaceMap.end() ) {
10376 n = (*nnIt).second;
10380 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10381 // elemIDsToRemove.push_back( e->GetID() );
10385 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10386 aMesh->RemoveElement( e );
10388 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10390 AddToSameGroups( newElem, e, aMesh );
10391 if ( int aShapeId = e->getshapeId() )
10392 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10398 Remove( nodeIDsToRemove, true );
10403 //================================================================================
10405 * \brief Find corresponding nodes in two sets of faces
10406 * \param theSide1 - first face set
10407 * \param theSide2 - second first face
10408 * \param theFirstNode1 - a boundary node of set 1
10409 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10410 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10411 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10412 * \param nReplaceMap - output map of corresponding nodes
10413 * \return bool - is a success or not
10415 //================================================================================
10418 //#define DEBUG_MATCHING_NODES
10421 SMESH_MeshEditor::Sew_Error
10422 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10423 set<const SMDS_MeshElement*>& theSide2,
10424 const SMDS_MeshNode* theFirstNode1,
10425 const SMDS_MeshNode* theFirstNode2,
10426 const SMDS_MeshNode* theSecondNode1,
10427 const SMDS_MeshNode* theSecondNode2,
10428 TNodeNodeMap & nReplaceMap)
10430 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10432 nReplaceMap.clear();
10433 if ( theFirstNode1 != theFirstNode2 )
10434 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10435 if ( theSecondNode1 != theSecondNode2 )
10436 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10438 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10439 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10441 list< NLink > linkList[2];
10442 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10443 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10445 // loop on links in linkList; find faces by links and append links
10446 // of the found faces to linkList
10447 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10448 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10449 NLink link[] = { *linkIt[0], *linkIt[1] };
10450 if ( linkSet.find( link[0] ) == linkSet.end() )
10453 // by links, find faces in the face sets,
10454 // and find indices of link nodes in the found faces;
10455 // in a face set, there is only one or no face sharing a link
10456 // ---------------------------------------------------------------
10458 const SMDS_MeshElement* face[] = { 0, 0 };
10459 list<const SMDS_MeshNode*> notLinkNodes[2];
10460 //bool reverse[] = { false, false }; // order of notLinkNodes
10462 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10464 const SMDS_MeshNode* n1 = link[iSide].first;
10465 const SMDS_MeshNode* n2 = link[iSide].second;
10466 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10467 set< const SMDS_MeshElement* > facesOfNode1;
10468 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10470 // during a loop of the first node, we find all faces around n1,
10471 // during a loop of the second node, we find one face sharing both n1 and n2
10472 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10473 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10474 while ( fIt->more() ) { // loop on faces sharing a node
10475 const SMDS_MeshElement* f = fIt->next();
10476 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10477 ! facesOfNode1.insert( f ).second ) // f encounters twice
10479 if ( face[ iSide ] ) {
10480 MESSAGE( "2 faces per link " );
10481 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10484 faceSet->erase( f );
10486 // get not link nodes
10487 int nbN = f->NbNodes();
10488 if ( f->IsQuadratic() )
10490 nbNodes[ iSide ] = nbN;
10491 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10492 int i1 = f->GetNodeIndex( n1 );
10493 int i2 = f->GetNodeIndex( n2 );
10494 int iEnd = nbN, iBeg = -1, iDelta = 1;
10495 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10497 std::swap( iEnd, iBeg ); iDelta = -1;
10502 if ( i == iEnd ) i = iBeg + iDelta;
10503 if ( i == i1 ) break;
10504 nodes.push_back ( f->GetNode( i ) );
10510 // check similarity of elements of the sides
10511 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10512 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10513 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10514 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10517 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10521 // set nodes to merge
10522 // -------------------
10524 if ( face[0] && face[1] ) {
10525 if ( nbNodes[0] != nbNodes[1] ) {
10526 MESSAGE("Diff nb of face nodes");
10527 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10529 #ifdef DEBUG_MATCHING_NODES
10530 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10531 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10532 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10534 int nbN = nbNodes[0];
10536 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10537 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10538 for ( int i = 0 ; i < nbN - 2; ++i ) {
10539 #ifdef DEBUG_MATCHING_NODES
10540 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10542 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10546 // add other links of the face 1 to linkList
10547 // -----------------------------------------
10549 const SMDS_MeshElement* f0 = face[0];
10550 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10551 for ( int i = 0; i < nbN; i++ )
10553 const SMDS_MeshNode* n2 = f0->GetNode( i );
10554 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10555 linkSet.insert( SMESH_TLink( n1, n2 ));
10556 if ( !iter_isnew.second ) { // already in a set: no need to process
10557 linkSet.erase( iter_isnew.first );
10559 else // new in set == encountered for the first time: add
10561 #ifdef DEBUG_MATCHING_NODES
10562 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10563 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10565 linkList[0].push_back ( NLink( n1, n2 ));
10566 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10571 } // loop on link lists
10576 //================================================================================
10578 * \brief Create elements equal (on same nodes) to given ones
10579 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10580 * elements of the uppest dimension are duplicated.
10582 //================================================================================
10584 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10586 ClearLastCreated();
10587 SMESHDS_Mesh* mesh = GetMeshDS();
10589 // get an element type and an iterator over elements
10591 SMDSAbs_ElementType type;
10592 SMDS_ElemIteratorPtr elemIt;
10593 vector< const SMDS_MeshElement* > allElems;
10594 if ( theElements.empty() )
10596 if ( mesh->NbNodes() == 0 )
10598 // get most complex type
10599 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10600 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10601 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10603 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10604 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10609 // put all elements in the vector <allElems>
10610 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10611 elemIt = mesh->elementsIterator( type );
10612 while ( elemIt->more() )
10613 allElems.push_back( elemIt->next());
10614 elemIt = elemSetIterator( allElems );
10618 type = (*theElements.begin())->GetType();
10619 elemIt = elemSetIterator( theElements );
10622 // duplicate elements
10624 ElemFeatures elemType;
10626 vector< const SMDS_MeshNode* > nodes;
10627 while ( elemIt->more() )
10629 const SMDS_MeshElement* elem = elemIt->next();
10630 if ( elem->GetType() != type )
10633 elemType.Init( elem, /*basicOnly=*/false );
10634 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10636 AddElement( nodes, elemType );
10640 //================================================================================
10642 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10643 \param theElems - the list of elements (edges or faces) to be replicated
10644 The nodes for duplication could be found from these elements
10645 \param theNodesNot - list of nodes to NOT replicate
10646 \param theAffectedElems - the list of elements (cells and edges) to which the
10647 replicated nodes should be associated to.
10648 \return TRUE if operation has been completed successfully, FALSE otherwise
10650 //================================================================================
10652 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10653 const TIDSortedElemSet& theNodesNot,
10654 const TIDSortedElemSet& theAffectedElems )
10656 myLastCreatedElems.Clear();
10657 myLastCreatedNodes.Clear();
10659 if ( theElems.size() == 0 )
10662 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10667 TNodeNodeMap anOldNodeToNewNode;
10668 // duplicate elements and nodes
10669 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10670 // replce nodes by duplications
10671 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10675 //================================================================================
10677 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10678 \param theMeshDS - mesh instance
10679 \param theElems - the elements replicated or modified (nodes should be changed)
10680 \param theNodesNot - nodes to NOT replicate
10681 \param theNodeNodeMap - relation of old node to new created node
10682 \param theIsDoubleElem - flag os to replicate element or modify
10683 \return TRUE if operation has been completed successfully, FALSE otherwise
10685 //================================================================================
10687 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10688 const TIDSortedElemSet& theElems,
10689 const TIDSortedElemSet& theNodesNot,
10690 TNodeNodeMap& theNodeNodeMap,
10691 const bool theIsDoubleElem )
10693 MESSAGE("doubleNodes");
10694 // iterate through element and duplicate them (by nodes duplication)
10696 std::vector<const SMDS_MeshNode*> newNodes;
10697 ElemFeatures elemType;
10699 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10700 for ( ; elemItr != theElems.end(); ++elemItr )
10702 const SMDS_MeshElement* anElem = *elemItr;
10706 // duplicate nodes to duplicate element
10707 bool isDuplicate = false;
10708 newNodes.resize( anElem->NbNodes() );
10709 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10711 while ( anIter->more() )
10713 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10714 const SMDS_MeshNode* aNewNode = aCurrNode;
10715 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10716 if ( n2n != theNodeNodeMap.end() )
10718 aNewNode = n2n->second;
10720 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10723 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10724 copyPosition( aCurrNode, aNewNode );
10725 theNodeNodeMap[ aCurrNode ] = aNewNode;
10726 myLastCreatedNodes.Append( aNewNode );
10728 isDuplicate |= (aCurrNode != aNewNode);
10729 newNodes[ ind++ ] = aNewNode;
10731 if ( !isDuplicate )
10734 if ( theIsDoubleElem )
10735 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10737 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10744 //================================================================================
10746 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10747 \param theNodes - identifiers of nodes to be doubled
10748 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10749 nodes. If list of element identifiers is empty then nodes are doubled but
10750 they not assigned to elements
10751 \return TRUE if operation has been completed successfully, FALSE otherwise
10753 //================================================================================
10755 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10756 const std::list< int >& theListOfModifiedElems )
10758 MESSAGE("DoubleNodes");
10759 myLastCreatedElems.Clear();
10760 myLastCreatedNodes.Clear();
10762 if ( theListOfNodes.size() == 0 )
10765 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10769 // iterate through nodes and duplicate them
10771 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10773 std::list< int >::const_iterator aNodeIter;
10774 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10776 int aCurr = *aNodeIter;
10777 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10783 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10786 copyPosition( aNode, aNewNode );
10787 anOldNodeToNewNode[ aNode ] = aNewNode;
10788 myLastCreatedNodes.Append( aNewNode );
10792 // Create map of new nodes for modified elements
10794 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10796 std::list< int >::const_iterator anElemIter;
10797 for ( anElemIter = theListOfModifiedElems.begin();
10798 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10800 int aCurr = *anElemIter;
10801 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10805 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10807 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10809 while ( anIter->more() )
10811 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10812 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10814 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10815 aNodeArr[ ind++ ] = aNewNode;
10818 aNodeArr[ ind++ ] = aCurrNode;
10820 anElemToNodes[ anElem ] = aNodeArr;
10823 // Change nodes of elements
10825 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10826 anElemToNodesIter = anElemToNodes.begin();
10827 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10829 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10830 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10833 MESSAGE("ChangeElementNodes");
10834 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10843 //================================================================================
10845 \brief Check if element located inside shape
10846 \return TRUE if IN or ON shape, FALSE otherwise
10848 //================================================================================
10850 template<class Classifier>
10851 bool isInside(const SMDS_MeshElement* theElem,
10852 Classifier& theClassifier,
10853 const double theTol)
10855 gp_XYZ centerXYZ (0, 0, 0);
10856 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10857 while (aNodeItr->more())
10858 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10860 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10861 theClassifier.Perform(aPnt, theTol);
10862 TopAbs_State aState = theClassifier.State();
10863 return (aState == TopAbs_IN || aState == TopAbs_ON );
10866 //================================================================================
10868 * \brief Classifier of the 3D point on the TopoDS_Face
10869 * with interaface suitable for isInside()
10871 //================================================================================
10873 struct _FaceClassifier
10875 Extrema_ExtPS _extremum;
10876 BRepAdaptor_Surface _surface;
10877 TopAbs_State _state;
10879 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10881 _extremum.Initialize( _surface,
10882 _surface.FirstUParameter(), _surface.LastUParameter(),
10883 _surface.FirstVParameter(), _surface.LastVParameter(),
10884 _surface.Tolerance(), _surface.Tolerance() );
10886 void Perform(const gp_Pnt& aPnt, double theTol)
10889 _state = TopAbs_OUT;
10890 _extremum.Perform(aPnt);
10891 if ( _extremum.IsDone() )
10892 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10893 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10895 TopAbs_State State() const
10902 //================================================================================
10904 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10905 This method is the first step of DoubleNodeElemGroupsInRegion.
10906 \param theElems - list of groups of elements (edges or faces) to be replicated
10907 \param theNodesNot - list of groups of nodes not to replicated
10908 \param theShape - shape to detect affected elements (element which geometric center
10909 located on or inside shape). If the shape is null, detection is done on faces orientations
10910 (select elements with a gravity center on the side given by faces normals).
10911 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10912 The replicated nodes should be associated to affected elements.
10913 \return groups of affected elements
10914 \sa DoubleNodeElemGroupsInRegion()
10916 //================================================================================
10918 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10919 const TIDSortedElemSet& theNodesNot,
10920 const TopoDS_Shape& theShape,
10921 TIDSortedElemSet& theAffectedElems)
10923 if ( theShape.IsNull() )
10925 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10926 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10927 std::set<const SMDS_MeshElement*> edgesToCheck;
10928 alreadyCheckedNodes.clear();
10929 alreadyCheckedElems.clear();
10930 edgesToCheck.clear();
10932 // --- iterates on elements to be replicated and get elements by back references from their nodes
10934 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10936 for ( ielem=1; elemItr != theElems.end(); ++elemItr )
10938 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10939 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10942 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10943 MESSAGE("element " << ielem++ << " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10944 std::set<const SMDS_MeshNode*> nodesElem;
10946 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10947 while ( nodeItr->more() )
10949 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10950 nodesElem.insert(aNode);
10952 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10953 for (; nodit != nodesElem.end(); nodit++)
10955 MESSAGE(" noeud ");
10956 const SMDS_MeshNode* aNode = *nodit;
10957 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10959 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10961 alreadyCheckedNodes.insert(aNode);
10962 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10963 while ( backElemItr->more() )
10965 MESSAGE(" backelem ");
10966 const SMDS_MeshElement* curElem = backElemItr->next();
10967 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10969 if (theElems.find(curElem) != theElems.end())
10971 alreadyCheckedElems.insert(curElem);
10972 double x=0, y=0, z=0;
10974 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10975 while ( nodeItr2->more() )
10977 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10978 x += anotherNode->X();
10979 y += anotherNode->Y();
10980 z += anotherNode->Z();
10984 p.SetCoord( x/nb -aNode->X(),
10986 z/nb -aNode->Z() );
10987 MESSAGE(" check " << p.X() << " " << p.Y() << " " << p.Z());
10990 MESSAGE(" --- inserted")
10991 theAffectedElems.insert( curElem );
10993 else if (curElem->GetType() == SMDSAbs_Edge)
10994 edgesToCheck.insert(curElem);
10998 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10999 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
11000 for( ; eit != edgesToCheck.end(); eit++)
11002 bool onside = true;
11003 const SMDS_MeshElement* anEdge = *eit;
11004 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
11005 while ( nodeItr->more() )
11007 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11008 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
11016 MESSAGE(" --- edge onside inserted")
11017 theAffectedElems.insert(anEdge);
11023 const double aTol = Precision::Confusion();
11024 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11025 auto_ptr<_FaceClassifier> aFaceClassifier;
11026 if ( theShape.ShapeType() == TopAbs_SOLID )
11028 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11029 bsc3d->PerformInfinitePoint(aTol);
11031 else if (theShape.ShapeType() == TopAbs_FACE )
11033 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11036 // iterates on indicated elements and get elements by back references from their nodes
11037 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11039 for ( ielem = 1; elemItr != theElems.end(); ++elemItr )
11041 MESSAGE("element " << ielem++);
11042 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11045 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11046 while ( nodeItr->more() )
11048 MESSAGE(" noeud ");
11049 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11050 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11052 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11053 while ( backElemItr->more() )
11055 MESSAGE(" backelem ");
11056 const SMDS_MeshElement* curElem = backElemItr->next();
11057 if ( curElem && theElems.find(curElem) == theElems.end() &&
11059 isInside( curElem, *bsc3d, aTol ) :
11060 isInside( curElem, *aFaceClassifier, aTol )))
11061 theAffectedElems.insert( curElem );
11069 //================================================================================
11071 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11072 \param theElems - group of of elements (edges or faces) to be replicated
11073 \param theNodesNot - group of nodes not to replicate
11074 \param theShape - shape to detect affected elements (element which geometric center
11075 located on or inside shape).
11076 The replicated nodes should be associated to affected elements.
11077 \return TRUE if operation has been completed successfully, FALSE otherwise
11079 //================================================================================
11081 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11082 const TIDSortedElemSet& theNodesNot,
11083 const TopoDS_Shape& theShape )
11085 if ( theShape.IsNull() )
11088 const double aTol = Precision::Confusion();
11089 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11090 auto_ptr<_FaceClassifier> aFaceClassifier;
11091 if ( theShape.ShapeType() == TopAbs_SOLID )
11093 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11094 bsc3d->PerformInfinitePoint(aTol);
11096 else if (theShape.ShapeType() == TopAbs_FACE )
11098 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11101 // iterates on indicated elements and get elements by back references from their nodes
11102 TIDSortedElemSet anAffected;
11103 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11104 for ( ; elemItr != theElems.end(); ++elemItr )
11106 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11110 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11111 while ( nodeItr->more() )
11113 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11114 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11116 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11117 while ( backElemItr->more() )
11119 const SMDS_MeshElement* curElem = backElemItr->next();
11120 if ( curElem && theElems.find(curElem) == theElems.end() &&
11122 isInside( curElem, *bsc3d, aTol ) :
11123 isInside( curElem, *aFaceClassifier, aTol )))
11124 anAffected.insert( curElem );
11128 return DoubleNodes( theElems, theNodesNot, anAffected );
11132 * \brief compute an oriented angle between two planes defined by four points.
11133 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11134 * @param p0 base of the rotation axe
11135 * @param p1 extremity of the rotation axe
11136 * @param g1 belongs to the first plane
11137 * @param g2 belongs to the second plane
11139 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11141 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11142 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11143 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11144 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11145 gp_Vec vref(p0, p1);
11148 gp_Vec n1 = vref.Crossed(v1);
11149 gp_Vec n2 = vref.Crossed(v2);
11151 return n2.AngleWithRef(n1, vref);
11153 catch ( Standard_Failure ) {
11155 return Max( v1.Magnitude(), v2.Magnitude() );
11159 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11160 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11161 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11162 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11163 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11164 * 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.
11165 * 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.
11166 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11167 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11168 * \param theElems - list of groups of volumes, where a group of volume is a set of
11169 * SMDS_MeshElements sorted by Id.
11170 * \param createJointElems - if TRUE, create the elements
11171 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11172 * the boundary between \a theDomains and the rest mesh
11173 * \return TRUE if operation has been completed successfully, FALSE otherwise
11175 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11176 bool createJointElems,
11177 bool onAllBoundaries)
11179 MESSAGE("----------------------------------------------");
11180 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11181 MESSAGE("----------------------------------------------");
11183 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11184 meshDS->BuildDownWardConnectivity(true);
11186 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11188 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11189 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11190 // build the list of nodes shared by 2 or more domains, with their domain indexes
11192 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11193 std::map<int,int>celldom; // cell vtkId --> domain
11194 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11195 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11196 faceDomains.clear();
11198 cellDomains.clear();
11199 nodeDomains.clear();
11200 std::map<int,int> emptyMap;
11201 std::set<int> emptySet;
11204 MESSAGE(".. Number of domains :"<<theElems.size());
11206 TIDSortedElemSet theRestDomElems;
11207 const int iRestDom = -1;
11208 const int idom0 = onAllBoundaries ? iRestDom : 0;
11209 const int nbDomains = theElems.size();
11211 // Check if the domains do not share an element
11212 for (int idom = 0; idom < nbDomains-1; idom++)
11214 // MESSAGE("... Check of domain #" << idom);
11215 const TIDSortedElemSet& domain = theElems[idom];
11216 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11217 for (; elemItr != domain.end(); ++elemItr)
11219 const SMDS_MeshElement* anElem = *elemItr;
11220 int idombisdeb = idom + 1 ;
11221 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
11223 const TIDSortedElemSet& domainbis = theElems[idombis];
11224 if ( domainbis.count(anElem) )
11226 MESSAGE(".... Domain #" << idom);
11227 MESSAGE(".... Domain #" << idombis);
11228 throw SALOME_Exception("The domains are not disjoint.");
11235 for (int idom = 0; idom < nbDomains; idom++)
11238 // --- build a map (face to duplicate --> volume to modify)
11239 // with all the faces shared by 2 domains (group of elements)
11240 // and corresponding volume of this domain, for each shared face.
11241 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11243 MESSAGE("... Neighbors of domain #" << idom);
11244 const TIDSortedElemSet& domain = theElems[idom];
11245 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11246 for (; elemItr != domain.end(); ++elemItr)
11248 const SMDS_MeshElement* anElem = *elemItr;
11251 int vtkId = anElem->getVtkId();
11252 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11253 int neighborsVtkIds[NBMAXNEIGHBORS];
11254 int downIds[NBMAXNEIGHBORS];
11255 unsigned char downTypes[NBMAXNEIGHBORS];
11256 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11257 for (int n = 0; n < nbNeighbors; n++)
11259 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11260 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11261 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11264 for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11266 // MESSAGE("Domain " << idombis);
11267 const TIDSortedElemSet& domainbis = theElems[idombis];
11268 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11270 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11272 DownIdType face(downIds[n], downTypes[n]);
11273 if (!faceDomains[face].count(idom))
11275 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11276 celldom[vtkId] = idom;
11277 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11281 theRestDomElems.insert( elem );
11282 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11283 celldom[neighborsVtkIds[n]] = iRestDom;
11291 //MESSAGE("Number of shared faces " << faceDomains.size());
11292 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11294 // --- explore the shared faces domain by domain,
11295 // explore the nodes of the face and see if they belong to a cell in the domain,
11296 // which has only a node or an edge on the border (not a shared face)
11298 for (int idomain = idom0; idomain < nbDomains; idomain++)
11300 //MESSAGE("Domain " << idomain);
11301 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11302 itface = faceDomains.begin();
11303 for (; itface != faceDomains.end(); ++itface)
11305 const std::map<int, int>& domvol = itface->second;
11306 if (!domvol.count(idomain))
11308 DownIdType face = itface->first;
11309 //MESSAGE(" --- face " << face.cellId);
11310 std::set<int> oldNodes;
11312 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11313 std::set<int>::iterator itn = oldNodes.begin();
11314 for (; itn != oldNodes.end(); ++itn)
11317 //MESSAGE(" node " << oldId);
11318 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11319 for (int i=0; i<l.ncells; i++)
11321 int vtkId = l.cells[i];
11322 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11323 if (!domain.count(anElem))
11325 int vtkType = grid->GetCellType(vtkId);
11326 int downId = grid->CellIdToDownId(vtkId);
11329 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11330 continue; // not OK at this stage of the algorithm:
11331 //no cells created after BuildDownWardConnectivity
11333 DownIdType aCell(downId, vtkType);
11334 cellDomains[aCell][idomain] = vtkId;
11335 celldom[vtkId] = idomain;
11336 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11342 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11343 // for each shared face, get the nodes
11344 // for each node, for each domain of the face, create a clone of the node
11346 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11347 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11348 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11350 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11351 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11352 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11354 MESSAGE(".. Duplication of the nodes");
11355 for (int idomain = idom0; idomain < nbDomains; idomain++)
11357 itface = faceDomains.begin();
11358 for (; itface != faceDomains.end(); ++itface)
11360 const std::map<int, int>& domvol = itface->second;
11361 if (!domvol.count(idomain))
11363 DownIdType face = itface->first;
11364 //MESSAGE(" --- face " << face.cellId);
11365 std::set<int> oldNodes;
11367 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11368 std::set<int>::iterator itn = oldNodes.begin();
11369 for (; itn != oldNodes.end(); ++itn)
11372 if (nodeDomains[oldId].empty())
11374 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11375 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11377 std::map<int, int>::const_iterator itdom = domvol.begin();
11378 for (; itdom != domvol.end(); ++itdom)
11380 int idom = itdom->first;
11381 //MESSAGE(" domain " << idom);
11382 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11384 if (nodeDomains[oldId].size() >= 2) // a multiple node
11386 vector<int> orderedDoms;
11387 //MESSAGE("multiple node " << oldId);
11388 if (mutipleNodes.count(oldId))
11389 orderedDoms = mutipleNodes[oldId];
11392 map<int,int>::iterator it = nodeDomains[oldId].begin();
11393 for (; it != nodeDomains[oldId].end(); ++it)
11394 orderedDoms.push_back(it->first);
11396 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11397 //stringstream txt;
11398 //for (int i=0; i<orderedDoms.size(); i++)
11399 // txt << orderedDoms[i] << " ";
11400 //MESSAGE("orderedDoms " << txt.str());
11401 mutipleNodes[oldId] = orderedDoms;
11403 double *coords = grid->GetPoint(oldId);
11404 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11405 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11406 int newId = newNode->getVtkId();
11407 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11408 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11415 MESSAGE(".. Creation of elements");
11416 for (int idomain = idom0; idomain < nbDomains; idomain++)
11418 itface = faceDomains.begin();
11419 for (; itface != faceDomains.end(); ++itface)
11421 std::map<int, int> domvol = itface->second;
11422 if (!domvol.count(idomain))
11424 DownIdType face = itface->first;
11425 //MESSAGE(" --- face " << face.cellId);
11426 std::set<int> oldNodes;
11428 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11429 int nbMultipleNodes = 0;
11430 std::set<int>::iterator itn = oldNodes.begin();
11431 for (; itn != oldNodes.end(); ++itn)
11434 if (mutipleNodes.count(oldId))
11437 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11439 //MESSAGE("multiple Nodes detected on a shared face");
11440 int downId = itface->first.cellId;
11441 unsigned char cellType = itface->first.cellType;
11442 // --- shared edge or shared face ?
11443 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11446 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11447 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11448 if (mutipleNodes.count(nodes[i]))
11449 if (!mutipleNodesToFace.count(nodes[i]))
11450 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11452 else // shared face (between two volumes)
11454 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11455 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11456 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11457 for (int ie =0; ie < nbEdges; ie++)
11460 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11461 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11463 vector<int> vn0 = mutipleNodes[nodes[0]];
11464 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11466 for (int i0 = 0; i0 < vn0.size(); i0++)
11467 for (int i1 = 0; i1 < vn1.size(); i1++)
11468 if (vn0[i0] == vn1[i1])
11469 doms.push_back(vn0[i0]);
11470 if (doms.size() >2)
11472 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11473 double *coords = grid->GetPoint(nodes[0]);
11474 gp_Pnt p0(coords[0], coords[1], coords[2]);
11475 coords = grid->GetPoint(nodes[nbNodes - 1]);
11476 gp_Pnt p1(coords[0], coords[1], coords[2]);
11478 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11479 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11480 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11481 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11482 for (int id=0; id < doms.size(); id++)
11484 int idom = doms[id];
11485 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11486 for (int ivol=0; ivol<nbvol; ivol++)
11488 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11489 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11490 if (domain.count(elem))
11492 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11493 domvol[idom] = svol;
11494 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11496 vtkIdType npts = 0;
11497 vtkIdType* pts = 0;
11498 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11499 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11502 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11503 angleDom[idom] = 0;
11507 gp_Pnt g(values[0], values[1], values[2]);
11508 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11509 //MESSAGE(" angle=" << angleDom[idom]);
11515 map<double, int> sortedDom; // sort domains by angle
11516 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11517 sortedDom[ia->second] = ia->first;
11518 vector<int> vnodes;
11520 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11522 vdom.push_back(ib->second);
11523 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11525 for (int ino = 0; ino < nbNodes; ino++)
11526 vnodes.push_back(nodes[ino]);
11527 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11536 // --- iterate on shared faces (volumes to modify, face to extrude)
11537 // get node id's of the face (id SMDS = id VTK)
11538 // create flat element with old and new nodes if requested
11540 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11541 // (domain1 X domain2) = domain1 + MAXINT*domain2
11543 std::map<int, std::map<long,int> > nodeQuadDomains;
11544 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11546 MESSAGE(".. Creation of elements: simple junction");
11547 if (createJointElems)
11550 string joints2DName = "joints2D";
11551 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11552 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11553 string joints3DName = "joints3D";
11554 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11555 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11557 itface = faceDomains.begin();
11558 for (; itface != faceDomains.end(); ++itface)
11560 DownIdType face = itface->first;
11561 std::set<int> oldNodes;
11562 std::set<int>::iterator itn;
11564 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11566 std::map<int, int> domvol = itface->second;
11567 std::map<int, int>::iterator itdom = domvol.begin();
11568 int dom1 = itdom->first;
11569 int vtkVolId = itdom->second;
11571 int dom2 = itdom->first;
11572 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11574 stringstream grpname;
11577 grpname << dom1 << "_" << dom2;
11579 grpname << dom2 << "_" << dom1;
11580 string namegrp = grpname.str();
11581 if (!mapOfJunctionGroups.count(namegrp))
11582 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11583 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11585 sgrp->Add(vol->GetID());
11586 if (vol->GetType() == SMDSAbs_Volume)
11587 joints3DGrp->Add(vol->GetID());
11588 else if (vol->GetType() == SMDSAbs_Face)
11589 joints2DGrp->Add(vol->GetID());
11593 // --- create volumes on multiple domain intersection if requested
11594 // iterate on mutipleNodesToFace
11595 // iterate on edgesMultiDomains
11597 MESSAGE(".. Creation of elements: multiple junction");
11598 if (createJointElems)
11600 // --- iterate on mutipleNodesToFace
11602 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11603 for (; itn != mutipleNodesToFace.end(); ++itn)
11605 int node = itn->first;
11606 vector<int> orderDom = itn->second;
11607 vector<vtkIdType> orderedNodes;
11608 for (int idom = 0; idom <orderDom.size(); idom++)
11609 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11610 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11612 stringstream grpname;
11614 grpname << 0 << "_" << 0;
11616 string namegrp = grpname.str();
11617 if (!mapOfJunctionGroups.count(namegrp))
11618 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11619 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11621 sgrp->Add(face->GetID());
11624 // --- iterate on edgesMultiDomains
11626 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11627 for (; ite != edgesMultiDomains.end(); ++ite)
11629 vector<int> nodes = ite->first;
11630 vector<int> orderDom = ite->second;
11631 vector<vtkIdType> orderedNodes;
11632 if (nodes.size() == 2)
11634 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11635 for (int ino=0; ino < nodes.size(); ino++)
11636 if (orderDom.size() == 3)
11637 for (int idom = 0; idom <orderDom.size(); idom++)
11638 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11640 for (int idom = orderDom.size()-1; idom >=0; idom--)
11641 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11642 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11645 string namegrp = "jointsMultiples";
11646 if (!mapOfJunctionGroups.count(namegrp))
11647 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11648 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11650 sgrp->Add(vol->GetID());
11654 INFOS("Quadratic multiple joints not implemented");
11655 // TODO quadratic nodes
11660 // --- list the explicit faces and edges of the mesh that need to be modified,
11661 // i.e. faces and edges built with one or more duplicated nodes.
11662 // associate these faces or edges to their corresponding domain.
11663 // only the first domain found is kept when a face or edge is shared
11665 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11666 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11667 faceOrEdgeDom.clear();
11670 MESSAGE(".. Modification of elements");
11671 for (int idomain = idom0; idomain < nbDomains; idomain++)
11673 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11674 for (; itnod != nodeDomains.end(); ++itnod)
11676 int oldId = itnod->first;
11677 //MESSAGE(" node " << oldId);
11678 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11679 for (int i = 0; i < l.ncells; i++)
11681 int vtkId = l.cells[i];
11682 int vtkType = grid->GetCellType(vtkId);
11683 int downId = grid->CellIdToDownId(vtkId);
11685 continue; // new cells: not to be modified
11686 DownIdType aCell(downId, vtkType);
11687 int volParents[1000];
11688 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11689 for (int j = 0; j < nbvol; j++)
11690 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11691 if (!feDom.count(vtkId))
11693 feDom[vtkId] = idomain;
11694 faceOrEdgeDom[aCell] = emptyMap;
11695 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11696 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11697 // << " type " << vtkType << " downId " << downId);
11703 // --- iterate on shared faces (volumes to modify, face to extrude)
11704 // get node id's of the face
11705 // replace old nodes by new nodes in volumes, and update inverse connectivity
11707 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11708 for (int m=0; m<3; m++)
11710 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11711 itface = (*amap).begin();
11712 for (; itface != (*amap).end(); ++itface)
11714 DownIdType face = itface->first;
11715 std::set<int> oldNodes;
11716 std::set<int>::iterator itn;
11718 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11719 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11720 std::map<int, int> localClonedNodeIds;
11722 std::map<int, int> domvol = itface->second;
11723 std::map<int, int>::iterator itdom = domvol.begin();
11724 for (; itdom != domvol.end(); ++itdom)
11726 int idom = itdom->first;
11727 int vtkVolId = itdom->second;
11728 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11729 localClonedNodeIds.clear();
11730 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11733 if (nodeDomains[oldId].count(idom))
11735 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11736 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11739 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11744 // Remove empty groups (issue 0022812)
11745 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11746 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11748 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11749 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11752 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11753 grid->BuildLinks();
11761 * \brief Double nodes on some external faces and create flat elements.
11762 * Flat elements are mainly used by some types of mechanic calculations.
11764 * Each group of the list must be constituted of faces.
11765 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11766 * @param theElems - list of groups of faces, where a group of faces is a set of
11767 * SMDS_MeshElements sorted by Id.
11768 * @return TRUE if operation has been completed successfully, FALSE otherwise
11770 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11772 MESSAGE("-------------------------------------------------");
11773 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11774 MESSAGE("-------------------------------------------------");
11776 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11778 // --- For each group of faces
11779 // duplicate the nodes, create a flat element based on the face
11780 // replace the nodes of the faces by their clones
11782 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11783 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11784 clonedNodes.clear();
11785 intermediateNodes.clear();
11786 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11787 mapOfJunctionGroups.clear();
11789 for (int idom = 0; idom < theElems.size(); idom++)
11791 const TIDSortedElemSet& domain = theElems[idom];
11792 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11793 for (; elemItr != domain.end(); ++elemItr)
11795 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11796 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11799 // MESSAGE("aFace=" << aFace->GetID());
11800 bool isQuad = aFace->IsQuadratic();
11801 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11803 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11805 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11806 while (nodeIt->more())
11808 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11809 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11811 ln2.push_back(node);
11813 ln0.push_back(node);
11815 const SMDS_MeshNode* clone = 0;
11816 if (!clonedNodes.count(node))
11818 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11819 copyPosition( node, clone );
11820 clonedNodes[node] = clone;
11823 clone = clonedNodes[node];
11826 ln3.push_back(clone);
11828 ln1.push_back(clone);
11830 const SMDS_MeshNode* inter = 0;
11831 if (isQuad && (!isMedium))
11833 if (!intermediateNodes.count(node))
11835 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11836 copyPosition( node, inter );
11837 intermediateNodes[node] = inter;
11840 inter = intermediateNodes[node];
11841 ln4.push_back(inter);
11845 // --- extrude the face
11847 vector<const SMDS_MeshNode*> ln;
11848 SMDS_MeshVolume* vol = 0;
11849 vtkIdType aType = aFace->GetVtkType();
11853 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11854 // MESSAGE("vol prism " << vol->GetID());
11855 ln.push_back(ln1[0]);
11856 ln.push_back(ln1[1]);
11857 ln.push_back(ln1[2]);
11860 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11861 // MESSAGE("vol hexa " << vol->GetID());
11862 ln.push_back(ln1[0]);
11863 ln.push_back(ln1[1]);
11864 ln.push_back(ln1[2]);
11865 ln.push_back(ln1[3]);
11867 case VTK_QUADRATIC_TRIANGLE:
11868 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11869 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11870 // MESSAGE("vol quad prism " << vol->GetID());
11871 ln.push_back(ln1[0]);
11872 ln.push_back(ln1[1]);
11873 ln.push_back(ln1[2]);
11874 ln.push_back(ln3[0]);
11875 ln.push_back(ln3[1]);
11876 ln.push_back(ln3[2]);
11878 case VTK_QUADRATIC_QUAD:
11879 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11880 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11881 // ln4[0], ln4[1], ln4[2], ln4[3]);
11882 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11883 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11884 ln4[0], ln4[1], ln4[2], ln4[3]);
11885 // MESSAGE("vol quad hexa " << vol->GetID());
11886 ln.push_back(ln1[0]);
11887 ln.push_back(ln1[1]);
11888 ln.push_back(ln1[2]);
11889 ln.push_back(ln1[3]);
11890 ln.push_back(ln3[0]);
11891 ln.push_back(ln3[1]);
11892 ln.push_back(ln3[2]);
11893 ln.push_back(ln3[3]);
11903 stringstream grpname;
11907 string namegrp = grpname.str();
11908 if (!mapOfJunctionGroups.count(namegrp))
11909 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11910 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11912 sgrp->Add(vol->GetID());
11915 // --- modify the face
11917 aFace->ChangeNodes(&ln[0], ln.size());
11924 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11925 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11926 * groups of faces to remove inside the object, (idem edges).
11927 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11929 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11930 const TopoDS_Shape& theShape,
11931 SMESH_NodeSearcher* theNodeSearcher,
11932 const char* groupName,
11933 std::vector<double>& nodesCoords,
11934 std::vector<std::vector<int> >& listOfListOfNodes)
11936 MESSAGE("--------------------------------");
11937 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11938 MESSAGE("--------------------------------");
11940 // --- zone of volumes to remove is given :
11941 // 1 either by a geom shape (one or more vertices) and a radius,
11942 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11943 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11944 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11945 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11946 // defined by it's name.
11948 SMESHDS_GroupBase* groupDS = 0;
11949 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11950 while ( groupIt->more() )
11953 SMESH_Group * group = groupIt->next();
11954 if ( !group ) continue;
11955 groupDS = group->GetGroupDS();
11956 if ( !groupDS || groupDS->IsEmpty() ) continue;
11957 std::string grpName = group->GetName();
11958 //MESSAGE("grpName=" << grpName);
11959 if (grpName == groupName)
11965 bool isNodeGroup = false;
11966 bool isNodeCoords = false;
11969 if (groupDS->GetType() != SMDSAbs_Node)
11971 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11974 if (nodesCoords.size() > 0)
11975 isNodeCoords = true; // a list o nodes given by their coordinates
11976 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11978 // --- define groups to build
11980 int idg; // --- group of SMDS volumes
11981 string grpvName = groupName;
11982 grpvName += "_vol";
11983 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11986 MESSAGE("group not created " << grpvName);
11989 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11991 int idgs; // --- group of SMDS faces on the skin
11992 string grpsName = groupName;
11993 grpsName += "_skin";
11994 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11997 MESSAGE("group not created " << grpsName);
12000 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12002 int idgi; // --- group of SMDS faces internal (several shapes)
12003 string grpiName = groupName;
12004 grpiName += "_internalFaces";
12005 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12008 MESSAGE("group not created " << grpiName);
12011 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12013 int idgei; // --- group of SMDS faces internal (several shapes)
12014 string grpeiName = groupName;
12015 grpeiName += "_internalEdges";
12016 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12019 MESSAGE("group not created " << grpeiName);
12022 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12024 // --- build downward connectivity
12026 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12027 meshDS->BuildDownWardConnectivity(true);
12028 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12030 // --- set of volumes detected inside
12032 std::set<int> setOfInsideVol;
12033 std::set<int> setOfVolToCheck;
12035 std::vector<gp_Pnt> gpnts;
12038 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12040 MESSAGE("group of nodes provided");
12041 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12042 while ( elemIt->more() )
12044 const SMDS_MeshElement* elem = elemIt->next();
12047 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12050 SMDS_MeshElement* vol = 0;
12051 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12052 while (volItr->more())
12054 vol = (SMDS_MeshElement*)volItr->next();
12055 setOfInsideVol.insert(vol->getVtkId());
12056 sgrp->Add(vol->GetID());
12060 else if (isNodeCoords)
12062 MESSAGE("list of nodes coordinates provided");
12065 while (i < nodesCoords.size()-2)
12067 double x = nodesCoords[i++];
12068 double y = nodesCoords[i++];
12069 double z = nodesCoords[i++];
12070 gp_Pnt p = gp_Pnt(x, y ,z);
12071 gpnts.push_back(p);
12072 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12076 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12078 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12079 TopTools_IndexedMapOfShape vertexMap;
12080 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12081 gp_Pnt p = gp_Pnt(0,0,0);
12082 if (vertexMap.Extent() < 1)
12085 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12087 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12088 p = BRep_Tool::Pnt(vertex);
12089 gpnts.push_back(p);
12090 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12094 if (gpnts.size() > 0)
12097 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12099 nodeId = startNode->GetID();
12100 MESSAGE("nodeId " << nodeId);
12102 double radius2 = radius*radius;
12103 MESSAGE("radius2 " << radius2);
12105 // --- volumes on start node
12107 setOfVolToCheck.clear();
12108 SMDS_MeshElement* startVol = 0;
12109 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12110 while (volItr->more())
12112 startVol = (SMDS_MeshElement*)volItr->next();
12113 setOfVolToCheck.insert(startVol->getVtkId());
12115 if (setOfVolToCheck.empty())
12117 MESSAGE("No volumes found");
12121 // --- starting with central volumes then their neighbors, check if they are inside
12122 // or outside the domain, until no more new neighbor volume is inside.
12123 // Fill the group of inside volumes
12125 std::map<int, double> mapOfNodeDistance2;
12126 mapOfNodeDistance2.clear();
12127 std::set<int> setOfOutsideVol;
12128 while (!setOfVolToCheck.empty())
12130 std::set<int>::iterator it = setOfVolToCheck.begin();
12132 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12133 bool volInside = false;
12134 vtkIdType npts = 0;
12135 vtkIdType* pts = 0;
12136 grid->GetCellPoints(vtkId, npts, pts);
12137 for (int i=0; i<npts; i++)
12139 double distance2 = 0;
12140 if (mapOfNodeDistance2.count(pts[i]))
12142 distance2 = mapOfNodeDistance2[pts[i]];
12143 MESSAGE("point " << pts[i] << " distance2 " << distance2);
12147 double *coords = grid->GetPoint(pts[i]);
12148 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12150 for (int j=0; j<gpnts.size(); j++)
12152 double d2 = aPoint.SquareDistance(gpnts[j]);
12153 if (d2 < distance2)
12156 if (distance2 < radius2)
12160 mapOfNodeDistance2[pts[i]] = distance2;
12161 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12163 if (distance2 < radius2)
12165 volInside = true; // one or more nodes inside the domain
12166 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12172 setOfInsideVol.insert(vtkId);
12173 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12174 int neighborsVtkIds[NBMAXNEIGHBORS];
12175 int downIds[NBMAXNEIGHBORS];
12176 unsigned char downTypes[NBMAXNEIGHBORS];
12177 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12178 for (int n = 0; n < nbNeighbors; n++)
12179 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12180 setOfVolToCheck.insert(neighborsVtkIds[n]);
12184 setOfOutsideVol.insert(vtkId);
12185 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12187 setOfVolToCheck.erase(vtkId);
12191 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12192 // If yes, add the volume to the inside set
12194 bool addedInside = true;
12195 std::set<int> setOfVolToReCheck;
12196 while (addedInside)
12198 MESSAGE(" --------------------------- re check");
12199 addedInside = false;
12200 std::set<int>::iterator itv = setOfInsideVol.begin();
12201 for (; itv != setOfInsideVol.end(); ++itv)
12204 int neighborsVtkIds[NBMAXNEIGHBORS];
12205 int downIds[NBMAXNEIGHBORS];
12206 unsigned char downTypes[NBMAXNEIGHBORS];
12207 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12208 for (int n = 0; n < nbNeighbors; n++)
12209 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12210 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12212 setOfVolToCheck = setOfVolToReCheck;
12213 setOfVolToReCheck.clear();
12214 while (!setOfVolToCheck.empty())
12216 std::set<int>::iterator it = setOfVolToCheck.begin();
12218 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12220 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12221 int countInside = 0;
12222 int neighborsVtkIds[NBMAXNEIGHBORS];
12223 int downIds[NBMAXNEIGHBORS];
12224 unsigned char downTypes[NBMAXNEIGHBORS];
12225 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12226 for (int n = 0; n < nbNeighbors; n++)
12227 if (setOfInsideVol.count(neighborsVtkIds[n]))
12229 MESSAGE("countInside " << countInside);
12230 if (countInside > 1)
12232 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12233 setOfInsideVol.insert(vtkId);
12234 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12235 addedInside = true;
12238 setOfVolToReCheck.insert(vtkId);
12240 setOfVolToCheck.erase(vtkId);
12244 // --- map of Downward faces at the boundary, inside the global volume
12245 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12246 // fill group of SMDS faces inside the volume (when several volume shapes)
12247 // fill group of SMDS faces on the skin of the global volume (if skin)
12249 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12250 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12251 std::set<int>::iterator it = setOfInsideVol.begin();
12252 for (; it != setOfInsideVol.end(); ++it)
12255 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12256 int neighborsVtkIds[NBMAXNEIGHBORS];
12257 int downIds[NBMAXNEIGHBORS];
12258 unsigned char downTypes[NBMAXNEIGHBORS];
12259 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12260 for (int n = 0; n < nbNeighbors; n++)
12262 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12263 if (neighborDim == 3)
12265 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12267 DownIdType face(downIds[n], downTypes[n]);
12268 boundaryFaces[face] = vtkId;
12270 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12271 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12272 if (vtkFaceId >= 0)
12274 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12275 // find also the smds edges on this face
12276 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12277 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12278 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12279 for (int i = 0; i < nbEdges; i++)
12281 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12282 if (vtkEdgeId >= 0)
12283 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12287 else if (neighborDim == 2) // skin of the volume
12289 DownIdType face(downIds[n], downTypes[n]);
12290 skinFaces[face] = vtkId;
12291 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12292 if (vtkFaceId >= 0)
12293 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12298 // --- identify the edges constituting the wire of each subshape on the skin
12299 // define polylines with the nodes of edges, equivalent to wires
12300 // project polylines on subshapes, and partition, to get geom faces
12302 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12303 std::set<int> emptySet;
12305 std::set<int> shapeIds;
12307 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12308 while (itelem->more())
12310 const SMDS_MeshElement *elem = itelem->next();
12311 int shapeId = elem->getshapeId();
12312 int vtkId = elem->getVtkId();
12313 if (!shapeIdToVtkIdSet.count(shapeId))
12315 shapeIdToVtkIdSet[shapeId] = emptySet;
12316 shapeIds.insert(shapeId);
12318 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12321 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12322 std::set<DownIdType, DownIdCompare> emptyEdges;
12323 emptyEdges.clear();
12325 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12326 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12328 int shapeId = itShape->first;
12329 MESSAGE(" --- Shape ID --- "<< shapeId);
12330 shapeIdToEdges[shapeId] = emptyEdges;
12332 std::vector<int> nodesEdges;
12334 std::set<int>::iterator its = itShape->second.begin();
12335 for (; its != itShape->second.end(); ++its)
12338 MESSAGE(" " << vtkId);
12339 int neighborsVtkIds[NBMAXNEIGHBORS];
12340 int downIds[NBMAXNEIGHBORS];
12341 unsigned char downTypes[NBMAXNEIGHBORS];
12342 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12343 for (int n = 0; n < nbNeighbors; n++)
12345 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12347 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12348 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12349 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12351 DownIdType edge(downIds[n], downTypes[n]);
12352 if (!shapeIdToEdges[shapeId].count(edge))
12354 shapeIdToEdges[shapeId].insert(edge);
12356 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12357 nodesEdges.push_back(vtkNodeId[0]);
12358 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12359 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12365 std::list<int> order;
12367 if (nodesEdges.size() > 0)
12369 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12370 nodesEdges[0] = -1;
12371 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12372 nodesEdges[1] = -1; // do not reuse this edge
12376 int nodeTofind = order.back(); // try first to push back
12378 for (i = 0; i<nodesEdges.size(); i++)
12379 if (nodesEdges[i] == nodeTofind)
12381 if (i == nodesEdges.size())
12382 found = false; // no follower found on back
12385 if (i%2) // odd ==> use the previous one
12386 if (nodesEdges[i-1] < 0)
12390 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12391 nodesEdges[i-1] = -1;
12393 else // even ==> use the next one
12394 if (nodesEdges[i+1] < 0)
12398 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12399 nodesEdges[i+1] = -1;
12404 // try to push front
12406 nodeTofind = order.front(); // try to push front
12407 for (i = 0; i<nodesEdges.size(); i++)
12408 if (nodesEdges[i] == nodeTofind)
12410 if (i == nodesEdges.size())
12412 found = false; // no predecessor found on front
12415 if (i%2) // odd ==> use the previous one
12416 if (nodesEdges[i-1] < 0)
12420 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12421 nodesEdges[i-1] = -1;
12423 else // even ==> use the next one
12424 if (nodesEdges[i+1] < 0)
12428 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12429 nodesEdges[i+1] = -1;
12435 std::vector<int> nodes;
12436 nodes.push_back(shapeId);
12437 std::list<int>::iterator itl = order.begin();
12438 for (; itl != order.end(); itl++)
12440 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12441 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12443 listOfListOfNodes.push_back(nodes);
12446 // partition geom faces with blocFissure
12447 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12448 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12454 //================================================================================
12456 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12457 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12458 * \return TRUE if operation has been completed successfully, FALSE otherwise
12460 //================================================================================
12462 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12464 // iterates on volume elements and detect all free faces on them
12465 SMESHDS_Mesh* aMesh = GetMeshDS();
12469 ElemFeatures faceType( SMDSAbs_Face );
12470 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12471 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12474 const SMDS_MeshVolume* volume = vIt->next();
12475 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12476 vTool.SetExternalNormal();
12477 const int iQuad = volume->IsQuadratic();
12478 faceType.SetQuad( iQuad );
12479 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12481 if (!vTool.IsFreeFace(iface))
12484 vector<const SMDS_MeshNode *> nodes;
12485 int nbFaceNodes = vTool.NbFaceNodes(iface);
12486 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12488 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12489 nodes.push_back(faceNodes[inode]);
12491 if (iQuad) // add medium nodes
12493 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12494 nodes.push_back(faceNodes[inode]);
12495 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12496 nodes.push_back(faceNodes[8]);
12498 // add new face based on volume nodes
12499 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12501 nbExisted++; // face already exsist
12505 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12510 return ( nbFree == ( nbExisted + nbCreated ));
12515 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12517 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12519 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12522 //================================================================================
12524 * \brief Creates missing boundary elements
12525 * \param elements - elements whose boundary is to be checked
12526 * \param dimension - defines type of boundary elements to create
12527 * \param group - a group to store created boundary elements in
12528 * \param targetMesh - a mesh to store created boundary elements in
12529 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12530 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12531 * boundary elements will be copied into the targetMesh
12532 * \param toAddExistingBondary - if true, not only new but also pre-existing
12533 * boundary elements will be added into the new group
12534 * \param aroundElements - if true, elements will be created on boundary of given
12535 * elements else, on boundary of the whole mesh.
12536 * \return nb of added boundary elements
12538 //================================================================================
12540 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12541 Bnd_Dimension dimension,
12542 SMESH_Group* group/*=0*/,
12543 SMESH_Mesh* targetMesh/*=0*/,
12544 bool toCopyElements/*=false*/,
12545 bool toCopyExistingBoundary/*=false*/,
12546 bool toAddExistingBondary/*= false*/,
12547 bool aroundElements/*= false*/)
12549 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12550 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12551 // hope that all elements are of the same type, do not check them all
12552 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12553 throw SALOME_Exception(LOCALIZED("wrong element type"));
12556 toCopyElements = toCopyExistingBoundary = false;
12558 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12559 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12560 int nbAddedBnd = 0;
12562 // editor adding present bnd elements and optionally holding elements to add to the group
12563 SMESH_MeshEditor* presentEditor;
12564 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12565 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12567 SMESH_MesherHelper helper( *myMesh );
12568 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12569 SMDS_VolumeTool vTool;
12570 TIDSortedElemSet avoidSet;
12571 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12574 typedef vector<const SMDS_MeshNode*> TConnectivity;
12575 TConnectivity tgtNodes;
12576 ElemFeatures elemKind( missType ), elemToCopy;
12578 SMDS_ElemIteratorPtr eIt;
12579 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12580 else eIt = elemSetIterator( elements );
12582 while (eIt->more())
12584 const SMDS_MeshElement* elem = eIt->next();
12585 const int iQuad = elem->IsQuadratic();
12586 elemKind.SetQuad( iQuad );
12588 // ------------------------------------------------------------------------------------
12589 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12590 // ------------------------------------------------------------------------------------
12591 vector<const SMDS_MeshElement*> presentBndElems;
12592 vector<TConnectivity> missingBndElems;
12593 TConnectivity nodes, elemNodes;
12594 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12596 vTool.SetExternalNormal();
12597 const SMDS_MeshElement* otherVol = 0;
12598 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12600 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12601 ( !aroundElements || elements.count( otherVol )))
12603 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12604 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12605 if ( missType == SMDSAbs_Edge ) // boundary edges
12607 nodes.resize( 2+iQuad );
12608 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12610 for ( int j = 0; j < nodes.size(); ++j )
12611 nodes[j] = nn[ i+j ];
12612 if ( const SMDS_MeshElement* edge =
12613 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12614 presentBndElems.push_back( edge );
12616 missingBndElems.push_back( nodes );
12619 else // boundary face
12622 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12623 nodes.push_back( nn[inode] ); // add corner nodes
12625 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12626 nodes.push_back( nn[inode] ); // add medium nodes
12627 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12629 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12631 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12632 SMDSAbs_Face, /*noMedium=*/false ))
12633 presentBndElems.push_back( f );
12635 missingBndElems.push_back( nodes );
12637 if ( targetMesh != myMesh )
12639 // add 1D elements on face boundary to be added to a new mesh
12640 const SMDS_MeshElement* edge;
12641 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12644 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12646 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12647 if ( edge && avoidSet.insert( edge ).second )
12648 presentBndElems.push_back( edge );
12654 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12656 avoidSet.clear(), avoidSet.insert( elem );
12657 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12658 SMDS_MeshElement::iterator() );
12659 elemNodes.push_back( elemNodes[0] );
12660 nodes.resize( 2 + iQuad );
12661 const int nbLinks = elem->NbCornerNodes();
12662 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12664 nodes[0] = elemNodes[iN];
12665 nodes[1] = elemNodes[iN+1+iQuad];
12666 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12667 continue; // not free link
12669 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12670 if ( const SMDS_MeshElement* edge =
12671 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12672 presentBndElems.push_back( edge );
12674 missingBndElems.push_back( nodes );
12678 // ---------------------------------
12679 // 2. Add missing boundary elements
12680 // ---------------------------------
12681 if ( targetMesh != myMesh )
12682 // instead of making a map of nodes in this mesh and targetMesh,
12683 // we create nodes with same IDs.
12684 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12686 TConnectivity& srcNodes = missingBndElems[i];
12687 tgtNodes.resize( srcNodes.size() );
12688 for ( inode = 0; inode < srcNodes.size(); ++inode )
12689 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12690 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12692 /*noMedium=*/false))
12694 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12698 for ( int i = 0; i < missingBndElems.size(); ++i )
12700 TConnectivity& nodes = missingBndElems[i];
12701 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12703 /*noMedium=*/false))
12705 SMDS_MeshElement* newElem =
12706 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12707 nbAddedBnd += bool( newElem );
12709 // try to set a new element to a shape
12710 if ( myMesh->HasShapeToMesh() )
12713 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12714 const size_t nbN = nodes.size() / (iQuad+1 );
12715 for ( inode = 0; inode < nbN && ok; ++inode )
12717 pair<int, TopAbs_ShapeEnum> i_stype =
12718 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12719 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12720 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12722 if ( ok && mediumShapes.size() > 1 )
12724 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12725 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12726 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12728 if (( ok = ( stype_i->first != stype_i_0.first )))
12729 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12730 aMesh->IndexToShape( stype_i_0.second ));
12733 if ( ok && mediumShapes.begin()->first == missShapeType )
12734 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12738 // ----------------------------------
12739 // 3. Copy present boundary elements
12740 // ----------------------------------
12741 if ( toCopyExistingBoundary )
12742 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12744 const SMDS_MeshElement* e = presentBndElems[i];
12745 tgtNodes.resize( e->NbNodes() );
12746 for ( inode = 0; inode < nodes.size(); ++inode )
12747 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12748 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12750 else // store present elements to add them to a group
12751 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12753 presentEditor->myLastCreatedElems.Append( presentBndElems[i] );
12756 } // loop on given elements
12758 // ---------------------------------------------
12759 // 4. Fill group with boundary elements
12760 // ---------------------------------------------
12763 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12764 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12765 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12767 tgtEditor.myLastCreatedElems.Clear();
12768 tgtEditor2.myLastCreatedElems.Clear();
12770 // -----------------------
12771 // 5. Copy given elements
12772 // -----------------------
12773 if ( toCopyElements && targetMesh != myMesh )
12775 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12776 else eIt = elemSetIterator( elements );
12777 while (eIt->more())
12779 const SMDS_MeshElement* elem = eIt->next();
12780 tgtNodes.resize( elem->NbNodes() );
12781 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12782 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12783 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12785 tgtEditor.myLastCreatedElems.Clear();
12791 //================================================================================
12793 * \brief Copy node position and set \a to node on the same geometry
12795 //================================================================================
12797 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12798 const SMDS_MeshNode* to )
12800 if ( !from || !to ) return;
12802 SMDS_PositionPtr pos = from->GetPosition();
12803 if ( !pos || from->getshapeId() < 1 ) return;
12805 switch ( pos->GetTypeOfPosition() )
12807 case SMDS_TOP_3DSPACE: break;
12809 case SMDS_TOP_FACE:
12811 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12812 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12813 fPos->GetUParameter(), fPos->GetVParameter() );
12816 case SMDS_TOP_EDGE:
12818 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12819 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12820 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12823 case SMDS_TOP_VERTEX:
12825 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12828 case SMDS_TOP_UNSPEC: