1 // Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MeshAlgos.hxx"
46 #include "SMESH_MesherHelper.hxx"
47 #include "SMESH_OctreeNode.hxx"
48 #include "SMESH_subMesh.hxx"
50 #include <Basics_OCCTVersion.hxx>
52 #include "utilities.h"
55 #include <BRepAdaptor_Surface.hxx>
56 #include <BRepBuilderAPI_MakeEdge.hxx>
57 #include <BRepClass3d_SolidClassifier.hxx>
58 #include <BRep_Tool.hxx>
60 #include <Extrema_GenExtPS.hxx>
61 #include <Extrema_POnCurv.hxx>
62 #include <Extrema_POnSurf.hxx>
63 #include <Geom2d_Curve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Surface.hxx>
67 #include <Precision.hxx>
68 #include <TColStd_ListOfInteger.hxx>
69 #include <TopAbs_State.hxx>
71 #include <TopExp_Explorer.hxx>
72 #include <TopTools_ListIteratorOfListOfShape.hxx>
73 #include <TopTools_ListOfShape.hxx>
74 #include <TopTools_SequenceOfShape.hxx>
76 #include <TopoDS_Face.hxx>
77 #include <TopoDS_Solid.hxx>
83 #include <gp_Trsf.hxx>
97 #include <boost/tuple/tuple.hpp>
99 #include <Standard_Failure.hxx>
100 #include <Standard_ErrorHandler.hxx>
102 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
105 using namespace SMESH::Controls;
109 template < class ELEM_SET >
110 SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
112 typedef SMDS_SetIterator
113 < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
114 return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
118 //=======================================================================
119 //function : SMESH_MeshEditor
121 //=======================================================================
123 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
124 :myMesh( theMesh ) // theMesh may be NULL
128 //================================================================================
130 * \brief Clears myLastCreatedNodes and myLastCreatedElems
132 //================================================================================
134 void SMESH_MeshEditor::ClearLastCreated()
136 myLastCreatedNodes.Clear();
137 myLastCreatedElems.Clear();
140 //================================================================================
142 * \brief Initializes members by an existing element
143 * \param [in] elem - the source element
144 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
146 //================================================================================
148 SMESH_MeshEditor::ElemFeatures&
149 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
153 myType = elem->GetType();
154 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
156 myIsPoly = elem->IsPoly();
159 myIsQuad = elem->IsQuadratic();
160 if ( myType == SMDSAbs_Volume && !basicOnly )
162 vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
163 myPolyhedQuantities.swap( quant );
167 else if ( myType == SMDSAbs_Ball && !basicOnly )
169 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
175 //=======================================================================
179 //=======================================================================
182 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
183 const ElemFeatures& features)
185 SMDS_MeshElement* e = 0;
186 int nbnode = node.size();
187 SMESHDS_Mesh* mesh = GetMeshDS();
188 const int ID = features.myID;
190 switch ( features.myType ) {
192 if ( !features.myIsPoly ) {
194 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
195 else e = mesh->AddFace (node[0], node[1], node[2] );
197 else if (nbnode == 4) {
198 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
199 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
201 else if (nbnode == 6) {
202 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
203 node[4], node[5], ID);
204 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
207 else if (nbnode == 7) {
208 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
209 node[4], node[5], node[6], ID);
210 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
211 node[4], node[5], node[6] );
213 else if (nbnode == 8) {
214 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
215 node[4], node[5], node[6], node[7], ID);
216 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
217 node[4], node[5], node[6], node[7] );
219 else if (nbnode == 9) {
220 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
221 node[4], node[5], node[6], node[7], node[8], ID);
222 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
223 node[4], node[5], node[6], node[7], node[8] );
226 else if ( !features.myIsQuad )
228 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
229 else e = mesh->AddPolygonalFace (node );
231 else if ( nbnode % 2 == 0 ) // just a protection
233 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
234 else e = mesh->AddQuadPolygonalFace (node );
239 if ( !features.myIsPoly ) {
241 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
242 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
244 else if (nbnode == 5) {
245 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
247 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
250 else if (nbnode == 6) {
251 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
252 node[4], node[5], ID);
253 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
256 else if (nbnode == 8) {
257 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
258 node[4], node[5], node[6], node[7], ID);
259 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
260 node[4], node[5], node[6], node[7] );
262 else if (nbnode == 10) {
263 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
264 node[4], node[5], node[6], node[7],
265 node[8], node[9], ID);
266 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
267 node[4], node[5], node[6], node[7],
270 else if (nbnode == 12) {
271 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
272 node[4], node[5], node[6], node[7],
273 node[8], node[9], node[10], node[11], ID);
274 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
275 node[4], node[5], node[6], node[7],
276 node[8], node[9], node[10], node[11] );
278 else if (nbnode == 13) {
279 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
280 node[4], node[5], node[6], node[7],
281 node[8], node[9], node[10],node[11],
283 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
284 node[4], node[5], node[6], node[7],
285 node[8], node[9], node[10],node[11],
288 else if (nbnode == 15) {
289 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
290 node[4], node[5], node[6], node[7],
291 node[8], node[9], node[10],node[11],
292 node[12],node[13],node[14],ID);
293 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
294 node[4], node[5], node[6], node[7],
295 node[8], node[9], node[10],node[11],
296 node[12],node[13],node[14] );
298 else if (nbnode == 20) {
299 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
300 node[4], node[5], node[6], node[7],
301 node[8], node[9], node[10],node[11],
302 node[12],node[13],node[14],node[15],
303 node[16],node[17],node[18],node[19],ID);
304 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
305 node[4], node[5], node[6], node[7],
306 node[8], node[9], node[10],node[11],
307 node[12],node[13],node[14],node[15],
308 node[16],node[17],node[18],node[19] );
310 else if (nbnode == 27) {
311 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
312 node[4], node[5], node[6], node[7],
313 node[8], node[9], node[10],node[11],
314 node[12],node[13],node[14],node[15],
315 node[16],node[17],node[18],node[19],
316 node[20],node[21],node[22],node[23],
317 node[24],node[25],node[26], ID);
318 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
319 node[4], node[5], node[6], node[7],
320 node[8], node[9], node[10],node[11],
321 node[12],node[13],node[14],node[15],
322 node[16],node[17],node[18],node[19],
323 node[20],node[21],node[22],node[23],
324 node[24],node[25],node[26] );
327 else if ( !features.myIsQuad )
329 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
330 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
334 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
335 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
341 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
342 else e = mesh->AddEdge (node[0], node[1] );
344 else if ( nbnode == 3 ) {
345 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
346 else e = mesh->AddEdge (node[0], node[1], node[2] );
350 case SMDSAbs_0DElement:
352 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
353 else e = mesh->Add0DElement (node[0] );
358 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
359 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
363 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
364 else e = mesh->AddBall (node[0], features.myBallDiameter );
369 if ( e ) myLastCreatedElems.Append( e );
373 //=======================================================================
377 //=======================================================================
379 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
380 const ElemFeatures& features)
382 vector<const SMDS_MeshNode*> nodes;
383 nodes.reserve( nodeIDs.size() );
384 vector<int>::const_iterator id = nodeIDs.begin();
385 while ( id != nodeIDs.end() ) {
386 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
387 nodes.push_back( node );
391 return AddElement( nodes, features );
394 //=======================================================================
396 //purpose : Remove a node or an element.
397 // Modify a compute state of sub-meshes which become empty
398 //=======================================================================
400 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
403 myLastCreatedElems.Clear();
404 myLastCreatedNodes.Clear();
406 SMESHDS_Mesh* aMesh = GetMeshDS();
407 set< SMESH_subMesh *> smmap;
410 list<int>::const_iterator it = theIDs.begin();
411 for ( ; it != theIDs.end(); it++ ) {
412 const SMDS_MeshElement * elem;
414 elem = aMesh->FindNode( *it );
416 elem = aMesh->FindElement( *it );
420 // Notify VERTEX sub-meshes about modification
422 const SMDS_MeshNode* node = cast2Node( elem );
423 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
424 if ( int aShapeID = node->getshapeId() )
425 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
428 // Find sub-meshes to notify about modification
429 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
430 // while ( nodeIt->more() ) {
431 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
432 // const SMDS_PositionPtr& aPosition = node->GetPosition();
433 // if ( aPosition.get() ) {
434 // if ( int aShapeID = aPosition->GetShapeId() ) {
435 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
436 // smmap.insert( sm );
443 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
445 aMesh->RemoveElement( elem );
449 // Notify sub-meshes about modification
450 if ( !smmap.empty() ) {
451 set< SMESH_subMesh *>::iterator smIt;
452 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
453 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
456 // // Check if the whole mesh becomes empty
457 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
458 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
463 //================================================================================
465 * \brief Create 0D elements on all nodes of the given object except those
466 * nodes on which a 0D element already exists.
467 * \param elements - Elements on whose nodes to create 0D elements; if empty,
468 * the all mesh is treated
469 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
471 //================================================================================
473 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
474 TIDSortedElemSet& all0DElems )
476 SMDS_ElemIteratorPtr elemIt;
477 vector< const SMDS_MeshElement* > allNodes;
478 if ( elements.empty() )
480 allNodes.reserve( GetMeshDS()->NbNodes() );
481 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
482 while ( elemIt->more() )
483 allNodes.push_back( elemIt->next() );
485 elemIt = elemSetIterator( allNodes );
489 elemIt = elemSetIterator( elements );
492 while ( elemIt->more() )
494 const SMDS_MeshElement* e = elemIt->next();
495 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
496 while ( nodeIt->more() )
498 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
499 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
501 all0DElems.insert( it0D->next() );
503 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
504 all0DElems.insert( myLastCreatedElems.Last() );
510 //=======================================================================
511 //function : FindShape
512 //purpose : Return an index of the shape theElem is on
513 // or zero if a shape not found
514 //=======================================================================
516 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
518 myLastCreatedElems.Clear();
519 myLastCreatedNodes.Clear();
521 SMESHDS_Mesh * aMesh = GetMeshDS();
522 if ( aMesh->ShapeToMesh().IsNull() )
525 int aShapeID = theElem->getshapeId();
529 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
530 if ( sm->Contains( theElem ))
533 if ( theElem->GetType() == SMDSAbs_Node ) {
534 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
537 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
540 TopoDS_Shape aShape; // the shape a node of theElem is on
541 if ( theElem->GetType() != SMDSAbs_Node )
543 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
544 while ( nodeIt->more() ) {
545 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
546 if ((aShapeID = node->getshapeId()) > 0) {
547 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
548 if ( sm->Contains( theElem ))
550 if ( aShape.IsNull() )
551 aShape = aMesh->IndexToShape( aShapeID );
557 // None of nodes is on a proper shape,
558 // find the shape among ancestors of aShape on which a node is
559 if ( !aShape.IsNull() ) {
560 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
561 for ( ; ancIt.More(); ancIt.Next() ) {
562 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
563 if ( sm && sm->Contains( theElem ))
564 return aMesh->ShapeToIndex( ancIt.Value() );
569 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
570 while ( const SMESHDS_SubMesh* sm = smIt->next() )
571 if ( sm->Contains( theElem ))
578 //=======================================================================
579 //function : IsMedium
581 //=======================================================================
583 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
584 const SMDSAbs_ElementType typeToCheck)
586 bool isMedium = false;
587 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
588 while (it->more() && !isMedium ) {
589 const SMDS_MeshElement* elem = it->next();
590 isMedium = elem->IsMediumNode(node);
595 //=======================================================================
596 //function : shiftNodesQuadTria
597 //purpose : Shift nodes in the array corresponded to quadratic triangle
598 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
599 //=======================================================================
601 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
603 const SMDS_MeshNode* nd1 = aNodes[0];
604 aNodes[0] = aNodes[1];
605 aNodes[1] = aNodes[2];
607 const SMDS_MeshNode* nd2 = aNodes[3];
608 aNodes[3] = aNodes[4];
609 aNodes[4] = aNodes[5];
613 //=======================================================================
614 //function : nbEdgeConnectivity
615 //purpose : return number of the edges connected with the theNode.
616 // if theEdges has connections with the other type of the
617 // elements, return -1
618 //=======================================================================
620 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
622 // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
624 // while(elemIt->more()) {
629 return theNode->NbInverseElements();
632 //=======================================================================
633 //function : getNodesFromTwoTria
635 //=======================================================================
637 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
638 const SMDS_MeshElement * theTria2,
639 vector< const SMDS_MeshNode*>& N1,
640 vector< const SMDS_MeshNode*>& N2)
642 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
643 if ( N1.size() < 6 ) return false;
644 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
645 if ( N2.size() < 6 ) return false;
647 int sames[3] = {-1,-1,-1};
659 if(nbsames!=2) return false;
661 shiftNodesQuadTria(N1);
663 shiftNodesQuadTria(N1);
666 i = sames[0] + sames[1] + sames[2];
668 shiftNodesQuadTria(N2);
670 // now we receive following N1 and N2 (using numeration as in the image below)
671 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
672 // i.e. first nodes from both arrays form a new diagonal
676 //=======================================================================
677 //function : InverseDiag
678 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
679 // but having other common link.
680 // Return False if args are improper
681 //=======================================================================
683 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
684 const SMDS_MeshElement * theTria2 )
686 MESSAGE("InverseDiag");
687 myLastCreatedElems.Clear();
688 myLastCreatedNodes.Clear();
690 if (!theTria1 || !theTria2)
693 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
694 if (!F1) return false;
695 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
696 if (!F2) return false;
697 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
698 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
700 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
701 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
705 // put nodes in array and find out indices of the same ones
706 const SMDS_MeshNode* aNodes [6];
707 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
709 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
710 while ( it->more() ) {
711 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
713 if ( i > 2 ) // theTria2
714 // find same node of theTria1
715 for ( int j = 0; j < 3; j++ )
716 if ( aNodes[ i ] == aNodes[ j ]) {
725 return false; // theTria1 is not a triangle
726 it = theTria2->nodesIterator();
728 if ( i == 6 && it->more() )
729 return false; // theTria2 is not a triangle
732 // find indices of 1,2 and of A,B in theTria1
733 int iA = -1, iB = 0, i1 = 0, i2 = 0;
734 for ( i = 0; i < 6; i++ ) {
735 if ( sameInd [ i ] == -1 ) {
740 if ( iA >= 0) iB = i;
744 // nodes 1 and 2 should not be the same
745 if ( aNodes[ i1 ] == aNodes[ i2 ] )
749 aNodes[ iA ] = aNodes[ i2 ];
751 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
753 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
754 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
758 } // end if(F1 && F2)
760 // check case of quadratic faces
761 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
762 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
764 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
765 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
769 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
770 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
778 vector< const SMDS_MeshNode* > N1;
779 vector< const SMDS_MeshNode* > N2;
780 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
782 // now we receive following N1 and N2 (using numeration as above image)
783 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
784 // i.e. first nodes from both arrays determ new diagonal
786 vector< const SMDS_MeshNode*> N1new( N1.size() );
787 vector< const SMDS_MeshNode*> N2new( N2.size() );
788 N1new.back() = N1.back(); // central node of biquadratic
789 N2new.back() = N2.back();
790 N1new[0] = N1[0]; N2new[0] = N1[0];
791 N1new[1] = N2[0]; N2new[1] = N1[1];
792 N1new[2] = N2[1]; N2new[2] = N2[0];
793 N1new[3] = N1[4]; N2new[3] = N1[3];
794 N1new[4] = N2[3]; N2new[4] = N2[5];
795 N1new[5] = N1[5]; N2new[5] = N1[4];
796 // change nodes in faces
797 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
798 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
800 // move the central node of biquadratic triangle
801 SMESH_MesherHelper helper( *GetMesh() );
802 for ( int is2nd = 0; is2nd < 2; ++is2nd )
804 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
805 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
806 if ( nodes.size() < 7 )
808 helper.SetSubShape( tria->getshapeId() );
809 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
813 xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
814 SMESH_TNodeXYZ( nodes[4] ) +
815 SMESH_TNodeXYZ( nodes[5] )) / 3.;
820 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
821 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
822 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
824 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
825 xyz = S->Value( uv.X(), uv.Y() );
826 xyz.Transform( loc );
827 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
828 nodes[6]->getshapeId() > 0 )
829 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
831 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
836 //=======================================================================
837 //function : findTriangles
838 //purpose : find triangles sharing theNode1-theNode2 link
839 //=======================================================================
841 static bool findTriangles(const SMDS_MeshNode * theNode1,
842 const SMDS_MeshNode * theNode2,
843 const SMDS_MeshElement*& theTria1,
844 const SMDS_MeshElement*& theTria2)
846 if ( !theNode1 || !theNode2 ) return false;
848 theTria1 = theTria2 = 0;
850 set< const SMDS_MeshElement* > emap;
851 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
853 const SMDS_MeshElement* elem = it->next();
854 if ( elem->NbCornerNodes() == 3 )
857 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
859 const SMDS_MeshElement* elem = it->next();
860 if ( emap.count( elem )) {
868 // theTria1 must be element with minimum ID
869 if ( theTria2->GetID() < theTria1->GetID() )
870 std::swap( theTria2, theTria1 );
878 //=======================================================================
879 //function : InverseDiag
880 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
881 // with ones built on the same 4 nodes but having other common link.
882 // Return false if proper faces not found
883 //=======================================================================
885 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
886 const SMDS_MeshNode * theNode2)
888 myLastCreatedElems.Clear();
889 myLastCreatedNodes.Clear();
891 MESSAGE( "::InverseDiag()" );
893 const SMDS_MeshElement *tr1, *tr2;
894 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
897 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
898 if (!F1) return false;
899 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
900 if (!F2) return false;
901 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
902 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
904 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
905 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
909 // put nodes in array
910 // and find indices of 1,2 and of A in tr1 and of B in tr2
911 int i, iA1 = 0, i1 = 0;
912 const SMDS_MeshNode* aNodes1 [3];
913 SMDS_ElemIteratorPtr it;
914 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
915 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
916 if ( aNodes1[ i ] == theNode1 )
917 iA1 = i; // node A in tr1
918 else if ( aNodes1[ i ] != theNode2 )
922 const SMDS_MeshNode* aNodes2 [3];
923 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
924 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
925 if ( aNodes2[ i ] == theNode2 )
926 iB2 = i; // node B in tr2
927 else if ( aNodes2[ i ] != theNode1 )
931 // nodes 1 and 2 should not be the same
932 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
936 aNodes1[ iA1 ] = aNodes2[ i2 ];
938 aNodes2[ iB2 ] = aNodes1[ i1 ];
940 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
941 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
946 // check case of quadratic faces
947 return InverseDiag(tr1,tr2);
950 //=======================================================================
951 //function : getQuadrangleNodes
952 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
953 // fusion of triangles tr1 and tr2 having shared link on
954 // theNode1 and theNode2
955 //=======================================================================
957 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
958 const SMDS_MeshNode * theNode1,
959 const SMDS_MeshNode * theNode2,
960 const SMDS_MeshElement * tr1,
961 const SMDS_MeshElement * tr2 )
963 if( tr1->NbNodes() != tr2->NbNodes() )
965 // find the 4-th node to insert into tr1
966 const SMDS_MeshNode* n4 = 0;
967 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
969 while ( !n4 && i<3 ) {
970 const SMDS_MeshNode * n = cast2Node( it->next() );
972 bool isDiag = ( n == theNode1 || n == theNode2 );
976 // Make an array of nodes to be in a quadrangle
977 int iNode = 0, iFirstDiag = -1;
978 it = tr1->nodesIterator();
981 const SMDS_MeshNode * n = cast2Node( it->next() );
983 bool isDiag = ( n == theNode1 || n == theNode2 );
985 if ( iFirstDiag < 0 )
987 else if ( iNode - iFirstDiag == 1 )
988 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
990 else if ( n == n4 ) {
991 return false; // tr1 and tr2 should not have all the same nodes
993 theQuadNodes[ iNode++ ] = n;
995 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
996 theQuadNodes[ iNode ] = n4;
1001 //=======================================================================
1002 //function : DeleteDiag
1003 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
1004 // with a quadrangle built on the same 4 nodes.
1005 // Return false if proper faces not found
1006 //=======================================================================
1008 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1009 const SMDS_MeshNode * theNode2)
1011 myLastCreatedElems.Clear();
1012 myLastCreatedNodes.Clear();
1014 MESSAGE( "::DeleteDiag()" );
1016 const SMDS_MeshElement *tr1, *tr2;
1017 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1020 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1021 if (!F1) return false;
1022 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1023 if (!F2) return false;
1024 SMESHDS_Mesh * aMesh = GetMeshDS();
1026 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1027 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1029 const SMDS_MeshNode* aNodes [ 4 ];
1030 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1033 const SMDS_MeshElement* newElem = 0;
1034 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1035 myLastCreatedElems.Append(newElem);
1036 AddToSameGroups( newElem, tr1, aMesh );
1037 int aShapeId = tr1->getshapeId();
1040 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1042 aMesh->RemoveElement( tr1 );
1043 aMesh->RemoveElement( tr2 );
1048 // check case of quadratic faces
1049 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1051 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1055 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1056 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1064 vector< const SMDS_MeshNode* > N1;
1065 vector< const SMDS_MeshNode* > N2;
1066 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1068 // now we receive following N1 and N2 (using numeration as above image)
1069 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1070 // i.e. first nodes from both arrays determ new diagonal
1072 const SMDS_MeshNode* aNodes[8];
1082 const SMDS_MeshElement* newElem = 0;
1083 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1084 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1085 myLastCreatedElems.Append(newElem);
1086 AddToSameGroups( newElem, tr1, aMesh );
1087 int aShapeId = tr1->getshapeId();
1090 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1092 aMesh->RemoveElement( tr1 );
1093 aMesh->RemoveElement( tr2 );
1095 // remove middle node (9)
1096 GetMeshDS()->RemoveNode( N1[4] );
1101 //=======================================================================
1102 //function : Reorient
1103 //purpose : Reverse theElement orientation
1104 //=======================================================================
1106 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1108 MESSAGE("Reorient");
1109 myLastCreatedElems.Clear();
1110 myLastCreatedNodes.Clear();
1114 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1115 if ( !it || !it->more() )
1118 const SMDSAbs_ElementType type = theElem->GetType();
1119 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1122 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1123 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1125 const SMDS_VtkVolume* aPolyedre =
1126 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1128 MESSAGE("Warning: bad volumic element");
1131 const int nbFaces = aPolyedre->NbFaces();
1132 vector<const SMDS_MeshNode *> poly_nodes;
1133 vector<int> quantities (nbFaces);
1135 // reverse each face of the polyedre
1136 for (int iface = 1; iface <= nbFaces; iface++) {
1137 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1138 quantities[iface - 1] = nbFaceNodes;
1140 for (inode = nbFaceNodes; inode >= 1; inode--) {
1141 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1142 poly_nodes.push_back(curNode);
1145 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1147 else // other elements
1149 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1150 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1151 if ( interlace.empty() )
1153 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1157 SMDS_MeshCell::applyInterlace( interlace, nodes );
1159 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1164 //================================================================================
1166 * \brief Reorient faces.
1167 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1168 * \param theDirection - desired direction of normal of \a theFace
1169 * \param theFace - one of \a theFaces that sould be oriented according to
1170 * \a theDirection and whose orientation defines orientation of other faces
1171 * \return number of reoriented faces.
1173 //================================================================================
1175 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1176 const gp_Dir& theDirection,
1177 const SMDS_MeshElement * theFace)
1180 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1182 if ( theFaces.empty() )
1184 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1185 while ( fIt->more() )
1186 theFaces.insert( theFaces.end(), fIt->next() );
1189 // orient theFace according to theDirection
1191 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1192 if ( normal * theDirection.XYZ() < 0 )
1193 nbReori += Reorient( theFace );
1195 // Orient other faces
1197 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1198 TIDSortedElemSet avoidSet;
1199 set< SMESH_TLink > checkedLinks;
1200 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1202 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1203 theFaces.erase( theFace );
1204 startFaces.insert( theFace );
1206 int nodeInd1, nodeInd2;
1207 const SMDS_MeshElement* otherFace;
1208 vector< const SMDS_MeshElement* > facesNearLink;
1209 vector< std::pair< int, int > > nodeIndsOfFace;
1211 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1212 while ( !startFaces.empty() )
1214 startFace = startFaces.begin();
1215 theFace = *startFace;
1216 startFaces.erase( startFace );
1217 if ( !visitedFaces.insert( theFace ).second )
1221 avoidSet.insert(theFace);
1223 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1225 const int nbNodes = theFace->NbCornerNodes();
1226 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1228 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1229 linkIt_isNew = checkedLinks.insert( link );
1230 if ( !linkIt_isNew.second )
1232 // link has already been checked and won't be encountered more
1233 // if the group (theFaces) is manifold
1234 //checkedLinks.erase( linkIt_isNew.first );
1238 facesNearLink.clear();
1239 nodeIndsOfFace.clear();
1240 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1242 &nodeInd1, &nodeInd2 )))
1243 if ( otherFace != theFace)
1245 facesNearLink.push_back( otherFace );
1246 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1247 avoidSet.insert( otherFace );
1249 if ( facesNearLink.size() > 1 )
1251 // NON-MANIFOLD mesh shell !
1252 // select a face most co-directed with theFace,
1253 // other faces won't be visited this time
1255 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1256 double proj, maxProj = -1;
1257 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1258 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1259 if (( proj = Abs( NF * NOF )) > maxProj ) {
1261 otherFace = facesNearLink[i];
1262 nodeInd1 = nodeIndsOfFace[i].first;
1263 nodeInd2 = nodeIndsOfFace[i].second;
1266 // not to visit rejected faces
1267 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1268 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1269 visitedFaces.insert( facesNearLink[i] );
1271 else if ( facesNearLink.size() == 1 )
1273 otherFace = facesNearLink[0];
1274 nodeInd1 = nodeIndsOfFace.back().first;
1275 nodeInd2 = nodeIndsOfFace.back().second;
1277 if ( otherFace && otherFace != theFace)
1279 // link must be reverse in otherFace if orientation ot otherFace
1280 // is same as that of theFace
1281 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1283 nbReori += Reorient( otherFace );
1285 startFaces.insert( otherFace );
1288 std::swap( link.first, link.second ); // reverse the link
1294 //================================================================================
1296 * \brief Reorient faces basing on orientation of adjacent volumes.
1297 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1298 * \param theVolumes - reference volumes.
1299 * \param theOutsideNormal - to orient faces to have their normal
1300 * pointing either \a outside or \a inside the adjacent volumes.
1301 * \return number of reoriented faces.
1303 //================================================================================
1305 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1306 TIDSortedElemSet & theVolumes,
1307 const bool theOutsideNormal)
1311 SMDS_ElemIteratorPtr faceIt;
1312 if ( theFaces.empty() )
1313 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1315 faceIt = elemSetIterator( theFaces );
1317 vector< const SMDS_MeshNode* > faceNodes;
1318 TIDSortedElemSet checkedVolumes;
1319 set< const SMDS_MeshNode* > faceNodesSet;
1320 SMDS_VolumeTool volumeTool;
1322 while ( faceIt->more() ) // loop on given faces
1324 const SMDS_MeshElement* face = faceIt->next();
1325 if ( face->GetType() != SMDSAbs_Face )
1328 const size_t nbCornersNodes = face->NbCornerNodes();
1329 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1331 checkedVolumes.clear();
1332 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1333 while ( vIt->more() )
1335 const SMDS_MeshElement* volume = vIt->next();
1337 if ( !checkedVolumes.insert( volume ).second )
1339 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1342 // is volume adjacent?
1343 bool allNodesCommon = true;
1344 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1345 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1346 if ( !allNodesCommon )
1349 // get nodes of a corresponding volume facet
1350 faceNodesSet.clear();
1351 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1352 volumeTool.Set( volume );
1353 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1354 if ( facetID < 0 ) continue;
1355 volumeTool.SetExternalNormal();
1356 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1358 // compare order of faceNodes and facetNodes
1359 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1361 for ( int i = 0; i < 2; ++i )
1363 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1364 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1365 if ( faceNodes[ iN ] == n )
1371 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1372 if ( isOutside != theOutsideNormal )
1373 nbReori += Reorient( face );
1375 } // loop on given faces
1380 //=======================================================================
1381 //function : getBadRate
1383 //=======================================================================
1385 static double getBadRate (const SMDS_MeshElement* theElem,
1386 SMESH::Controls::NumericalFunctorPtr& theCrit)
1388 SMESH::Controls::TSequenceOfXYZ P;
1389 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1391 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1392 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1395 //=======================================================================
1396 //function : QuadToTri
1397 //purpose : Cut quadrangles into triangles.
1398 // theCrit is used to select a diagonal to cut
1399 //=======================================================================
1401 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1402 SMESH::Controls::NumericalFunctorPtr theCrit)
1404 myLastCreatedElems.Clear();
1405 myLastCreatedNodes.Clear();
1407 if ( !theCrit.get() )
1410 SMESHDS_Mesh * aMesh = GetMeshDS();
1412 Handle(Geom_Surface) surface;
1413 SMESH_MesherHelper helper( *GetMesh() );
1415 TIDSortedElemSet::iterator itElem;
1416 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1418 const SMDS_MeshElement* elem = *itElem;
1419 if ( !elem || elem->GetType() != SMDSAbs_Face )
1421 if ( elem->NbCornerNodes() != 4 )
1424 // retrieve element nodes
1425 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1427 // compare two sets of possible triangles
1428 double aBadRate1, aBadRate2; // to what extent a set is bad
1429 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1430 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1431 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1433 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1434 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1435 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1437 const int aShapeId = FindShape( elem );
1438 const SMDS_MeshElement* newElem1 = 0;
1439 const SMDS_MeshElement* newElem2 = 0;
1441 if ( !elem->IsQuadratic() ) // split liner quadrangle
1443 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1444 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1445 if ( aBadRate1 <= aBadRate2 ) {
1446 // tr1 + tr2 is better
1447 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1448 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1451 // tr3 + tr4 is better
1452 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1453 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1456 else // split quadratic quadrangle
1458 helper.SetIsQuadratic( true );
1459 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1461 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1462 if ( aNodes.size() == 9 )
1464 helper.SetIsBiQuadratic( true );
1465 if ( aBadRate1 <= aBadRate2 )
1466 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1468 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1470 // create a new element
1471 if ( aBadRate1 <= aBadRate2 ) {
1472 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1473 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1476 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1477 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1481 // care of a new element
1483 myLastCreatedElems.Append(newElem1);
1484 myLastCreatedElems.Append(newElem2);
1485 AddToSameGroups( newElem1, elem, aMesh );
1486 AddToSameGroups( newElem2, elem, aMesh );
1488 // put a new triangle on the same shape
1490 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1491 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1493 aMesh->RemoveElement( elem );
1498 //=======================================================================
1500 * \brief Split each of given quadrangles into 4 triangles.
1501 * \param theElems - The faces to be splitted. If empty all faces are split.
1503 //=======================================================================
1505 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1507 myLastCreatedElems.Clear();
1508 myLastCreatedNodes.Clear();
1510 SMESH_MesherHelper helper( *GetMesh() );
1511 helper.SetElementsOnShape( true );
1513 SMDS_ElemIteratorPtr faceIt;
1514 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1515 else faceIt = elemSetIterator( theElems );
1518 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1520 vector< const SMDS_MeshNode* > nodes;
1521 SMESHDS_SubMesh* subMeshDS;
1523 Handle(Geom_Surface) surface;
1524 TopLoc_Location loc;
1526 while ( faceIt->more() )
1528 const SMDS_MeshElement* quad = faceIt->next();
1529 if ( !quad || quad->NbCornerNodes() != 4 )
1532 // get a surface the quad is on
1534 if ( quad->getshapeId() < 1 )
1537 helper.SetSubShape( 0 );
1540 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1542 helper.SetSubShape( quad->getshapeId() );
1543 if ( !helper.GetSubShape().IsNull() &&
1544 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1546 F = TopoDS::Face( helper.GetSubShape() );
1547 surface = BRep_Tool::Surface( F, loc );
1548 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1552 helper.SetSubShape( 0 );
1557 // create a central node
1559 const SMDS_MeshNode* nCentral;
1560 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1562 if ( nodes.size() == 9 )
1564 nCentral = nodes.back();
1571 for ( ; iN < nodes.size(); ++iN )
1572 xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1574 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1575 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1577 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1578 xyz[0], xyz[1], xyz[2], xyz[3],
1579 xyz[4], xyz[5], xyz[6], xyz[7] );
1583 for ( ; iN < nodes.size(); ++iN )
1584 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1586 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1587 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1589 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1590 uv[0], uv[1], uv[2], uv[3],
1591 uv[4], uv[5], uv[6], uv[7] );
1593 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1597 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1598 uv[8].X(), uv[8].Y() );
1599 myLastCreatedNodes.Append( nCentral );
1602 // create 4 triangles
1604 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 ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1930 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1931 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1932 facetCreated = method.hasFacet( *facet );
1934 if ( !facetCreated )
1935 method = TSplitMethod(0); // incompatible method
1939 if ( method._nbSplits < 1 )
1941 // No standard method is applicable, use a generic solution:
1942 // each facet of a volume is split into triangles and
1943 // each of triangles and a volume barycenter form a tetrahedron.
1945 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1947 int* connectivity = new int[ maxTetConnSize + 1 ];
1948 method._connectivity = connectivity;
1949 method._ownConn = true;
1950 method._baryNode = !isHex27; // to create central node or not
1953 int baryCenInd = vol.NbNodes() - int( isHex27 );
1954 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1956 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1957 const int* nInd = vol.GetFaceNodesIndices( iF );
1958 // find common node of triangle facets of tetra to create
1959 int iCommon = 0; // index in linear numeration
1960 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1961 if ( !triaSplits.empty() )
1964 const TTriangleFacet* facet = &triaSplits.front();
1965 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1966 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1967 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1970 else if ( nbNodes > 3 && !is24TetMode )
1972 // find the best method of splitting into triangles by aspect ratio
1973 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1974 map< double, int > badness2iCommon;
1975 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1976 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1977 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1980 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1982 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1983 nodes[ iQ*((iLast-1)%nbNodes)],
1984 nodes[ iQ*((iLast )%nbNodes)]);
1985 badness += getBadRate( &tria, aspectRatio );
1987 badness2iCommon.insert( make_pair( badness, iCommon ));
1989 // use iCommon with lowest badness
1990 iCommon = badness2iCommon.begin()->second;
1992 if ( iCommon >= nbNodes )
1993 iCommon = 0; // something wrong
1995 // fill connectivity of tetrahedra based on a current face
1996 int nbTet = nbNodes - 2;
1997 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2002 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2003 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2007 method._faceBaryNode[ iF ] = 0;
2008 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2011 for ( int i = 0; i < nbTet; ++i )
2013 int i1 = i, i2 = (i+1) % nbNodes;
2014 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2015 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2016 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2017 connectivity[ connSize++ ] = faceBaryCenInd;
2018 connectivity[ connSize++ ] = baryCenInd;
2023 for ( int i = 0; i < nbTet; ++i )
2025 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2026 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2027 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2028 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2029 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2030 connectivity[ connSize++ ] = baryCenInd;
2033 method._nbSplits += nbTet;
2035 } // loop on volume faces
2037 connectivity[ connSize++ ] = -1;
2039 } // end of generic solution
2043 //=======================================================================
2045 * \brief return TSplitMethod to split haxhedron into prisms
2047 //=======================================================================
2049 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2050 const int methodFlags,
2051 const int facetToSplit)
2053 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2055 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2057 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2059 static TSplitMethod to4methods[4]; // order BT, LR, FB
2060 if ( to4methods[iF]._nbSplits == 0 )
2064 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2065 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2066 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2069 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2070 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2071 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2074 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2075 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2076 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2078 default: return to4methods[3];
2080 to4methods[iF]._nbSplits = 4;
2081 to4methods[iF]._nbCorners = 6;
2083 return to4methods[iF];
2085 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2087 TSplitMethod method;
2089 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2091 const int nbVariants = 2, nbSplits = 2;
2092 const int** connVariants = 0;
2094 case 0: connVariants = theHexTo2Prisms_BT; break;
2095 case 1: connVariants = theHexTo2Prisms_LR; break;
2096 case 2: connVariants = theHexTo2Prisms_FB; break;
2097 default: return method;
2100 // look for prisms adjacent via facetToSplit and an opposite one
2101 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2103 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2104 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2105 if ( nbNodes != 4 ) return method;
2107 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2108 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2109 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2111 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2113 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2118 // there are adjacent prism
2119 for ( int variant = 0; variant < nbVariants; ++variant )
2121 // check method compliancy with adjacent prisms,
2122 // the found prism facets must be among facets of prisms described by current method
2123 method._nbSplits = nbSplits;
2124 method._nbCorners = 6;
2125 method._connectivity = connVariants[ variant ];
2126 if ( method.hasFacet( *t ))
2131 // No adjacent prisms. Select a variant with a best aspect ratio.
2133 double badness[2] = { 0, 0 };
2134 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2135 const SMDS_MeshNode** nodes = vol.GetNodes();
2136 for ( int variant = 0; variant < nbVariants; ++variant )
2137 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2139 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2140 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2142 method._connectivity = connVariants[ variant ];
2143 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2144 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2145 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2147 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2150 badness[ variant ] += getBadRate( &tria, aspectRatio );
2152 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2154 method._nbSplits = nbSplits;
2155 method._nbCorners = 6;
2156 method._connectivity = connVariants[ iBetter ];
2161 //================================================================================
2163 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2165 //================================================================================
2167 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2168 const SMDSAbs_GeometryType geom ) const
2170 // find the tetrahedron including the three nodes of facet
2171 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2172 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2173 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2174 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2175 while ( volIt1->more() )
2177 const SMDS_MeshElement* v = volIt1->next();
2178 if ( v->GetGeomType() != geom )
2180 const int lastCornerInd = v->NbCornerNodes() - 1;
2181 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2182 continue; // medium node not allowed
2183 const int ind2 = v->GetNodeIndex( n2 );
2184 if ( ind2 < 0 || lastCornerInd < ind2 )
2186 const int ind3 = v->GetNodeIndex( n3 );
2187 if ( ind3 < 0 || lastCornerInd < ind3 )
2194 //=======================================================================
2196 * \brief A key of a face of volume
2198 //=======================================================================
2200 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2202 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2204 TIDSortedNodeSet sortedNodes;
2205 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2206 int nbNodes = vol.NbFaceNodes( iF );
2207 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2208 for ( int i = 0; i < nbNodes; i += iQ )
2209 sortedNodes.insert( fNodes[i] );
2210 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2211 first.first = (*(n++))->GetID();
2212 first.second = (*(n++))->GetID();
2213 second.first = (*(n++))->GetID();
2214 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2219 //=======================================================================
2220 //function : SplitVolumes
2221 //purpose : Split volume elements into tetrahedra or prisms.
2222 // If facet ID < 0, element is split into tetrahedra,
2223 // else a hexahedron is split into prisms so that the given facet is
2224 // split into triangles
2225 //=======================================================================
2227 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2228 const int theMethodFlags)
2230 SMDS_VolumeTool volTool;
2231 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2232 fHelper.ToFixNodeParameters( true );
2234 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2235 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2237 SMESH_SequenceOfElemPtr newNodes, newElems;
2239 // map face of volume to it's baricenrtic node
2240 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2242 vector<const SMDS_MeshElement* > splitVols;
2244 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2245 for ( ; elem2facet != theElems.end(); ++elem2facet )
2247 const SMDS_MeshElement* elem = elem2facet->first;
2248 const int facetToSplit = elem2facet->second;
2249 if ( elem->GetType() != SMDSAbs_Volume )
2251 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2252 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2255 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2257 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2258 getTetraSplitMethod( volTool, theMethodFlags ) :
2259 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2260 if ( splitMethod._nbSplits < 1 ) continue;
2262 // find submesh to add new tetras to
2263 if ( !subMesh || !subMesh->Contains( elem ))
2265 int shapeID = FindShape( elem );
2266 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2267 subMesh = GetMeshDS()->MeshElements( shapeID );
2270 if ( elem->IsQuadratic() )
2273 // add quadratic links to the helper
2274 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2276 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2277 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2278 for ( int iN = 0; iN < nbN; iN += iQ )
2279 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2281 helper.SetIsQuadratic( true );
2286 helper.SetIsQuadratic( false );
2288 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2289 volTool.GetNodes() + elem->NbNodes() );
2290 helper.SetElementsOnShape( true );
2291 if ( splitMethod._baryNode )
2293 // make a node at barycenter
2294 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2295 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2296 nodes.push_back( gcNode );
2297 newNodes.Append( gcNode );
2299 if ( !splitMethod._faceBaryNode.empty() )
2301 // make or find baricentric nodes of faces
2302 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2303 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2305 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2306 volFace2BaryNode.insert
2307 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2310 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2311 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2313 nodes.push_back( iF_n->second = f_n->second );
2318 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2319 const int* volConn = splitMethod._connectivity;
2320 if ( splitMethod._nbCorners == 4 ) // tetra
2321 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2322 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2323 nodes[ volConn[1] ],
2324 nodes[ volConn[2] ],
2325 nodes[ volConn[3] ]));
2327 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2328 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2329 nodes[ volConn[1] ],
2330 nodes[ volConn[2] ],
2331 nodes[ volConn[3] ],
2332 nodes[ volConn[4] ],
2333 nodes[ volConn[5] ]));
2335 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2337 // Split faces on sides of the split volume
2339 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2340 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2342 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2343 if ( nbNodes < 4 ) continue;
2345 // find an existing face
2346 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2347 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2348 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2349 /*noMedium=*/false))
2352 helper.SetElementsOnShape( false );
2353 vector< const SMDS_MeshElement* > triangles;
2355 // find submesh to add new triangles in
2356 if ( !fSubMesh || !fSubMesh->Contains( face ))
2358 int shapeID = FindShape( face );
2359 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2361 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2362 if ( iF_n != splitMethod._faceBaryNode.end() )
2364 const SMDS_MeshNode *baryNode = iF_n->second;
2365 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2367 const SMDS_MeshNode* n1 = fNodes[iN];
2368 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2369 const SMDS_MeshNode *n3 = baryNode;
2370 if ( !volTool.IsFaceExternal( iF ))
2372 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2374 if ( fSubMesh ) // update position of the bary node on geometry
2377 subMesh->RemoveNode( baryNode, false );
2378 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2379 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2380 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2382 fHelper.SetSubShape( s );
2383 gp_XY uv( 1e100, 1e100 );
2385 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2386 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2389 // node is too far from the surface
2390 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2391 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2392 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2399 // among possible triangles create ones discribed by split method
2400 const int* nInd = volTool.GetFaceNodesIndices( iF );
2401 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2402 int iCom = 0; // common node of triangle faces to split into
2403 list< TTriangleFacet > facets;
2404 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2406 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2407 nInd[ iQ * ( (iCom+1)%nbNodes )],
2408 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2409 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2410 nInd[ iQ * ( (iCom+2)%nbNodes )],
2411 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2412 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2414 facets.push_back( t012 );
2415 facets.push_back( t023 );
2416 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2417 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2418 nInd[ iQ * ((iLast-1)%nbNodes )],
2419 nInd[ iQ * ((iLast )%nbNodes )]));
2423 list< TTriangleFacet >::iterator facet = facets.begin();
2424 if ( facet == facets.end() )
2426 for ( ; facet != facets.end(); ++facet )
2428 if ( !volTool.IsFaceExternal( iF ))
2429 swap( facet->_n2, facet->_n3 );
2430 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2431 volNodes[ facet->_n2 ],
2432 volNodes[ facet->_n3 ]));
2435 for ( size_t i = 0; i < triangles.size(); ++i )
2437 if ( !triangles[ i ]) continue;
2439 fSubMesh->AddElement( triangles[ i ]);
2440 newElems.Append( triangles[ i ]);
2442 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2443 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2445 } // while a face based on facet nodes exists
2446 } // loop on volume faces to split them into triangles
2448 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2450 if ( geomType == SMDSEntity_TriQuad_Hexa )
2452 // remove medium nodes that could become free
2453 for ( int i = 20; i < volTool.NbNodes(); ++i )
2454 if ( volNodes[i]->NbInverseElements() == 0 )
2455 GetMeshDS()->RemoveNode( volNodes[i] );
2457 } // loop on volumes to split
2459 myLastCreatedNodes = newNodes;
2460 myLastCreatedElems = newElems;
2463 //=======================================================================
2464 //function : GetHexaFacetsToSplit
2465 //purpose : For hexahedra that will be split into prisms, finds facets to
2466 // split into triangles. Only hexahedra adjacent to the one closest
2467 // to theFacetNormal.Location() are returned.
2468 //param [in,out] theHexas - the hexahedra
2469 //param [in] theFacetNormal - facet normal
2470 //param [out] theFacets - the hexahedra and found facet IDs
2471 //=======================================================================
2473 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2474 const gp_Ax1& theFacetNormal,
2475 TFacetOfElem & theFacets)
2477 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2479 // Find a hexa closest to the location of theFacetNormal
2481 const SMDS_MeshElement* startHex;
2483 // get SMDS_ElemIteratorPtr on theHexas
2484 typedef const SMDS_MeshElement* TValue;
2485 typedef TIDSortedElemSet::iterator TSetIterator;
2486 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2487 typedef SMDS_MeshElement::GeomFilter TFilter;
2488 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2489 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2490 ( new TElemSetIter( theHexas.begin(),
2492 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2494 SMESH_ElementSearcher* searcher =
2495 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2497 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2502 throw SALOME_Exception( THIS_METHOD "startHex not found");
2505 // Select a facet of startHex by theFacetNormal
2507 SMDS_VolumeTool vTool( startHex );
2508 double norm[3], dot, maxDot = 0;
2510 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2511 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2513 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2521 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2523 // Fill theFacets starting from facetID of startHex
2525 // facets used for seach of volumes adjacent to already treated ones
2526 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2527 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2528 TFacetMap facetsToCheck;
2530 set<const SMDS_MeshNode*> facetNodes;
2531 const SMDS_MeshElement* curHex;
2533 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2537 // move in two directions from startHex via facetID
2538 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2541 int curFacet = facetID;
2542 if ( is2nd ) // do not treat startHex twice
2544 vTool.Set( curHex );
2545 if ( vTool.IsFreeFace( curFacet, &curHex ))
2551 vTool.GetFaceNodes( curFacet, facetNodes );
2552 vTool.Set( curHex );
2553 curFacet = vTool.GetFaceIndex( facetNodes );
2558 // store a facet to split
2559 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2561 theFacets.insert( make_pair( curHex, -1 ));
2564 if ( !allHex && !theHexas.count( curHex ))
2567 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2568 theFacets.insert( make_pair( curHex, curFacet ));
2569 if ( !facetIt2isNew.second )
2572 // remember not-to-split facets in facetsToCheck
2573 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2574 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2576 if ( iF == curFacet && iF == oppFacet )
2578 TVolumeFaceKey facetKey ( vTool, iF );
2579 TElemFacets elemFacet( facetIt2isNew.first, iF );
2580 pair< TFacetMap::iterator, bool > it2isnew =
2581 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2582 if ( !it2isnew.second )
2583 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2585 // pass to a volume adjacent via oppFacet
2586 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2592 // get a new curFacet
2593 vTool.GetFaceNodes( oppFacet, facetNodes );
2594 vTool.Set( curHex );
2595 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2598 } // move in two directions from startHex via facetID
2600 // Find a new startHex by facetsToCheck
2604 TFacetMap::iterator fIt = facetsToCheck.begin();
2605 while ( !startHex && fIt != facetsToCheck.end() )
2607 const TElemFacets& elemFacets = fIt->second;
2608 const SMDS_MeshElement* hex = elemFacets.first->first;
2609 int splitFacet = elemFacets.first->second;
2610 int lateralFacet = elemFacets.second;
2611 facetsToCheck.erase( fIt );
2612 fIt = facetsToCheck.begin();
2615 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2616 curHex->GetGeomType() != SMDSGeom_HEXA )
2618 if ( !allHex && !theHexas.count( curHex ))
2623 // find a facet of startHex to split
2625 set<const SMDS_MeshNode*> lateralNodes;
2626 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2627 vTool.GetFaceNodes( splitFacet, facetNodes );
2628 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2629 vTool.Set( startHex );
2630 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2632 // look for a facet of startHex having common nodes with facetNodes
2633 // but not lateralFacet
2634 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2636 if ( iF == lateralFacet )
2638 int nbCommonNodes = 0;
2639 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2640 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2641 nbCommonNodes += facetNodes.count( nn[ iN ]);
2643 if ( nbCommonNodes >= 2 )
2650 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2652 } // while ( startHex )
2659 //================================================================================
2661 * \brief Selects nodes of several elements according to a given interlace
2662 * \param [in] srcNodes - nodes to select from
2663 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2664 * \param [in] interlace - indices of nodes for all elements
2665 * \param [in] nbElems - nb of elements
2666 * \param [in] nbNodes - nb of nodes in each element
2667 * \param [in] mesh - the mesh
2668 * \param [out] elemQueue - a list to push elements found by the selected nodes
2669 * \param [in] type - type of elements to look for
2671 //================================================================================
2673 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2674 vector< const SMDS_MeshNode* >* tgtNodesVec,
2675 const int* interlace,
2678 SMESHDS_Mesh* mesh = 0,
2679 list< const SMDS_MeshElement* >* elemQueue=0,
2680 SMDSAbs_ElementType type=SMDSAbs_All)
2682 for ( int iE = 0; iE < nbElems; ++iE )
2684 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2685 const int* select = & interlace[iE*nbNodes];
2686 elemNodes.resize( nbNodes );
2687 for ( int iN = 0; iN < nbNodes; ++iN )
2688 elemNodes[iN] = srcNodes[ select[ iN ]];
2690 const SMDS_MeshElement* e;
2692 for ( int iE = 0; iE < nbElems; ++iE )
2693 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2694 elemQueue->push_back( e );
2698 //=======================================================================
2700 * Split bi-quadratic elements into linear ones without creation of additional nodes
2701 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2702 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2703 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2704 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2705 * will be split in order to keep the mesh conformal.
2706 * \param elems - elements to split
2708 //=======================================================================
2710 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2712 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2713 vector<const SMDS_MeshElement* > splitElems;
2714 list< const SMDS_MeshElement* > elemQueue;
2715 list< const SMDS_MeshElement* >::iterator elemIt;
2717 SMESHDS_Mesh * mesh = GetMeshDS();
2718 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2719 int nbElems, nbNodes;
2721 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2722 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2725 elemQueue.push_back( *elemSetIt );
2726 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2728 const SMDS_MeshElement* elem = *elemIt;
2729 switch( elem->GetEntityType() )
2731 case SMDSEntity_TriQuad_Hexa: // HEX27
2733 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2734 nbElems = nbNodes = 8;
2735 elemType = & hexaType;
2737 // get nodes for new elements
2738 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2739 { 1,9,20,8, 17,22,26,21 },
2740 { 2,10,20,9, 18,23,26,22 },
2741 { 3,11,20,10, 19,24,26,23 },
2742 { 16,21,26,24, 4,12,25,15 },
2743 { 17,22,26,21, 5,13,25,12 },
2744 { 18,23,26,22, 6,14,25,13 },
2745 { 19,24,26,23, 7,15,25,14 }};
2746 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2748 // add boundary faces to elemQueue
2749 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2750 { 4,5,6,7, 12,13,14,15, 25 },
2751 { 0,1,5,4, 8,17,12,16, 21 },
2752 { 1,2,6,5, 9,18,13,17, 22 },
2753 { 2,3,7,6, 10,19,14,18, 23 },
2754 { 3,0,4,7, 11,16,15,19, 24 }};
2755 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2757 // add boundary segments to elemQueue
2758 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2759 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2760 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2761 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2764 case SMDSEntity_BiQuad_Triangle: // TRIA7
2766 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2769 elemType = & quadType;
2771 // get nodes for new elements
2772 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2773 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2775 // add boundary segments to elemQueue
2776 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2777 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2780 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2782 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2785 elemType = & quadType;
2787 // get nodes for new elements
2788 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2789 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2791 // add boundary segments to elemQueue
2792 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2793 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2796 case SMDSEntity_Quad_Edge:
2798 if ( elemIt == elemQueue.begin() )
2799 continue; // an elem is in theElems
2800 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2803 elemType = & segType;
2805 // get nodes for new elements
2806 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2807 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2811 } // switch( elem->GetEntityType() )
2813 // Create new elements
2815 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2819 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2820 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2821 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2822 //elemType->SetID( -1 );
2824 for ( int iE = 0; iE < nbElems; ++iE )
2825 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2828 ReplaceElemInGroups( elem, splitElems, mesh );
2831 for ( size_t i = 0; i < splitElems.size(); ++i )
2832 subMesh->AddElement( splitElems[i] );
2837 //=======================================================================
2838 //function : AddToSameGroups
2839 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2840 //=======================================================================
2842 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2843 const SMDS_MeshElement* elemInGroups,
2844 SMESHDS_Mesh * aMesh)
2846 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2847 if (!groups.empty()) {
2848 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2849 for ( ; grIt != groups.end(); grIt++ ) {
2850 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2851 if ( group && group->Contains( elemInGroups ))
2852 group->SMDSGroup().Add( elemToAdd );
2858 //=======================================================================
2859 //function : RemoveElemFromGroups
2860 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2861 //=======================================================================
2862 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2863 SMESHDS_Mesh * aMesh)
2865 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2866 if (!groups.empty())
2868 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2869 for (; GrIt != groups.end(); GrIt++)
2871 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2872 if (!grp || grp->IsEmpty()) continue;
2873 grp->SMDSGroup().Remove(removeelem);
2878 //================================================================================
2880 * \brief Replace elemToRm by elemToAdd in the all groups
2882 //================================================================================
2884 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2885 const SMDS_MeshElement* elemToAdd,
2886 SMESHDS_Mesh * aMesh)
2888 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2889 if (!groups.empty()) {
2890 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2891 for ( ; grIt != groups.end(); grIt++ ) {
2892 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2893 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2894 group->SMDSGroup().Add( elemToAdd );
2899 //================================================================================
2901 * \brief Replace elemToRm by elemToAdd in the all groups
2903 //================================================================================
2905 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2906 const vector<const SMDS_MeshElement*>& elemToAdd,
2907 SMESHDS_Mesh * aMesh)
2909 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2910 if (!groups.empty())
2912 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2913 for ( ; grIt != groups.end(); grIt++ ) {
2914 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2915 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2916 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2917 group->SMDSGroup().Add( elemToAdd[ i ] );
2922 //=======================================================================
2923 //function : QuadToTri
2924 //purpose : Cut quadrangles into triangles.
2925 // theCrit is used to select a diagonal to cut
2926 //=======================================================================
2928 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2929 const bool the13Diag)
2931 myLastCreatedElems.Clear();
2932 myLastCreatedNodes.Clear();
2934 MESSAGE( "::QuadToTri()" );
2936 SMESHDS_Mesh * aMesh = GetMeshDS();
2938 Handle(Geom_Surface) surface;
2939 SMESH_MesherHelper helper( *GetMesh() );
2941 TIDSortedElemSet::iterator itElem;
2942 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2944 const SMDS_MeshElement* elem = *itElem;
2945 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2948 if ( elem->NbNodes() == 4 ) {
2949 // retrieve element nodes
2950 const SMDS_MeshNode* aNodes [4];
2951 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2953 while ( itN->more() )
2954 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2956 int aShapeId = FindShape( elem );
2957 const SMDS_MeshElement* newElem1 = 0;
2958 const SMDS_MeshElement* newElem2 = 0;
2960 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2961 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2964 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2965 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2967 myLastCreatedElems.Append(newElem1);
2968 myLastCreatedElems.Append(newElem2);
2969 // put a new triangle on the same shape and add to the same groups
2972 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2973 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2975 AddToSameGroups( newElem1, elem, aMesh );
2976 AddToSameGroups( newElem2, elem, aMesh );
2977 aMesh->RemoveElement( elem );
2980 // Quadratic quadrangle
2982 else if ( elem->NbNodes() == 8 )
2984 // get surface elem is on
2985 int aShapeId = FindShape( elem );
2986 if ( aShapeId != helper.GetSubShapeID() ) {
2990 shape = aMesh->IndexToShape( aShapeId );
2991 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2992 TopoDS_Face face = TopoDS::Face( shape );
2993 surface = BRep_Tool::Surface( face );
2994 if ( !surface.IsNull() )
2995 helper.SetSubShape( shape );
2999 const SMDS_MeshNode* aNodes [8];
3000 const SMDS_MeshNode* inFaceNode = 0;
3001 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3003 if ( helper.GetNodeUVneedInFaceNode() )
3004 while ( itN->more() && !inFaceNode ) {
3005 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3006 if ( aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
3008 inFaceNode = aNodes[ i-1 ];
3012 // find middle point for (0,1,2,3)
3013 // and create a node in this point;
3015 if ( surface.IsNull() ) {
3017 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
3021 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
3024 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
3026 p = surface->Value( uv.X(), uv.Y() ).XYZ();
3028 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
3029 myLastCreatedNodes.Append(newN);
3031 // create a new element
3032 const SMDS_MeshElement* newElem1 = 0;
3033 const SMDS_MeshElement* newElem2 = 0;
3035 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3036 aNodes[6], aNodes[7], newN );
3037 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3038 newN, aNodes[4], aNodes[5] );
3041 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3042 aNodes[7], aNodes[4], newN );
3043 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3044 newN, aNodes[5], aNodes[6] );
3046 myLastCreatedElems.Append(newElem1);
3047 myLastCreatedElems.Append(newElem2);
3048 // put a new triangle on the same shape and add to the same groups
3051 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3052 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3054 AddToSameGroups( newElem1, elem, aMesh );
3055 AddToSameGroups( newElem2, elem, aMesh );
3056 aMesh->RemoveElement( elem );
3063 //=======================================================================
3064 //function : getAngle
3066 //=======================================================================
3068 double getAngle(const SMDS_MeshElement * tr1,
3069 const SMDS_MeshElement * tr2,
3070 const SMDS_MeshNode * n1,
3071 const SMDS_MeshNode * n2)
3073 double angle = 2. * M_PI; // bad angle
3076 SMESH::Controls::TSequenceOfXYZ P1, P2;
3077 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3078 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3081 if(!tr1->IsQuadratic())
3082 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3084 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3085 if ( N1.SquareMagnitude() <= gp::Resolution() )
3087 if(!tr2->IsQuadratic())
3088 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3090 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3091 if ( N2.SquareMagnitude() <= gp::Resolution() )
3094 // find the first diagonal node n1 in the triangles:
3095 // take in account a diagonal link orientation
3096 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3097 for ( int t = 0; t < 2; t++ ) {
3098 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3099 int i = 0, iDiag = -1;
3100 while ( it->more()) {
3101 const SMDS_MeshElement *n = it->next();
3102 if ( n == n1 || n == n2 ) {
3106 if ( i - iDiag == 1 )
3107 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3116 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3119 angle = N1.Angle( N2 );
3124 // =================================================
3125 // class generating a unique ID for a pair of nodes
3126 // and able to return nodes by that ID
3127 // =================================================
3131 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3132 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3135 long GetLinkID (const SMDS_MeshNode * n1,
3136 const SMDS_MeshNode * n2) const
3138 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3141 bool GetNodes (const long theLinkID,
3142 const SMDS_MeshNode* & theNode1,
3143 const SMDS_MeshNode* & theNode2) const
3145 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3146 if ( !theNode1 ) return false;
3147 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3148 if ( !theNode2 ) return false;
3154 const SMESHDS_Mesh* myMesh;
3159 //=======================================================================
3160 //function : TriToQuad
3161 //purpose : Fuse neighbour triangles into quadrangles.
3162 // theCrit is used to select a neighbour to fuse with.
3163 // theMaxAngle is a max angle between element normals at which
3164 // fusion is still performed.
3165 //=======================================================================
3167 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3168 SMESH::Controls::NumericalFunctorPtr theCrit,
3169 const double theMaxAngle)
3171 myLastCreatedElems.Clear();
3172 myLastCreatedNodes.Clear();
3174 MESSAGE( "::TriToQuad()" );
3176 if ( !theCrit.get() )
3179 SMESHDS_Mesh * aMesh = GetMeshDS();
3181 // Prepare data for algo: build
3182 // 1. map of elements with their linkIDs
3183 // 2. map of linkIDs with their elements
3185 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3186 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3187 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3188 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3190 TIDSortedElemSet::iterator itElem;
3191 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3193 const SMDS_MeshElement* elem = *itElem;
3194 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3195 bool IsTria = ( elem->NbCornerNodes()==3 );
3196 if (!IsTria) continue;
3198 // retrieve element nodes
3199 const SMDS_MeshNode* aNodes [4];
3200 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3203 aNodes[ i++ ] = itN->next();
3204 aNodes[ 3 ] = aNodes[ 0 ];
3207 for ( i = 0; i < 3; i++ ) {
3208 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3209 // check if elements sharing a link can be fused
3210 itLE = mapLi_listEl.find( link );
3211 if ( itLE != mapLi_listEl.end() ) {
3212 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3214 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3215 //if ( FindShape( elem ) != FindShape( elem2 ))
3216 // continue; // do not fuse triangles laying on different shapes
3217 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3218 continue; // avoid making badly shaped quads
3219 (*itLE).second.push_back( elem );
3222 mapLi_listEl[ link ].push_back( elem );
3224 mapEl_setLi [ elem ].insert( link );
3227 // Clean the maps from the links shared by a sole element, ie
3228 // links to which only one element is bound in mapLi_listEl
3230 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3231 int nbElems = (*itLE).second.size();
3232 if ( nbElems < 2 ) {
3233 const SMDS_MeshElement* elem = (*itLE).second.front();
3234 SMESH_TLink link = (*itLE).first;
3235 mapEl_setLi[ elem ].erase( link );
3236 if ( mapEl_setLi[ elem ].empty() )
3237 mapEl_setLi.erase( elem );
3241 // Algo: fuse triangles into quadrangles
3243 while ( ! mapEl_setLi.empty() ) {
3244 // Look for the start element:
3245 // the element having the least nb of shared links
3246 const SMDS_MeshElement* startElem = 0;
3248 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3249 int nbLinks = (*itEL).second.size();
3250 if ( nbLinks < minNbLinks ) {
3251 startElem = (*itEL).first;
3252 minNbLinks = nbLinks;
3253 if ( minNbLinks == 1 )
3258 // search elements to fuse starting from startElem or links of elements
3259 // fused earlyer - startLinks
3260 list< SMESH_TLink > startLinks;
3261 while ( startElem || !startLinks.empty() ) {
3262 while ( !startElem && !startLinks.empty() ) {
3263 // Get an element to start, by a link
3264 SMESH_TLink linkId = startLinks.front();
3265 startLinks.pop_front();
3266 itLE = mapLi_listEl.find( linkId );
3267 if ( itLE != mapLi_listEl.end() ) {
3268 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3269 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3270 for ( ; itE != listElem.end() ; itE++ )
3271 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3273 mapLi_listEl.erase( itLE );
3278 // Get candidates to be fused
3279 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3280 const SMESH_TLink *link12, *link13;
3282 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3283 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3284 ASSERT( !setLi.empty() );
3285 set< SMESH_TLink >::iterator itLi;
3286 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3288 const SMESH_TLink & link = (*itLi);
3289 itLE = mapLi_listEl.find( link );
3290 if ( itLE == mapLi_listEl.end() )
3293 const SMDS_MeshElement* elem = (*itLE).second.front();
3295 elem = (*itLE).second.back();
3296 mapLi_listEl.erase( itLE );
3297 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3308 // add other links of elem to list of links to re-start from
3309 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3310 set< SMESH_TLink >::iterator it;
3311 for ( it = links.begin(); it != links.end(); it++ ) {
3312 const SMESH_TLink& link2 = (*it);
3313 if ( link2 != link )
3314 startLinks.push_back( link2 );
3318 // Get nodes of possible quadrangles
3319 const SMDS_MeshNode *n12 [4], *n13 [4];
3320 bool Ok12 = false, Ok13 = false;
3321 const SMDS_MeshNode *linkNode1, *linkNode2;
3323 linkNode1 = link12->first;
3324 linkNode2 = link12->second;
3325 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3329 linkNode1 = link13->first;
3330 linkNode2 = link13->second;
3331 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3335 // Choose a pair to fuse
3336 if ( Ok12 && Ok13 ) {
3337 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3338 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3339 double aBadRate12 = getBadRate( &quad12, theCrit );
3340 double aBadRate13 = getBadRate( &quad13, theCrit );
3341 if ( aBadRate13 < aBadRate12 )
3348 // and remove fused elems and remove links from the maps
3349 mapEl_setLi.erase( tr1 );
3352 mapEl_setLi.erase( tr2 );
3353 mapLi_listEl.erase( *link12 );
3354 if ( tr1->NbNodes() == 3 )
3356 const SMDS_MeshElement* newElem = 0;
3357 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3358 myLastCreatedElems.Append(newElem);
3359 AddToSameGroups( newElem, tr1, aMesh );
3360 int aShapeId = tr1->getshapeId();
3362 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3363 aMesh->RemoveElement( tr1 );
3364 aMesh->RemoveElement( tr2 );
3367 vector< const SMDS_MeshNode* > N1;
3368 vector< const SMDS_MeshNode* > N2;
3369 getNodesFromTwoTria(tr1,tr2,N1,N2);
3370 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3371 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3372 // i.e. first nodes from both arrays form a new diagonal
3373 const SMDS_MeshNode* aNodes[8];
3382 const SMDS_MeshElement* newElem = 0;
3383 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3384 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3385 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3387 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3388 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3389 myLastCreatedElems.Append(newElem);
3390 AddToSameGroups( newElem, tr1, aMesh );
3391 int aShapeId = tr1->getshapeId();
3393 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3394 aMesh->RemoveElement( tr1 );
3395 aMesh->RemoveElement( tr2 );
3396 // remove middle node (9)
3397 if ( N1[4]->NbInverseElements() == 0 )
3398 aMesh->RemoveNode( N1[4] );
3399 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3400 aMesh->RemoveNode( N1[6] );
3401 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3402 aMesh->RemoveNode( N2[6] );
3407 mapEl_setLi.erase( tr3 );
3408 mapLi_listEl.erase( *link13 );
3409 if ( tr1->NbNodes() == 3 ) {
3410 const SMDS_MeshElement* newElem = 0;
3411 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3412 myLastCreatedElems.Append(newElem);
3413 AddToSameGroups( newElem, tr1, aMesh );
3414 int aShapeId = tr1->getshapeId();
3416 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3417 aMesh->RemoveElement( tr1 );
3418 aMesh->RemoveElement( tr3 );
3421 vector< const SMDS_MeshNode* > N1;
3422 vector< const SMDS_MeshNode* > N2;
3423 getNodesFromTwoTria(tr1,tr3,N1,N2);
3424 // now we receive following N1 and N2 (using numeration as above image)
3425 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3426 // i.e. first nodes from both arrays form a new diagonal
3427 const SMDS_MeshNode* aNodes[8];
3436 const SMDS_MeshElement* newElem = 0;
3437 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3438 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3439 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3441 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3442 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3443 myLastCreatedElems.Append(newElem);
3444 AddToSameGroups( newElem, tr1, aMesh );
3445 int aShapeId = tr1->getshapeId();
3447 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3448 aMesh->RemoveElement( tr1 );
3449 aMesh->RemoveElement( tr3 );
3450 // remove middle node (9)
3451 if ( N1[4]->NbInverseElements() == 0 )
3452 aMesh->RemoveNode( N1[4] );
3453 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3454 aMesh->RemoveNode( N1[6] );
3455 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3456 aMesh->RemoveNode( N2[6] );
3460 // Next element to fuse: the rejected one
3462 startElem = Ok12 ? tr3 : tr2;
3464 } // if ( startElem )
3465 } // while ( startElem || !startLinks.empty() )
3466 } // while ( ! mapEl_setLi.empty() )
3472 /*#define DUMPSO(txt) \
3473 // cout << txt << endl;
3474 //=============================================================================
3478 //=============================================================================
3479 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3483 int tmp = idNodes[ i1 ];
3484 idNodes[ i1 ] = idNodes[ i2 ];
3485 idNodes[ i2 ] = tmp;
3486 gp_Pnt Ptmp = P[ i1 ];
3489 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3492 //=======================================================================
3493 //function : SortQuadNodes
3494 //purpose : Set 4 nodes of a quadrangle face in a good order.
3495 // Swap 1<->2 or 2<->3 nodes and correspondingly return
3497 //=======================================================================
3499 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3504 for ( i = 0; i < 4; i++ ) {
3505 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3507 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3510 gp_Vec V1(P[0], P[1]);
3511 gp_Vec V2(P[0], P[2]);
3512 gp_Vec V3(P[0], P[3]);
3514 gp_Vec Cross1 = V1 ^ V2;
3515 gp_Vec Cross2 = V2 ^ V3;
3518 if (Cross1.Dot(Cross2) < 0)
3523 if (Cross1.Dot(Cross2) < 0)
3527 swap ( i, i + 1, idNodes, P );
3529 // for ( int ii = 0; ii < 4; ii++ ) {
3530 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3531 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3537 //=======================================================================
3538 //function : SortHexaNodes
3539 //purpose : Set 8 nodes of a hexahedron in a good order.
3540 // Return success status
3541 //=======================================================================
3543 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3548 DUMPSO( "INPUT: ========================================");
3549 for ( i = 0; i < 8; i++ ) {
3550 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3551 if ( !n ) return false;
3552 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3553 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3555 DUMPSO( "========================================");
3558 set<int> faceNodes; // ids of bottom face nodes, to be found
3559 set<int> checkedId1; // ids of tried 2-nd nodes
3560 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3561 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
3562 int iMin, iLoop1 = 0;
3564 // Loop to try the 2-nd nodes
3566 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3568 // Find not checked 2-nd node
3569 for ( i = 1; i < 8; i++ )
3570 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3571 int id1 = idNodes[i];
3572 swap ( 1, i, idNodes, P );
3573 checkedId1.insert ( id1 );
3577 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3578 // ie that all but meybe one (id3 which is on the same face) nodes
3579 // lay on the same side from the triangle plane.
3581 bool manyInPlane = false; // more than 4 nodes lay in plane
3583 while ( ++iLoop2 < 6 ) {
3585 // get 1-2-3 plane coeffs
3586 Standard_Real A, B, C, D;
3587 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3588 if ( N.SquareMagnitude() > gp::Resolution() )
3590 gp_Pln pln ( P[0], N );
3591 pln.Coefficients( A, B, C, D );
3593 // find the node (iMin) closest to pln
3594 Standard_Real dist[ 8 ], minDist = DBL_MAX;
3596 for ( i = 3; i < 8; i++ ) {
3597 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3598 if ( fabs( dist[i] ) < minDist ) {
3599 minDist = fabs( dist[i] );
3602 if ( fabs( dist[i] ) <= tol )
3603 idInPln.insert( idNodes[i] );
3606 // there should not be more than 4 nodes in bottom plane
3607 if ( idInPln.size() > 1 )
3609 DUMPSO( "### idInPln.size() = " << idInPln.size());
3610 // idInPlane does not contain the first 3 nodes
3611 if ( manyInPlane || idInPln.size() == 5)
3612 return false; // all nodes in one plane
3615 // set the 1-st node to be not in plane
3616 for ( i = 3; i < 8; i++ ) {
3617 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3618 DUMPSO( "### Reset 0-th node");
3619 swap( 0, i, idNodes, P );
3624 // reset to re-check second nodes
3625 leastDist = DBL_MAX;
3629 break; // from iLoop2;
3632 // check that the other 4 nodes are on the same side
3633 bool sameSide = true;
3634 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3635 for ( i = 3; sameSide && i < 8; i++ ) {
3637 sameSide = ( isNeg == dist[i] <= 0.);
3640 // keep best solution
3641 if ( sameSide && minDist < leastDist ) {
3642 leastDist = minDist;
3644 faceNodes.insert( idNodes[ 1 ] );
3645 faceNodes.insert( idNodes[ 2 ] );
3646 faceNodes.insert( idNodes[ iMin ] );
3647 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3648 << " leastDist = " << leastDist);
3649 if ( leastDist <= DBL_MIN )
3654 // set next 3-d node to check
3655 int iNext = 2 + iLoop2;
3657 DUMPSO( "Try 2-nd");
3658 swap ( 2, iNext, idNodes, P );
3660 } // while ( iLoop2 < 6 )
3663 if ( faceNodes.empty() ) return false;
3665 // Put the faceNodes in proper places
3666 for ( i = 4; i < 8; i++ ) {
3667 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3668 // find a place to put
3670 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3672 DUMPSO( "Set faceNodes");
3673 swap ( iTo, i, idNodes, P );
3678 // Set nodes of the found bottom face in good order
3679 DUMPSO( " Found bottom face: ");
3680 i = SortQuadNodes( theMesh, idNodes );
3682 gp_Pnt Ptmp = P[ i ];
3687 // for ( int ii = 0; ii < 4; ii++ ) {
3688 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3689 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3692 // Gravity center of the top and bottom faces
3693 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3694 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3696 // Get direction from the bottom to the top face
3697 gp_Vec upDir ( aGCb, aGCt );
3698 Standard_Real upDirSize = upDir.Magnitude();
3699 if ( upDirSize <= gp::Resolution() ) return false;
3702 // Assure that the bottom face normal points up
3703 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3704 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3705 if ( Nb.Dot( upDir ) < 0 ) {
3706 DUMPSO( "Reverse bottom face");
3707 swap( 1, 3, idNodes, P );
3710 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3711 Standard_Real minDist = DBL_MAX;
3712 for ( i = 4; i < 8; i++ ) {
3713 // projection of P[i] to the plane defined by P[0] and upDir
3714 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3715 Standard_Real sqDist = P[0].SquareDistance( Pp );
3716 if ( sqDist < minDist ) {
3721 DUMPSO( "Set 4-th");
3722 swap ( 4, iMin, idNodes, P );
3724 // Set nodes of the top face in good order
3725 DUMPSO( "Sort top face");
3726 i = SortQuadNodes( theMesh, &idNodes[4] );
3729 gp_Pnt Ptmp = P[ i ];
3734 // Assure that direction of the top face normal is from the bottom face
3735 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3736 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3737 if ( Nt.Dot( upDir ) < 0 ) {
3738 DUMPSO( "Reverse top face");
3739 swap( 5, 7, idNodes, P );
3742 // DUMPSO( "OUTPUT: ========================================");
3743 // for ( i = 0; i < 8; i++ ) {
3744 // float *p = ugrid->GetPoint(idNodes[i]);
3745 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3751 //================================================================================
3753 * \brief Return nodes linked to the given one
3754 * \param theNode - the node
3755 * \param linkedNodes - the found nodes
3756 * \param type - the type of elements to check
3758 * Medium nodes are ignored
3760 //================================================================================
3762 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3763 TIDSortedElemSet & linkedNodes,
3764 SMDSAbs_ElementType type )
3766 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3767 while ( elemIt->more() )
3769 const SMDS_MeshElement* elem = elemIt->next();
3770 if(elem->GetType() == SMDSAbs_0DElement)
3773 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3774 if ( elem->GetType() == SMDSAbs_Volume )
3776 SMDS_VolumeTool vol( elem );
3777 while ( nodeIt->more() ) {
3778 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3779 if ( theNode != n && vol.IsLinked( theNode, n ))
3780 linkedNodes.insert( n );
3785 for ( int i = 0; nodeIt->more(); ++i ) {
3786 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3787 if ( n == theNode ) {
3788 int iBefore = i - 1;
3790 if ( elem->IsQuadratic() ) {
3791 int nb = elem->NbNodes() / 2;
3792 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3793 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3795 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3796 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3803 //=======================================================================
3804 //function : laplacianSmooth
3805 //purpose : pulls theNode toward the center of surrounding nodes directly
3806 // connected to that node along an element edge
3807 //=======================================================================
3809 void laplacianSmooth(const SMDS_MeshNode* theNode,
3810 const Handle(Geom_Surface)& theSurface,
3811 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3813 // find surrounding nodes
3815 TIDSortedElemSet nodeSet;
3816 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3818 // compute new coodrs
3820 double coord[] = { 0., 0., 0. };
3821 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3822 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3823 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3824 if ( theSurface.IsNull() ) { // smooth in 3D
3825 coord[0] += node->X();
3826 coord[1] += node->Y();
3827 coord[2] += node->Z();
3829 else { // smooth in 2D
3830 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3831 gp_XY* uv = theUVMap[ node ];
3832 coord[0] += uv->X();
3833 coord[1] += uv->Y();
3836 int nbNodes = nodeSet.size();
3839 coord[0] /= nbNodes;
3840 coord[1] /= nbNodes;
3842 if ( !theSurface.IsNull() ) {
3843 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3844 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3845 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3851 coord[2] /= nbNodes;
3855 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3858 //=======================================================================
3859 //function : centroidalSmooth
3860 //purpose : pulls theNode toward the element-area-weighted centroid of the
3861 // surrounding elements
3862 //=======================================================================
3864 void centroidalSmooth(const SMDS_MeshNode* theNode,
3865 const Handle(Geom_Surface)& theSurface,
3866 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3868 gp_XYZ aNewXYZ(0.,0.,0.);
3869 SMESH::Controls::Area anAreaFunc;
3870 double totalArea = 0.;
3875 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3876 while ( elemIt->more() )
3878 const SMDS_MeshElement* elem = elemIt->next();
3881 gp_XYZ elemCenter(0.,0.,0.);
3882 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3883 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3884 int nn = elem->NbNodes();
3885 if(elem->IsQuadratic()) nn = nn/2;
3887 //while ( itN->more() ) {
3889 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3891 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3892 aNodePoints.push_back( aP );
3893 if ( !theSurface.IsNull() ) { // smooth in 2D
3894 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3895 gp_XY* uv = theUVMap[ aNode ];
3896 aP.SetCoord( uv->X(), uv->Y(), 0. );
3900 double elemArea = anAreaFunc.GetValue( aNodePoints );
3901 totalArea += elemArea;
3903 aNewXYZ += elemCenter * elemArea;
3905 aNewXYZ /= totalArea;
3906 if ( !theSurface.IsNull() ) {
3907 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3908 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3913 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3916 //=======================================================================
3917 //function : getClosestUV
3918 //purpose : return UV of closest projection
3919 //=======================================================================
3921 static bool getClosestUV (Extrema_GenExtPS& projector,
3922 const gp_Pnt& point,
3925 projector.Perform( point );
3926 if ( projector.IsDone() ) {
3927 double u, v, minVal = DBL_MAX;
3928 for ( int i = projector.NbExt(); i > 0; i-- )
3929 if ( projector.SquareDistance( i ) < minVal ) {
3930 minVal = projector.SquareDistance( i );
3931 projector.Point( i ).Parameter( u, v );
3933 result.SetCoord( u, v );
3939 //=======================================================================
3941 //purpose : Smooth theElements during theNbIterations or until a worst
3942 // element has aspect ratio <= theTgtAspectRatio.
3943 // Aspect Ratio varies in range [1.0, inf].
3944 // If theElements is empty, the whole mesh is smoothed.
3945 // theFixedNodes contains additionally fixed nodes. Nodes built
3946 // on edges and boundary nodes are always fixed.
3947 //=======================================================================
3949 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3950 set<const SMDS_MeshNode*> & theFixedNodes,
3951 const SmoothMethod theSmoothMethod,
3952 const int theNbIterations,
3953 double theTgtAspectRatio,
3956 myLastCreatedElems.Clear();
3957 myLastCreatedNodes.Clear();
3959 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3961 if ( theTgtAspectRatio < 1.0 )
3962 theTgtAspectRatio = 1.0;
3964 const double disttol = 1.e-16;
3966 SMESH::Controls::AspectRatio aQualityFunc;
3968 SMESHDS_Mesh* aMesh = GetMeshDS();
3970 if ( theElems.empty() ) {
3971 // add all faces to theElems
3972 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3973 while ( fIt->more() ) {
3974 const SMDS_MeshElement* face = fIt->next();
3975 theElems.insert( theElems.end(), face );
3978 // get all face ids theElems are on
3979 set< int > faceIdSet;
3980 TIDSortedElemSet::iterator itElem;
3982 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3983 int fId = FindShape( *itElem );
3984 // check that corresponding submesh exists and a shape is face
3986 faceIdSet.find( fId ) == faceIdSet.end() &&
3987 aMesh->MeshElements( fId )) {
3988 TopoDS_Shape F = aMesh->IndexToShape( fId );
3989 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3990 faceIdSet.insert( fId );
3993 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3995 // ===============================================
3996 // smooth elements on each TopoDS_Face separately
3997 // ===============================================
3999 SMESH_MesherHelper helper( *GetMesh() );
4001 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
4002 for ( ; fId != faceIdSet.rend(); ++fId )
4004 // get face surface and submesh
4005 Handle(Geom_Surface) surface;
4006 SMESHDS_SubMesh* faceSubMesh = 0;
4009 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
4010 bool isUPeriodic = false, isVPeriodic = false;
4013 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
4014 surface = BRep_Tool::Surface( face );
4015 faceSubMesh = aMesh->MeshElements( *fId );
4016 fToler2 = BRep_Tool::Tolerance( face );
4017 fToler2 *= fToler2 * 10.;
4018 isUPeriodic = surface->IsUPeriodic();
4021 isVPeriodic = surface->IsVPeriodic();
4024 surface->Bounds( u1, u2, v1, v2 );
4025 helper.SetSubShape( face );
4027 // ---------------------------------------------------------
4028 // for elements on a face, find movable and fixed nodes and
4029 // compute UV for them
4030 // ---------------------------------------------------------
4031 bool checkBoundaryNodes = false;
4032 bool isQuadratic = false;
4033 set<const SMDS_MeshNode*> setMovableNodes;
4034 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4035 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4036 list< const SMDS_MeshElement* > elemsOnFace;
4038 Extrema_GenExtPS projector;
4039 GeomAdaptor_Surface surfAdaptor;
4040 if ( !surface.IsNull() ) {
4041 surfAdaptor.Load( surface );
4042 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4044 int nbElemOnFace = 0;
4045 itElem = theElems.begin();
4046 // loop on not yet smoothed elements: look for elems on a face
4047 while ( itElem != theElems.end() )
4049 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4050 break; // all elements found
4052 const SMDS_MeshElement* elem = *itElem;
4053 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4054 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4058 elemsOnFace.push_back( elem );
4059 theElems.erase( itElem++ );
4063 isQuadratic = elem->IsQuadratic();
4065 // get movable nodes of elem
4066 const SMDS_MeshNode* node;
4067 SMDS_TypeOfPosition posType;
4068 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4069 int nn = 0, nbn = elem->NbNodes();
4070 if(elem->IsQuadratic())
4072 while ( nn++ < nbn ) {
4073 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4074 const SMDS_PositionPtr& pos = node->GetPosition();
4075 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4076 if (posType != SMDS_TOP_EDGE &&
4077 posType != SMDS_TOP_VERTEX &&
4078 theFixedNodes.find( node ) == theFixedNodes.end())
4080 // check if all faces around the node are on faceSubMesh
4081 // because a node on edge may be bound to face
4082 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4084 if ( faceSubMesh ) {
4085 while ( eIt->more() && all ) {
4086 const SMDS_MeshElement* e = eIt->next();
4087 all = faceSubMesh->Contains( e );
4091 setMovableNodes.insert( node );
4093 checkBoundaryNodes = true;
4095 if ( posType == SMDS_TOP_3DSPACE )
4096 checkBoundaryNodes = true;
4099 if ( surface.IsNull() )
4102 // get nodes to check UV
4103 list< const SMDS_MeshNode* > uvCheckNodes;
4104 const SMDS_MeshNode* nodeInFace = 0;
4105 itN = elem->nodesIterator();
4106 nn = 0; nbn = elem->NbNodes();
4107 if(elem->IsQuadratic())
4109 while ( nn++ < nbn ) {
4110 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4111 if ( node->GetPosition()->GetDim() == 2 )
4113 if ( uvMap.find( node ) == uvMap.end() )
4114 uvCheckNodes.push_back( node );
4115 // add nodes of elems sharing node
4116 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4117 // while ( eIt->more() ) {
4118 // const SMDS_MeshElement* e = eIt->next();
4119 // if ( e != elem ) {
4120 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4121 // while ( nIt->more() ) {
4122 // const SMDS_MeshNode* n =
4123 // static_cast<const SMDS_MeshNode*>( nIt->next() );
4124 // if ( uvMap.find( n ) == uvMap.end() )
4125 // uvCheckNodes.push_back( n );
4131 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4132 for ( ; n != uvCheckNodes.end(); ++n ) {
4135 const SMDS_PositionPtr& pos = node->GetPosition();
4136 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4140 bool toCheck = true;
4141 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4143 // compute not existing UV
4144 bool project = ( posType == SMDS_TOP_3DSPACE );
4145 // double dist1 = DBL_MAX, dist2 = 0;
4146 // if ( posType != SMDS_TOP_3DSPACE ) {
4147 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4148 // project = dist1 > fToler2;
4150 if ( project ) { // compute new UV
4152 gp_Pnt pNode = SMESH_TNodeXYZ( node );
4153 if ( !getClosestUV( projector, pNode, newUV )) {
4154 MESSAGE("Node Projection Failed " << node);
4158 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4160 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4162 // if ( posType != SMDS_TOP_3DSPACE )
4163 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4164 // if ( dist2 < dist1 )
4168 // store UV in the map
4169 listUV.push_back( uv );
4170 uvMap.insert( make_pair( node, &listUV.back() ));
4172 } // loop on not yet smoothed elements
4174 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4175 checkBoundaryNodes = true;
4177 // fix nodes on mesh boundary
4179 if ( checkBoundaryNodes ) {
4180 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4181 map< SMESH_TLink, int >::iterator link_nb;
4182 // put all elements links to linkNbMap
4183 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4184 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4185 const SMDS_MeshElement* elem = (*elemIt);
4186 int nbn = elem->NbCornerNodes();
4187 // loop on elem links: insert them in linkNbMap
4188 for ( int iN = 0; iN < nbn; ++iN ) {
4189 const SMDS_MeshNode* n1 = elem->GetNode( iN );
4190 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4191 SMESH_TLink link( n1, n2 );
4192 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4196 // remove nodes that are in links encountered only once from setMovableNodes
4197 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4198 if ( link_nb->second == 1 ) {
4199 setMovableNodes.erase( link_nb->first.node1() );
4200 setMovableNodes.erase( link_nb->first.node2() );
4205 // -----------------------------------------------------
4206 // for nodes on seam edge, compute one more UV ( uvMap2 );
4207 // find movable nodes linked to nodes on seam and which
4208 // are to be smoothed using the second UV ( uvMap2 )
4209 // -----------------------------------------------------
4211 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4212 if ( !surface.IsNull() ) {
4213 TopExp_Explorer eExp( face, TopAbs_EDGE );
4214 for ( ; eExp.More(); eExp.Next() ) {
4215 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4216 if ( !BRep_Tool::IsClosed( edge, face ))
4218 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4219 if ( !sm ) continue;
4220 // find out which parameter varies for a node on seam
4223 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4224 if ( pcurve.IsNull() ) continue;
4225 uv1 = pcurve->Value( f );
4227 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4228 if ( pcurve.IsNull() ) continue;
4229 uv2 = pcurve->Value( f );
4230 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4232 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4233 std::swap( uv1, uv2 );
4234 // get nodes on seam and its vertices
4235 list< const SMDS_MeshNode* > seamNodes;
4236 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4237 while ( nSeamIt->more() ) {
4238 const SMDS_MeshNode* node = nSeamIt->next();
4239 if ( !isQuadratic || !IsMedium( node ))
4240 seamNodes.push_back( node );
4242 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4243 for ( ; vExp.More(); vExp.Next() ) {
4244 sm = aMesh->MeshElements( vExp.Current() );
4246 nSeamIt = sm->GetNodes();
4247 while ( nSeamIt->more() )
4248 seamNodes.push_back( nSeamIt->next() );
4251 // loop on nodes on seam
4252 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4253 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4254 const SMDS_MeshNode* nSeam = *noSeIt;
4255 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4256 if ( n_uv == uvMap.end() )
4259 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4260 // set the second UV
4261 listUV.push_back( *n_uv->second );
4262 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4263 if ( uvMap2.empty() )
4264 uvMap2 = uvMap; // copy the uvMap contents
4265 uvMap2[ nSeam ] = &listUV.back();
4267 // collect movable nodes linked to ones on seam in nodesNearSeam
4268 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4269 while ( eIt->more() ) {
4270 const SMDS_MeshElement* e = eIt->next();
4271 int nbUseMap1 = 0, nbUseMap2 = 0;
4272 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4273 int nn = 0, nbn = e->NbNodes();
4274 if(e->IsQuadratic()) nbn = nbn/2;
4275 while ( nn++ < nbn )
4277 const SMDS_MeshNode* n =
4278 static_cast<const SMDS_MeshNode*>( nIt->next() );
4280 setMovableNodes.find( n ) == setMovableNodes.end() )
4282 // add only nodes being closer to uv2 than to uv1
4283 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4284 // 0.5 * ( n->Y() + nSeam->Y() ),
4285 // 0.5 * ( n->Z() + nSeam->Z() ));
4287 // getClosestUV( projector, pMid, uv );
4288 double x = uvMap[ n ]->Coord( iPar );
4289 if ( Abs( uv1.Coord( iPar ) - x ) >
4290 Abs( uv2.Coord( iPar ) - x )) {
4291 nodesNearSeam.insert( n );
4297 // for centroidalSmooth all element nodes must
4298 // be on one side of a seam
4299 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4300 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4302 while ( nn++ < nbn ) {
4303 const SMDS_MeshNode* n =
4304 static_cast<const SMDS_MeshNode*>( nIt->next() );
4305 setMovableNodes.erase( n );
4309 } // loop on nodes on seam
4310 } // loop on edge of a face
4311 } // if ( !face.IsNull() )
4313 if ( setMovableNodes.empty() ) {
4314 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4315 continue; // goto next face
4323 double maxRatio = -1., maxDisplacement = -1.;
4324 set<const SMDS_MeshNode*>::iterator nodeToMove;
4325 for ( it = 0; it < theNbIterations; it++ ) {
4326 maxDisplacement = 0.;
4327 nodeToMove = setMovableNodes.begin();
4328 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4329 const SMDS_MeshNode* node = (*nodeToMove);
4330 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4333 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4334 if ( theSmoothMethod == LAPLACIAN )
4335 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4337 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4339 // node displacement
4340 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4341 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4342 if ( aDispl > maxDisplacement )
4343 maxDisplacement = aDispl;
4345 // no node movement => exit
4346 //if ( maxDisplacement < 1.e-16 ) {
4347 if ( maxDisplacement < disttol ) {
4348 MESSAGE("-- no node movement --");
4352 // check elements quality
4354 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4355 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4356 const SMDS_MeshElement* elem = (*elemIt);
4357 if ( !elem || elem->GetType() != SMDSAbs_Face )
4359 SMESH::Controls::TSequenceOfXYZ aPoints;
4360 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4361 double aValue = aQualityFunc.GetValue( aPoints );
4362 if ( aValue > maxRatio )
4366 if ( maxRatio <= theTgtAspectRatio ) {
4367 MESSAGE("-- quality achived --");
4370 if (it+1 == theNbIterations) {
4371 MESSAGE("-- Iteration limit exceeded --");
4373 } // smoothing iterations
4375 MESSAGE(" Face id: " << *fId <<
4376 " Nb iterstions: " << it <<
4377 " Displacement: " << maxDisplacement <<
4378 " Aspect Ratio " << maxRatio);
4380 // ---------------------------------------
4381 // new nodes positions are computed,
4382 // record movement in DS and set new UV
4383 // ---------------------------------------
4384 nodeToMove = setMovableNodes.begin();
4385 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4386 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4387 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4388 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4389 if ( node_uv != uvMap.end() ) {
4390 gp_XY* uv = node_uv->second;
4392 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4396 // move medium nodes of quadratic elements
4399 vector<const SMDS_MeshNode*> nodes;
4401 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4402 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4404 const SMDS_MeshElement* QF = *elemIt;
4405 if ( QF->IsQuadratic() )
4407 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4408 SMDS_MeshElement::iterator() );
4409 nodes.push_back( nodes[0] );
4411 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4413 if ( !surface.IsNull() )
4415 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4416 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4417 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4418 xyz = surface->Value( uv.X(), uv.Y() );
4421 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4423 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4424 // we have to move a medium node
4425 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4431 } // loop on face ids
4437 //=======================================================================
4438 //function : isReverse
4439 //purpose : Return true if normal of prevNodes is not co-directied with
4440 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4441 // iNotSame is where prevNodes and nextNodes are different.
4442 // If result is true then future volume orientation is OK
4443 //=======================================================================
4445 bool isReverse(const SMDS_MeshElement* face,
4446 const vector<const SMDS_MeshNode*>& prevNodes,
4447 const vector<const SMDS_MeshNode*>& nextNodes,
4451 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4452 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4453 gp_XYZ extrDir( pN - pP ), faceNorm;
4454 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4456 return faceNorm * extrDir < 0.0;
4459 //================================================================================
4461 * \brief Assure that theElemSets[0] holds elements, not nodes
4463 //================================================================================
4465 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4467 if ( !theElemSets[0].empty() &&
4468 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4470 std::swap( theElemSets[0], theElemSets[1] );
4472 else if ( !theElemSets[1].empty() &&
4473 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4475 std::swap( theElemSets[0], theElemSets[1] );
4480 //=======================================================================
4482 * \brief Create elements by sweeping an element
4483 * \param elem - element to sweep
4484 * \param newNodesItVec - nodes generated from each node of the element
4485 * \param newElems - generated elements
4486 * \param nbSteps - number of sweeping steps
4487 * \param srcElements - to append elem for each generated element
4489 //=======================================================================
4491 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4492 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4493 list<const SMDS_MeshElement*>& newElems,
4494 const size_t nbSteps,
4495 SMESH_SequenceOfElemPtr& srcElements)
4497 //MESSAGE("sweepElement " << nbSteps);
4498 SMESHDS_Mesh* aMesh = GetMeshDS();
4500 const int nbNodes = elem->NbNodes();
4501 const int nbCorners = elem->NbCornerNodes();
4502 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4503 polyhedron creation !!! */
4504 // Loop on elem nodes:
4505 // find new nodes and detect same nodes indices
4506 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4507 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4508 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4509 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4511 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4512 vector<int> sames(nbNodes);
4513 vector<bool> isSingleNode(nbNodes);
4515 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4516 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4517 const SMDS_MeshNode* node = nnIt->first;
4518 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4519 if ( listNewNodes.empty() )
4522 itNN [ iNode ] = listNewNodes.begin();
4523 prevNod[ iNode ] = node;
4524 nextNod[ iNode ] = listNewNodes.front();
4526 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4527 corner node of linear */
4528 if ( prevNod[ iNode ] != nextNod [ iNode ])
4529 nbDouble += !isSingleNode[iNode];
4531 if( iNode < nbCorners ) { // check corners only
4532 if ( prevNod[ iNode ] == nextNod [ iNode ])
4533 sames[nbSame++] = iNode;
4535 iNotSameNode = iNode;
4539 if ( nbSame == nbNodes || nbSame > 2) {
4540 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4544 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4546 // fix nodes order to have bottom normal external
4547 if ( baseType == SMDSEntity_Polygon )
4549 std::reverse( itNN.begin(), itNN.end() );
4550 std::reverse( prevNod.begin(), prevNod.end() );
4551 std::reverse( midlNod.begin(), midlNod.end() );
4552 std::reverse( nextNod.begin(), nextNod.end() );
4553 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4557 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4558 SMDS_MeshCell::applyInterlace( ind, itNN );
4559 SMDS_MeshCell::applyInterlace( ind, prevNod );
4560 SMDS_MeshCell::applyInterlace( ind, nextNod );
4561 SMDS_MeshCell::applyInterlace( ind, midlNod );
4562 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4565 sames[nbSame] = iNotSameNode;
4566 for ( int j = 0; j <= nbSame; ++j )
4567 for ( size_t i = 0; i < ind.size(); ++i )
4568 if ( ind[i] == sames[j] )
4573 iNotSameNode = sames[nbSame];
4577 else if ( elem->GetType() == SMDSAbs_Edge )
4579 // orient a new face same as adjacent one
4581 const SMDS_MeshElement* e;
4582 TIDSortedElemSet dummy;
4583 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4584 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4585 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4587 // there is an adjacent face, check order of nodes in it
4588 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4591 std::swap( itNN[0], itNN[1] );
4592 std::swap( prevNod[0], prevNod[1] );
4593 std::swap( nextNod[0], nextNod[1] );
4594 isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4596 sames[0] = 1 - sames[0];
4597 iNotSameNode = 1 - iNotSameNode;
4602 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4604 iSameNode = sames[ nbSame-1 ];
4605 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4606 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4607 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4610 if ( baseType == SMDSEntity_Polygon )
4612 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4613 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4615 else if ( baseType == SMDSEntity_Quad_Polygon )
4617 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4618 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4621 // make new elements
4622 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4625 for ( iNode = 0; iNode < nbNodes; iNode++ )
4627 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4628 nextNod[ iNode ] = *itNN[ iNode ]++;
4631 SMDS_MeshElement* aNewElem = 0;
4632 /*if(!elem->IsPoly())*/ {
4633 switch ( baseType ) {
4635 case SMDSEntity_Node: { // sweep NODE
4636 if ( nbSame == 0 ) {
4637 if ( isSingleNode[0] )
4638 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4640 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4646 case SMDSEntity_Edge: { // sweep EDGE
4647 if ( nbDouble == 0 )
4649 if ( nbSame == 0 ) // ---> quadrangle
4650 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4651 nextNod[ 1 ], nextNod[ 0 ] );
4652 else // ---> triangle
4653 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4654 nextNod[ iNotSameNode ] );
4656 else // ---> polygon
4658 vector<const SMDS_MeshNode*> poly_nodes;
4659 poly_nodes.push_back( prevNod[0] );
4660 poly_nodes.push_back( prevNod[1] );
4661 if ( prevNod[1] != nextNod[1] )
4663 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4664 poly_nodes.push_back( nextNod[1] );
4666 if ( prevNod[0] != nextNod[0] )
4668 poly_nodes.push_back( nextNod[0] );
4669 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4671 switch ( poly_nodes.size() ) {
4673 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4676 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4677 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4680 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4685 case SMDSEntity_Triangle: // TRIANGLE --->
4687 if ( nbDouble > 0 ) break;
4688 if ( nbSame == 0 ) // ---> pentahedron
4689 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4690 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4692 else if ( nbSame == 1 ) // ---> pyramid
4693 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4694 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4695 nextNod[ iSameNode ]);
4697 else // 2 same nodes: ---> tetrahedron
4698 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4699 nextNod[ iNotSameNode ]);
4702 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4706 if ( nbDouble+nbSame == 2 )
4708 if(nbSame==0) { // ---> quadratic quadrangle
4709 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4710 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4712 else { //(nbSame==1) // ---> quadratic triangle
4714 return; // medium node on axis
4716 else if(sames[0]==0)
4717 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4718 prevNod[2], midlNod[1], nextNod[2] );
4720 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4721 prevNod[2], nextNod[2], midlNod[0]);
4724 else if ( nbDouble == 3 )
4726 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4727 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4728 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4735 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4736 if ( nbDouble > 0 ) break;
4738 if ( nbSame == 0 ) // ---> hexahedron
4739 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4740 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4742 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4743 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4744 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4745 nextNod[ iSameNode ]);
4746 newElems.push_back( aNewElem );
4747 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4748 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4749 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4751 else if ( nbSame == 2 ) { // ---> pentahedron
4752 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4753 // iBeforeSame is same too
4754 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4755 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4756 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4758 // iAfterSame is same too
4759 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4760 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4761 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4765 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4766 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4767 if ( nbDouble+nbSame != 3 ) break;
4769 // ---> pentahedron with 15 nodes
4770 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4771 nextNod[0], nextNod[1], nextNod[2],
4772 prevNod[3], prevNod[4], prevNod[5],
4773 nextNod[3], nextNod[4], nextNod[5],
4774 midlNod[0], midlNod[1], midlNod[2]);
4776 else if(nbSame==1) {
4777 // ---> 2d order pyramid of 13 nodes
4778 int apex = iSameNode;
4779 int i0 = ( apex + 1 ) % nbCorners;
4780 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4784 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4785 nextNod[i0], nextNod[i1], prevNod[apex],
4786 prevNod[i01], midlNod[i0],
4787 nextNod[i01], midlNod[i1],
4788 prevNod[i1a], prevNod[i0a],
4789 nextNod[i0a], nextNod[i1a]);
4791 else if(nbSame==2) {
4792 // ---> 2d order tetrahedron of 10 nodes
4793 int n1 = iNotSameNode;
4794 int n2 = ( n1 + 1 ) % nbCorners;
4795 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4799 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4800 prevNod[n12], prevNod[n23], prevNod[n31],
4801 midlNod[n1], nextNod[n12], nextNod[n31]);
4805 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4807 if ( nbDouble != 4 ) break;
4808 // ---> hexahedron with 20 nodes
4809 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4810 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4811 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4812 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4813 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4815 else if(nbSame==1) {
4816 // ---> pyramid + pentahedron - can not be created since it is needed
4817 // additional middle node at the center of face
4818 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4821 else if( nbSame == 2 ) {
4822 if ( nbDouble != 2 ) break;
4823 // ---> 2d order Pentahedron with 15 nodes
4825 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4826 // iBeforeSame is same too
4833 // iAfterSame is same too
4843 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4844 prevNod[n4], prevNod[n5], nextNod[n5],
4845 prevNod[n12], midlNod[n2], nextNod[n12],
4846 prevNod[n45], midlNod[n5], nextNod[n45],
4847 prevNod[n14], prevNod[n25], nextNod[n25]);
4851 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4853 if( nbSame == 0 && nbDouble == 9 ) {
4854 // ---> tri-quadratic hexahedron with 27 nodes
4855 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4856 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4857 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4858 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4859 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4860 prevNod[8], // bottom center
4861 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4862 nextNod[8], // top center
4863 midlNod[8]);// elem center
4871 case SMDSEntity_Polygon: { // sweep POLYGON
4873 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4874 // ---> hexagonal prism
4875 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4876 prevNod[3], prevNod[4], prevNod[5],
4877 nextNod[0], nextNod[1], nextNod[2],
4878 nextNod[3], nextNod[4], nextNod[5]);
4882 case SMDSEntity_Ball:
4887 } // switch ( baseType )
4890 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4892 if ( baseType != SMDSEntity_Polygon )
4894 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4895 SMDS_MeshCell::applyInterlace( ind, prevNod );
4896 SMDS_MeshCell::applyInterlace( ind, nextNod );
4897 SMDS_MeshCell::applyInterlace( ind, midlNod );
4898 SMDS_MeshCell::applyInterlace( ind, itNN );
4899 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4900 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4902 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4903 vector<int> quantities (nbNodes + 2);
4904 polyedre_nodes.clear();
4908 for (int inode = 0; inode < nbNodes; inode++)
4909 polyedre_nodes.push_back( prevNod[inode] );
4910 quantities.push_back( nbNodes );
4913 polyedre_nodes.push_back( nextNod[0] );
4914 for (int inode = nbNodes; inode-1; --inode )
4915 polyedre_nodes.push_back( nextNod[inode-1] );
4916 quantities.push_back( nbNodes );
4924 const int iQuad = elem->IsQuadratic();
4925 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4927 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4928 int inextface = (iface+1+iQuad) % nbNodes;
4929 int imid = (iface+1) % nbNodes;
4930 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4931 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4932 polyedre_nodes.push_back( prevNod[iface] ); // 1
4933 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4935 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4936 polyedre_nodes.push_back( nextNod[iface] ); // 2
4938 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4939 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4941 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4942 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4944 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4945 if ( nbFaceNodes > 2 )
4946 quantities.push_back( nbFaceNodes );
4947 else // degenerated face
4948 polyedre_nodes.resize( prevNbNodes );
4950 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4952 } // try to create a polyherdal prism
4955 newElems.push_back( aNewElem );
4956 myLastCreatedElems.Append(aNewElem);
4957 srcElements.Append( elem );
4960 // set new prev nodes
4961 for ( iNode = 0; iNode < nbNodes; iNode++ )
4962 prevNod[ iNode ] = nextNod[ iNode ];
4967 //=======================================================================
4969 * \brief Create 1D and 2D elements around swept elements
4970 * \param mapNewNodes - source nodes and ones generated from them
4971 * \param newElemsMap - source elements and ones generated from them
4972 * \param elemNewNodesMap - nodes generated from each node of each element
4973 * \param elemSet - all swept elements
4974 * \param nbSteps - number of sweeping steps
4975 * \param srcElements - to append elem for each generated element
4977 //=======================================================================
4979 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4980 TTElemOfElemListMap & newElemsMap,
4981 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4982 TIDSortedElemSet& elemSet,
4984 SMESH_SequenceOfElemPtr& srcElements)
4986 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4987 SMESHDS_Mesh* aMesh = GetMeshDS();
4989 // Find nodes belonging to only one initial element - sweep them into edges.
4991 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4992 for ( ; nList != mapNewNodes.end(); nList++ )
4994 const SMDS_MeshNode* node =
4995 static_cast<const SMDS_MeshNode*>( nList->first );
4996 if ( newElemsMap.count( node ))
4997 continue; // node was extruded into edge
4998 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4999 int nbInitElems = 0;
5000 const SMDS_MeshElement* el = 0;
5001 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
5002 while ( eIt->more() && nbInitElems < 2 ) {
5003 const SMDS_MeshElement* e = eIt->next();
5004 SMDSAbs_ElementType type = e->GetType();
5005 if ( type == SMDSAbs_Volume ||
5009 if ( type > highType ) {
5016 if ( nbInitElems == 1 ) {
5017 bool NotCreateEdge = el && el->IsMediumNode(node);
5018 if(!NotCreateEdge) {
5019 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5020 list<const SMDS_MeshElement*> newEdges;
5021 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5026 // Make a ceiling for each element ie an equal element of last new nodes.
5027 // Find free links of faces - make edges and sweep them into faces.
5029 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5031 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
5032 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5033 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5035 const SMDS_MeshElement* elem = itElem->first;
5036 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5038 if(itElem->second.size()==0) continue;
5040 const bool isQuadratic = elem->IsQuadratic();
5042 if ( elem->GetType() == SMDSAbs_Edge ) {
5043 // create a ceiling edge
5044 if ( !isQuadratic ) {
5045 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5046 vecNewNodes[ 1 ]->second.back())) {
5047 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5048 vecNewNodes[ 1 ]->second.back()));
5049 srcElements.Append( elem );
5053 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5054 vecNewNodes[ 1 ]->second.back(),
5055 vecNewNodes[ 2 ]->second.back())) {
5056 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5057 vecNewNodes[ 1 ]->second.back(),
5058 vecNewNodes[ 2 ]->second.back()));
5059 srcElements.Append( elem );
5063 if ( elem->GetType() != SMDSAbs_Face )
5066 bool hasFreeLinks = false;
5068 TIDSortedElemSet avoidSet;
5069 avoidSet.insert( elem );
5071 set<const SMDS_MeshNode*> aFaceLastNodes;
5072 int iNode, nbNodes = vecNewNodes.size();
5073 if ( !isQuadratic ) {
5074 // loop on the face nodes
5075 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5076 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5077 // look for free links of the face
5078 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5079 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5080 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5081 // check if a link n1-n2 is free
5082 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5083 hasFreeLinks = true;
5084 // make a new edge and a ceiling for a new edge
5085 const SMDS_MeshElement* edge;
5086 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5087 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5088 srcElements.Append( myLastCreatedElems.Last() );
5090 n1 = vecNewNodes[ iNode ]->second.back();
5091 n2 = vecNewNodes[ iNext ]->second.back();
5092 if ( !aMesh->FindEdge( n1, n2 )) {
5093 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5094 srcElements.Append( edge );
5099 else { // elem is quadratic face
5100 int nbn = nbNodes/2;
5101 for ( iNode = 0; iNode < nbn; iNode++ ) {
5102 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5103 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5104 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5105 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5106 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5107 // check if a link is free
5108 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5109 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5110 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5111 hasFreeLinks = true;
5112 // make an edge and a ceiling for a new edge
5114 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5115 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5116 srcElements.Append( elem );
5118 n1 = vecNewNodes[ iNode ]->second.back();
5119 n2 = vecNewNodes[ iNext ]->second.back();
5120 n3 = vecNewNodes[ iNode+nbn ]->second.back();
5121 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5122 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5123 srcElements.Append( elem );
5127 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5128 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5132 // sweep free links into faces
5134 if ( hasFreeLinks ) {
5135 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5136 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5138 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5139 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5140 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5141 initNodeSet.insert( vecNewNodes[ iNode ]->first );
5142 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5144 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
5145 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5146 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5148 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5149 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5150 std::advance( v, volNb );
5151 // find indices of free faces of a volume and their source edges
5152 list< int > freeInd;
5153 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5154 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5155 int iF, nbF = vTool.NbFaces();
5156 for ( iF = 0; iF < nbF; iF ++ ) {
5157 if (vTool.IsFreeFace( iF ) &&
5158 vTool.GetFaceNodes( iF, faceNodeSet ) &&
5159 initNodeSet != faceNodeSet) // except an initial face
5161 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5163 if ( faceNodeSet == initNodeSetNoCenter )
5165 freeInd.push_back( iF );
5166 // find source edge of a free face iF
5167 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5168 vector<const SMDS_MeshNode*>::iterator lastCommom;
5169 commonNodes.resize( nbNodes, 0 );
5170 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5171 initNodeSet.begin(), initNodeSet.end(),
5172 commonNodes.begin());
5173 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5174 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5176 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5178 if ( !srcEdges.back() )
5180 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5181 << iF << " of volume #" << vTool.ID() << endl;
5186 if ( freeInd.empty() )
5189 // create wall faces for all steps;
5190 // if such a face has been already created by sweep of edge,
5191 // assure that its orientation is OK
5192 for ( int iStep = 0; iStep < nbSteps; iStep++ )
5194 vTool.Set( *v, /*ignoreCentralNodes=*/false );
5195 vTool.SetExternalNormal();
5196 const int nextShift = vTool.IsForward() ? +1 : -1;
5197 list< int >::iterator ind = freeInd.begin();
5198 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5199 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5201 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5202 int nbn = vTool.NbFaceNodes( *ind );
5203 const SMDS_MeshElement * f = 0;
5204 if ( nbn == 3 ) ///// triangle
5206 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5208 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5210 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5212 nodes[ 1 + nextShift ] };
5214 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5216 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5220 else if ( nbn == 4 ) ///// quadrangle
5222 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5224 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5226 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5227 nodes[ 2 ], nodes[ 2+nextShift ] };
5229 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5231 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5232 newOrder[ 2 ], newOrder[ 3 ]));
5235 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5237 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5239 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5241 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5243 nodes[2 + 2*nextShift],
5244 nodes[3 - 2*nextShift],
5246 nodes[3 + 2*nextShift]};
5248 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5250 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5258 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5260 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5261 nodes[1], nodes[3], nodes[5], nodes[7] );
5263 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5265 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5266 nodes[4 - 2*nextShift],
5268 nodes[4 + 2*nextShift],
5270 nodes[5 - 2*nextShift],
5272 nodes[5 + 2*nextShift] };
5274 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5276 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5277 newOrder[ 2 ], newOrder[ 3 ],
5278 newOrder[ 4 ], newOrder[ 5 ],
5279 newOrder[ 6 ], newOrder[ 7 ]));
5282 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5284 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5285 SMDSAbs_Face, /*noMedium=*/false);
5287 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5289 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5290 nodes[4 - 2*nextShift],
5292 nodes[4 + 2*nextShift],
5294 nodes[5 - 2*nextShift],
5296 nodes[5 + 2*nextShift],
5299 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5301 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5302 newOrder[ 2 ], newOrder[ 3 ],
5303 newOrder[ 4 ], newOrder[ 5 ],
5304 newOrder[ 6 ], newOrder[ 7 ],
5308 else //////// polygon
5310 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5311 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5313 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5315 if ( !vTool.IsForward() )
5316 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5318 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5320 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5324 while ( srcElements.Length() < myLastCreatedElems.Length() )
5325 srcElements.Append( *srcEdge );
5327 } // loop on free faces
5329 // go to the next volume
5331 while ( iVol++ < nbVolumesByStep ) v++;
5334 } // loop on volumes of one step
5335 } // sweep free links into faces
5337 // Make a ceiling face with a normal external to a volume
5339 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5340 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5341 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5343 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5344 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5345 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5349 lastVol.SetExternalNormal();
5350 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5351 const int nbn = lastVol.NbFaceNodes( iF );
5352 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5353 if ( !hasFreeLinks ||
5354 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5356 const vector<int>& interlace =
5357 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5358 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5360 AddElement( nodeVec, anyFace.Init( elem ));
5362 while ( srcElements.Length() < myLastCreatedElems.Length() )
5363 srcElements.Append( elem );
5366 } // loop on swept elements
5369 //=======================================================================
5370 //function : RotationSweep
5372 //=======================================================================
5374 SMESH_MeshEditor::PGroupIDs
5375 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5376 const gp_Ax1& theAxis,
5377 const double theAngle,
5378 const int theNbSteps,
5379 const double theTol,
5380 const bool theMakeGroups,
5381 const bool theMakeWalls)
5383 myLastCreatedElems.Clear();
5384 myLastCreatedNodes.Clear();
5386 // source elements for each generated one
5387 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5389 MESSAGE( "RotationSweep()");
5391 aTrsf.SetRotation( theAxis, theAngle );
5393 aTrsf2.SetRotation( theAxis, theAngle/2. );
5395 gp_Lin aLine( theAxis );
5396 double aSqTol = theTol * theTol;
5398 SMESHDS_Mesh* aMesh = GetMeshDS();
5400 TNodeOfNodeListMap mapNewNodes;
5401 TElemOfVecOfNnlmiMap mapElemNewNodes;
5402 TTElemOfElemListMap newElemsMap;
5404 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5405 myMesh->NbFaces(ORDER_QUADRATIC) +
5406 myMesh->NbVolumes(ORDER_QUADRATIC) );
5407 // loop on theElemSets
5408 setElemsFirst( theElemSets );
5409 TIDSortedElemSet::iterator itElem;
5410 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5412 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5413 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5414 const SMDS_MeshElement* elem = *itElem;
5415 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5417 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5418 newNodesItVec.reserve( elem->NbNodes() );
5420 // loop on elem nodes
5421 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5422 while ( itN->more() )
5424 const SMDS_MeshNode* node = cast2Node( itN->next() );
5426 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5428 aXYZ.Coord( coord[0], coord[1], coord[2] );
5429 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5431 // check if a node has been already sweeped
5432 TNodeOfNodeListMapItr nIt =
5433 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5434 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5435 if ( listNewNodes.empty() )
5437 // check if we are to create medium nodes between corner ones
5438 bool needMediumNodes = false;
5439 if ( isQuadraticMesh )
5441 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5442 while (it->more() && !needMediumNodes )
5444 const SMDS_MeshElement* invElem = it->next();
5445 if ( invElem != elem && !theElems.count( invElem )) continue;
5446 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5447 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5448 needMediumNodes = true;
5453 const SMDS_MeshNode * newNode = node;
5454 for ( int i = 0; i < theNbSteps; i++ ) {
5456 if ( needMediumNodes ) // create a medium node
5458 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5459 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5460 myLastCreatedNodes.Append(newNode);
5461 srcNodes.Append( node );
5462 listNewNodes.push_back( newNode );
5463 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5466 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5468 // create a corner node
5469 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5470 myLastCreatedNodes.Append(newNode);
5471 srcNodes.Append( node );
5472 listNewNodes.push_back( newNode );
5475 listNewNodes.push_back( newNode );
5476 // if ( needMediumNodes )
5477 // listNewNodes.push_back( newNode );
5481 newNodesItVec.push_back( nIt );
5483 // make new elements
5484 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5489 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5491 PGroupIDs newGroupIDs;
5492 if ( theMakeGroups )
5493 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5498 //=======================================================================
5499 //function : ExtrusParam
5500 //purpose : standard construction
5501 //=======================================================================
5503 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5504 const int theNbSteps,
5506 const double theTolerance):
5508 myFlags( theFlags ),
5509 myTolerance( theTolerance ),
5510 myElemsToUse( NULL )
5512 mySteps = new TColStd_HSequenceOfReal;
5513 const double stepSize = theStep.Magnitude();
5514 for (int i=1; i<=theNbSteps; i++ )
5515 mySteps->Append( stepSize );
5517 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5518 ( theTolerance > 0 ))
5520 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5524 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5528 //=======================================================================
5529 //function : ExtrusParam
5530 //purpose : steps are given explicitly
5531 //=======================================================================
5533 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5534 Handle(TColStd_HSequenceOfReal) theSteps,
5536 const double theTolerance):
5538 mySteps( theSteps ),
5539 myFlags( theFlags ),
5540 myTolerance( theTolerance ),
5541 myElemsToUse( NULL )
5543 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5544 ( theTolerance > 0 ))
5546 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5550 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5554 //=======================================================================
5555 //function : ExtrusParam
5556 //purpose : for extrusion by normal
5557 //=======================================================================
5559 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5560 const int theNbSteps,
5564 mySteps( new TColStd_HSequenceOfReal ),
5565 myFlags( theFlags ),
5567 myElemsToUse( NULL )
5569 for (int i = 0; i < theNbSteps; i++ )
5570 mySteps->Append( theStepSize );
5574 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5578 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5582 //=======================================================================
5583 //function : ExtrusParam::SetElementsToUse
5584 //purpose : stores elements to use for extrusion by normal, depending on
5585 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5586 //=======================================================================
5588 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5590 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5593 //=======================================================================
5594 //function : ExtrusParam::beginStepIter
5595 //purpose : prepare iteration on steps
5596 //=======================================================================
5598 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5600 myWithMediumNodes = withMediumNodes;
5604 //=======================================================================
5605 //function : ExtrusParam::moreSteps
5606 //purpose : are there more steps?
5607 //=======================================================================
5609 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5611 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5613 //=======================================================================
5614 //function : ExtrusParam::nextStep
5615 //purpose : returns the next step
5616 //=======================================================================
5618 double SMESH_MeshEditor::ExtrusParam::nextStep()
5621 if ( !myCurSteps.empty() )
5623 res = myCurSteps.back();
5624 myCurSteps.pop_back();
5626 else if ( myNextStep <= mySteps->Length() )
5628 myCurSteps.push_back( mySteps->Value( myNextStep ));
5630 if ( myWithMediumNodes )
5632 myCurSteps.back() /= 2.;
5633 myCurSteps.push_back( myCurSteps.back() );
5640 //=======================================================================
5641 //function : ExtrusParam::makeNodesByDir
5642 //purpose : create nodes for standard extrusion
5643 //=======================================================================
5645 int SMESH_MeshEditor::ExtrusParam::
5646 makeNodesByDir( SMESHDS_Mesh* mesh,
5647 const SMDS_MeshNode* srcNode,
5648 std::list<const SMDS_MeshNode*> & newNodes,
5649 const bool makeMediumNodes)
5651 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5654 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5656 p += myDir.XYZ() * nextStep();
5657 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5658 newNodes.push_back( newNode );
5663 //=======================================================================
5664 //function : ExtrusParam::makeNodesByDirAndSew
5665 //purpose : create nodes for standard extrusion with sewing
5666 //=======================================================================
5668 int SMESH_MeshEditor::ExtrusParam::
5669 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5670 const SMDS_MeshNode* srcNode,
5671 std::list<const SMDS_MeshNode*> & newNodes,
5672 const bool makeMediumNodes)
5674 gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5677 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5679 P1 += myDir.XYZ() * nextStep();
5681 // try to search in sequence of existing nodes
5682 // if myNodes.Length()>0 we 'nave to use given sequence
5683 // else - use all nodes of mesh
5684 const SMDS_MeshNode * node = 0;
5685 if ( myNodes.Length() > 0 ) {
5687 for(i=1; i<=myNodes.Length(); i++) {
5688 gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5689 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5691 node = myNodes.Value(i);
5697 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5698 while(itn->more()) {
5699 SMESH_TNodeXYZ P2( itn->next() );
5700 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5709 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5711 newNodes.push_back( node );
5718 //=======================================================================
5719 //function : ExtrusParam::makeNodesByNormal2D
5720 //purpose : create nodes for extrusion using normals of faces
5721 //=======================================================================
5723 int SMESH_MeshEditor::ExtrusParam::
5724 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5725 const SMDS_MeshNode* srcNode,
5726 std::list<const SMDS_MeshNode*> & newNodes,
5727 const bool makeMediumNodes)
5729 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5731 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5733 // get normals to faces sharing srcNode
5734 vector< gp_XYZ > norms, baryCenters;
5735 gp_XYZ norm, avgNorm( 0,0,0 );
5736 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5737 while ( faceIt->more() )
5739 const SMDS_MeshElement* face = faceIt->next();
5740 if ( myElemsToUse && !myElemsToUse->count( face ))
5742 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5744 norms.push_back( norm );
5746 if ( !alongAvgNorm )
5750 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5751 bc += SMESH_TNodeXYZ( nIt->next() );
5752 baryCenters.push_back( bc / nbN );
5757 if ( norms.empty() ) return 0;
5759 double normSize = avgNorm.Modulus();
5760 if ( normSize < std::numeric_limits<double>::min() )
5763 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5766 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5769 avgNorm /= normSize;
5772 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5775 double stepSize = nextStep();
5777 if ( norms.size() > 1 )
5779 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5781 // translate plane of a face
5782 baryCenters[ iF ] += norms[ iF ] * stepSize;
5784 // find point of intersection of the face plane located at baryCenters[ iF ]
5785 // and avgNorm located at pNew
5786 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5787 double dot = ( norms[ iF ] * avgNorm );
5788 if ( dot < std::numeric_limits<double>::min() )
5789 dot = stepSize * 1e-3;
5790 double step = -( norms[ iF ] * pNew + d ) / dot;
5791 pNew += step * avgNorm;
5796 pNew += stepSize * avgNorm;
5800 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5801 newNodes.push_back( newNode );
5806 //=======================================================================
5807 //function : ExtrusParam::makeNodesByNormal1D
5808 //purpose : create nodes for extrusion using normals of edges
5809 //=======================================================================
5811 int SMESH_MeshEditor::ExtrusParam::
5812 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5813 const SMDS_MeshNode* srcNode,
5814 std::list<const SMDS_MeshNode*> & newNodes,
5815 const bool makeMediumNodes)
5817 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5821 //=======================================================================
5822 //function : ExtrusionSweep
5824 //=======================================================================
5826 SMESH_MeshEditor::PGroupIDs
5827 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5828 const gp_Vec& theStep,
5829 const int theNbSteps,
5830 TTElemOfElemListMap& newElemsMap,
5832 const double theTolerance)
5834 ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5835 return ExtrusionSweep( theElems, aParams, newElemsMap );
5839 //=======================================================================
5840 //function : ExtrusionSweep
5842 //=======================================================================
5844 SMESH_MeshEditor::PGroupIDs
5845 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5846 ExtrusParam& theParams,
5847 TTElemOfElemListMap& newElemsMap)
5849 myLastCreatedElems.Clear();
5850 myLastCreatedNodes.Clear();
5852 // source elements for each generated one
5853 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5855 //SMESHDS_Mesh* aMesh = GetMeshDS();
5857 setElemsFirst( theElemSets );
5858 const int nbSteps = theParams.NbSteps();
5859 theParams.SetElementsToUse( theElemSets[0] );
5861 TNodeOfNodeListMap mapNewNodes;
5862 //TNodeOfNodeVecMap mapNewNodes;
5863 TElemOfVecOfNnlmiMap mapElemNewNodes;
5864 //TElemOfVecOfMapNodesMap mapElemNewNodes;
5866 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5867 myMesh->NbFaces(ORDER_QUADRATIC) +
5868 myMesh->NbVolumes(ORDER_QUADRATIC) );
5870 TIDSortedElemSet::iterator itElem;
5871 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5873 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5874 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5876 // check element type
5877 const SMDS_MeshElement* elem = *itElem;
5878 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5881 const size_t nbNodes = elem->NbNodes();
5882 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5883 newNodesItVec.reserve( nbNodes );
5885 // loop on elem nodes
5886 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5887 while ( itN->more() )
5889 // check if a node has been already sweeped
5890 const SMDS_MeshNode* node = cast2Node( itN->next() );
5891 TNodeOfNodeListMap::iterator nIt =
5892 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5893 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5894 if ( listNewNodes.empty() )
5898 // check if we are to create medium nodes between corner ones
5899 bool needMediumNodes = false;
5900 if ( isQuadraticMesh )
5902 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5903 while (it->more() && !needMediumNodes )
5905 const SMDS_MeshElement* invElem = it->next();
5906 if ( invElem != elem && !theElems.count( invElem )) continue;
5907 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5908 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5909 needMediumNodes = true;
5912 // create nodes for all steps
5913 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5915 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5916 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5918 myLastCreatedNodes.Append( *newNodesIt );
5919 srcNodes.Append( node );
5924 break; // newNodesItVec will be shorter than nbNodes
5927 newNodesItVec.push_back( nIt );
5929 // make new elements
5930 if ( newNodesItVec.size() == nbNodes )
5931 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5935 if ( theParams.ToMakeBoundary() ) {
5936 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5938 PGroupIDs newGroupIDs;
5939 if ( theParams.ToMakeGroups() )
5940 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5945 //=======================================================================
5946 //function : ExtrusionAlongTrack
5948 //=======================================================================
5949 SMESH_MeshEditor::Extrusion_Error
5950 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5951 SMESH_subMesh* theTrack,
5952 const SMDS_MeshNode* theN1,
5953 const bool theHasAngles,
5954 list<double>& theAngles,
5955 const bool theLinearVariation,
5956 const bool theHasRefPoint,
5957 const gp_Pnt& theRefPoint,
5958 const bool theMakeGroups)
5960 MESSAGE("ExtrusionAlongTrack");
5961 myLastCreatedElems.Clear();
5962 myLastCreatedNodes.Clear();
5965 std::list<double> aPrms;
5966 TIDSortedElemSet::iterator itElem;
5969 TopoDS_Edge aTrackEdge;
5970 TopoDS_Vertex aV1, aV2;
5972 SMDS_ElemIteratorPtr aItE;
5973 SMDS_NodeIteratorPtr aItN;
5974 SMDSAbs_ElementType aTypeE;
5976 TNodeOfNodeListMap mapNewNodes;
5979 aNbE = theElements[0].size() + theElements[1].size();
5982 return EXTR_NO_ELEMENTS;
5984 // 1.1 Track Pattern
5987 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5989 return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
5990 theHasAngles, theAngles, theLinearVariation,
5991 theHasRefPoint, theRefPoint, theMakeGroups );
5993 aItE = pSubMeshDS->GetElements();
5994 while ( aItE->more() ) {
5995 const SMDS_MeshElement* pE = aItE->next();
5996 aTypeE = pE->GetType();
5997 // Pattern must contain links only
5998 if ( aTypeE != SMDSAbs_Edge )
5999 return EXTR_PATH_NOT_EDGE;
6002 list<SMESH_MeshEditor_PathPoint> fullList;
6004 const TopoDS_Shape& aS = theTrack->GetSubShape();
6005 // Sub-shape for the Pattern must be an Edge or Wire
6006 if( aS.ShapeType() == TopAbs_EDGE ) {
6007 aTrackEdge = TopoDS::Edge( aS );
6008 // the Edge must not be degenerated
6009 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6010 return EXTR_BAD_PATH_SHAPE;
6011 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6012 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
6013 const SMDS_MeshNode* aN1 = aItN->next();
6014 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
6015 const SMDS_MeshNode* aN2 = aItN->next();
6016 // starting node must be aN1 or aN2
6017 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6018 return EXTR_BAD_STARTING_NODE;
6019 aItN = pSubMeshDS->GetNodes();
6020 while ( aItN->more() ) {
6021 const SMDS_MeshNode* pNode = aItN->next();
6022 const SMDS_EdgePosition* pEPos =
6023 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6024 double aT = pEPos->GetUParameter();
6025 aPrms.push_back( aT );
6027 //Extrusion_Error err =
6028 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6029 } else if( aS.ShapeType() == TopAbs_WIRE ) {
6030 list< SMESH_subMesh* > LSM;
6031 TopTools_SequenceOfShape Edges;
6032 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6033 while(itSM->more()) {
6034 SMESH_subMesh* SM = itSM->next();
6036 const TopoDS_Shape& aS = SM->GetSubShape();
6039 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6040 int startNid = theN1->GetID();
6041 TColStd_MapOfInteger UsedNums;
6043 int NbEdges = Edges.Length();
6045 for(; i<=NbEdges; i++) {
6047 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6048 for(; itLSM!=LSM.end(); itLSM++) {
6050 if(UsedNums.Contains(k)) continue;
6051 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6052 SMESH_subMesh* locTrack = *itLSM;
6053 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6054 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6055 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6056 const SMDS_MeshNode* aN1 = aItN->next();
6057 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6058 const SMDS_MeshNode* aN2 = aItN->next();
6059 // starting node must be aN1 or aN2
6060 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6061 // 2. Collect parameters on the track edge
6063 aItN = locMeshDS->GetNodes();
6064 while ( aItN->more() ) {
6065 const SMDS_MeshNode* pNode = aItN->next();
6066 const SMDS_EdgePosition* pEPos =
6067 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6068 double aT = pEPos->GetUParameter();
6069 aPrms.push_back( aT );
6071 list<SMESH_MeshEditor_PathPoint> LPP;
6072 //Extrusion_Error err =
6073 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6074 LLPPs.push_back(LPP);
6076 // update startN for search following egde
6077 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6078 else startNid = aN1->GetID();
6082 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6083 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6084 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6085 for(; itPP!=firstList.end(); itPP++) {
6086 fullList.push_back( *itPP );
6088 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6089 fullList.pop_back();
6091 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6092 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6093 itPP = currList.begin();
6094 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6095 gp_Dir D1 = PP1.Tangent();
6096 gp_Dir D2 = PP2.Tangent();
6097 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6098 (D1.Z()+D2.Z())/2 ) );
6099 PP1.SetTangent(Dnew);
6100 fullList.push_back(PP1);
6102 for(; itPP!=firstList.end(); itPP++) {
6103 fullList.push_back( *itPP );
6105 PP1 = fullList.back();
6106 fullList.pop_back();
6108 // if wire not closed
6109 fullList.push_back(PP1);
6113 return EXTR_BAD_PATH_SHAPE;
6116 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6117 theHasRefPoint, theRefPoint, theMakeGroups);
6121 //=======================================================================
6122 //function : ExtrusionAlongTrack
6124 //=======================================================================
6125 SMESH_MeshEditor::Extrusion_Error
6126 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6127 SMESH_Mesh* theTrack,
6128 const SMDS_MeshNode* theN1,
6129 const bool theHasAngles,
6130 list<double>& theAngles,
6131 const bool theLinearVariation,
6132 const bool theHasRefPoint,
6133 const gp_Pnt& theRefPoint,
6134 const bool theMakeGroups)
6136 myLastCreatedElems.Clear();
6137 myLastCreatedNodes.Clear();
6140 std::list<double> aPrms;
6141 TIDSortedElemSet::iterator itElem;
6144 TopoDS_Edge aTrackEdge;
6145 TopoDS_Vertex aV1, aV2;
6147 SMDS_ElemIteratorPtr aItE;
6148 SMDS_NodeIteratorPtr aItN;
6149 SMDSAbs_ElementType aTypeE;
6151 TNodeOfNodeListMap mapNewNodes;
6154 aNbE = theElements[0].size() + theElements[1].size();
6157 return EXTR_NO_ELEMENTS;
6159 // 1.1 Track Pattern
6162 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6164 aItE = pMeshDS->elementsIterator();
6165 while ( aItE->more() ) {
6166 const SMDS_MeshElement* pE = aItE->next();
6167 aTypeE = pE->GetType();
6168 // Pattern must contain links only
6169 if ( aTypeE != SMDSAbs_Edge )
6170 return EXTR_PATH_NOT_EDGE;
6173 list<SMESH_MeshEditor_PathPoint> fullList;
6175 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6177 if ( !theTrack->HasShapeToMesh() ) {
6178 //Mesh without shape
6179 const SMDS_MeshNode* currentNode = NULL;
6180 const SMDS_MeshNode* prevNode = theN1;
6181 std::vector<const SMDS_MeshNode*> aNodesList;
6182 aNodesList.push_back(theN1);
6183 int nbEdges = 0, conn=0;
6184 const SMDS_MeshElement* prevElem = NULL;
6185 const SMDS_MeshElement* currentElem = NULL;
6186 int totalNbEdges = theTrack->NbEdges();
6187 SMDS_ElemIteratorPtr nIt;
6190 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6191 return EXTR_BAD_STARTING_NODE;
6194 conn = nbEdgeConnectivity(theN1);
6196 return EXTR_PATH_NOT_EDGE;
6198 aItE = theN1->GetInverseElementIterator();
6199 prevElem = aItE->next();
6200 currentElem = prevElem;
6202 if(totalNbEdges == 1 ) {
6203 nIt = currentElem->nodesIterator();
6204 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6205 if(currentNode == prevNode)
6206 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6207 aNodesList.push_back(currentNode);
6209 nIt = currentElem->nodesIterator();
6210 while( nIt->more() ) {
6211 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6212 if(currentNode == prevNode)
6213 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6214 aNodesList.push_back(currentNode);
6216 //case of the closed mesh
6217 if(currentNode == theN1) {
6222 conn = nbEdgeConnectivity(currentNode);
6224 return EXTR_PATH_NOT_EDGE;
6225 }else if( conn == 1 && nbEdges > 0 ) {
6230 prevNode = currentNode;
6231 aItE = currentNode->GetInverseElementIterator();
6232 currentElem = aItE->next();
6233 if( currentElem == prevElem)
6234 currentElem = aItE->next();
6235 nIt = currentElem->nodesIterator();
6236 prevElem = currentElem;
6242 if(nbEdges != totalNbEdges)
6243 return EXTR_PATH_NOT_EDGE;
6245 TopTools_SequenceOfShape Edges;
6246 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6247 int startNid = theN1->GetID();
6248 for ( size_t i = 1; i < aNodesList.size(); i++ )
6250 gp_Pnt p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6251 gp_Pnt p2 = SMESH_TNodeXYZ( aNodesList[i] );
6252 TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6253 list<SMESH_MeshEditor_PathPoint> LPP;
6255 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6256 LLPPs.push_back(LPP);
6257 if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i ]->GetID();
6258 else startNid = aNodesList[i-1]->GetID();
6261 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6262 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6263 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6264 for(; itPP!=firstList.end(); itPP++) {
6265 fullList.push_back( *itPP );
6268 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6269 SMESH_MeshEditor_PathPoint PP2;
6270 fullList.pop_back();
6272 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6273 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6274 itPP = currList.begin();
6275 PP2 = currList.front();
6276 gp_Dir D1 = PP1.Tangent();
6277 gp_Dir D2 = PP2.Tangent();
6278 gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6279 PP1.SetTangent(Dnew);
6280 fullList.push_back(PP1);
6282 for(; itPP!=currList.end(); itPP++) {
6283 fullList.push_back( *itPP );
6285 PP1 = fullList.back();
6286 fullList.pop_back();
6288 fullList.push_back(PP1);
6290 } // Sub-shape for the Pattern must be an Edge or Wire
6291 else if ( aS.ShapeType() == TopAbs_EDGE )
6293 aTrackEdge = TopoDS::Edge( aS );
6294 // the Edge must not be degenerated
6295 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6296 return EXTR_BAD_PATH_SHAPE;
6297 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6298 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6299 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6300 // starting node must be aN1 or aN2
6301 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6302 return EXTR_BAD_STARTING_NODE;
6303 aItN = pMeshDS->nodesIterator();
6304 while ( aItN->more() ) {
6305 const SMDS_MeshNode* pNode = aItN->next();
6306 if( pNode==aN1 || pNode==aN2 ) continue;
6307 const SMDS_EdgePosition* pEPos =
6308 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6309 double aT = pEPos->GetUParameter();
6310 aPrms.push_back( aT );
6312 //Extrusion_Error err =
6313 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6315 else if( aS.ShapeType() == TopAbs_WIRE ) {
6316 list< SMESH_subMesh* > LSM;
6317 TopTools_SequenceOfShape Edges;
6318 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6319 for(; eExp.More(); eExp.Next()) {
6320 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6321 if( SMESH_Algo::isDegenerated(E) ) continue;
6322 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6328 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6329 TopoDS_Vertex aVprev;
6330 TColStd_MapOfInteger UsedNums;
6331 int NbEdges = Edges.Length();
6333 for(; i<=NbEdges; i++) {
6335 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6336 for(; itLSM!=LSM.end(); itLSM++) {
6338 if(UsedNums.Contains(k)) continue;
6339 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6340 SMESH_subMesh* locTrack = *itLSM;
6341 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6342 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6343 bool aN1isOK = false, aN2isOK = false;
6344 if ( aVprev.IsNull() ) {
6345 // if previous vertex is not yet defined, it means that we in the beginning of wire
6346 // and we have to find initial vertex corresponding to starting node theN1
6347 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6348 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6349 // starting node must be aN1 or aN2
6350 aN1isOK = ( aN1 && aN1 == theN1 );
6351 aN2isOK = ( aN2 && aN2 == theN1 );
6354 // we have specified ending vertex of the previous edge on the previous iteration
6355 // and we have just to check that it corresponds to any vertex in current segment
6356 aN1isOK = aVprev.IsSame( aV1 );
6357 aN2isOK = aVprev.IsSame( aV2 );
6359 if ( !aN1isOK && !aN2isOK ) continue;
6360 // 2. Collect parameters on the track edge
6362 aItN = locMeshDS->GetNodes();
6363 while ( aItN->more() ) {
6364 const SMDS_MeshNode* pNode = aItN->next();
6365 const SMDS_EdgePosition* pEPos =
6366 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6367 double aT = pEPos->GetUParameter();
6368 aPrms.push_back( aT );
6370 list<SMESH_MeshEditor_PathPoint> LPP;
6371 //Extrusion_Error err =
6372 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6373 LLPPs.push_back(LPP);
6375 // update startN for search following egde
6376 if ( aN1isOK ) aVprev = aV2;
6381 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6382 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6383 fullList.splice( fullList.end(), firstList );
6385 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6386 fullList.pop_back();
6388 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6389 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6390 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6391 gp_Dir D1 = PP1.Tangent();
6392 gp_Dir D2 = PP2.Tangent();
6393 gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6394 PP1.SetTangent(Dnew);
6395 fullList.push_back(PP1);
6396 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6397 PP1 = fullList.back();
6398 fullList.pop_back();
6400 // if wire not closed
6401 fullList.push_back(PP1);
6405 return EXTR_BAD_PATH_SHAPE;
6408 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6409 theHasRefPoint, theRefPoint, theMakeGroups);
6413 //=======================================================================
6414 //function : MakeEdgePathPoints
6415 //purpose : auxilary for ExtrusionAlongTrack
6416 //=======================================================================
6417 SMESH_MeshEditor::Extrusion_Error
6418 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
6419 const TopoDS_Edge& aTrackEdge,
6421 list<SMESH_MeshEditor_PathPoint>& LPP)
6423 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6425 aTolVec2=aTolVec*aTolVec;
6427 TopoDS_Vertex aV1, aV2;
6428 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6429 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6430 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6431 // 2. Collect parameters on the track edge
6432 aPrms.push_front( aT1 );
6433 aPrms.push_back( aT2 );
6436 if( FirstIsStart ) {
6447 SMESH_MeshEditor_PathPoint aPP;
6448 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6449 std::list<double>::iterator aItD = aPrms.begin();
6450 for(; aItD != aPrms.end(); ++aItD) {
6454 aC3D->D1( aT, aP3D, aVec );
6455 aL2 = aVec.SquareMagnitude();
6456 if ( aL2 < aTolVec2 )
6457 return EXTR_CANT_GET_TANGENT;
6458 gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6460 aPP.SetTangent( aTgt );
6461 aPP.SetParameter( aT );
6468 //=======================================================================
6469 //function : MakeExtrElements
6470 //purpose : auxilary for ExtrusionAlongTrack
6471 //=======================================================================
6472 SMESH_MeshEditor::Extrusion_Error
6473 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet theElemSets[2],
6474 list<SMESH_MeshEditor_PathPoint>& fullList,
6475 const bool theHasAngles,
6476 list<double>& theAngles,
6477 const bool theLinearVariation,
6478 const bool theHasRefPoint,
6479 const gp_Pnt& theRefPoint,
6480 const bool theMakeGroups)
6482 const int aNbTP = fullList.size();
6485 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6486 LinearAngleVariation(aNbTP-1, theAngles);
6488 // fill vector of path points with angles
6489 vector<SMESH_MeshEditor_PathPoint> aPPs;
6490 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6491 list<double>::iterator itAngles = theAngles.begin();
6492 aPPs.push_back( *itPP++ );
6493 for( ; itPP != fullList.end(); itPP++) {
6494 aPPs.push_back( *itPP );
6495 if ( theHasAngles && itAngles != theAngles.end() )
6496 aPPs.back().SetAngle( *itAngles++ );
6499 TNodeOfNodeListMap mapNewNodes;
6500 TElemOfVecOfNnlmiMap mapElemNewNodes;
6501 TTElemOfElemListMap newElemsMap;
6502 TIDSortedElemSet::iterator itElem;
6503 // source elements for each generated one
6504 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6506 // 3. Center of rotation aV0
6507 gp_Pnt aV0 = theRefPoint;
6508 if ( !theHasRefPoint )
6510 gp_XYZ aGC( 0.,0.,0. );
6511 TIDSortedElemSet newNodes;
6513 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6515 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6516 itElem = theElements.begin();
6517 for ( ; itElem != theElements.end(); itElem++ )
6519 const SMDS_MeshElement* elem = *itElem;
6520 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6521 while ( itN->more() ) {
6522 const SMDS_MeshElement* node = itN->next();
6523 if ( newNodes.insert( node ).second )
6524 aGC += SMESH_TNodeXYZ( node );
6528 aGC /= newNodes.size();
6530 } // if (!theHasRefPoint) {
6532 // 4. Processing the elements
6533 SMESHDS_Mesh* aMesh = GetMeshDS();
6534 list<const SMDS_MeshNode*> emptyList;
6536 setElemsFirst( theElemSets );
6537 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6539 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6540 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6542 const SMDS_MeshElement* elem = *itElem;
6544 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6545 newNodesItVec.reserve( elem->NbNodes() );
6547 // loop on elem nodes
6549 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6550 while ( itN->more() )
6553 // check if a node has been already processed
6554 const SMDS_MeshNode* node = cast2Node( itN->next() );
6555 TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6556 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6557 if ( listNewNodes.empty() )
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();
6574 for ( int j = 1; j < aNbTP; ++j ) {
6575 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6577 aDT1x = aPP1.Tangent();
6578 aAngle1x = aPP1.Angle();
6580 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6582 gp_Vec aV01x( aP0x, aP1x );
6583 aTrsf.SetTranslation( aV01x );
6586 aV1x = aV0x.Transformed( aTrsf );
6587 aPN1 = aPN0.Transformed( aTrsf );
6589 // rotation 1 [ T1,T0 ]
6590 aAngleT1T0=-aDT1x.Angle( aDT0x );
6591 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 if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6613 // create additional node
6614 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6615 const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6616 myLastCreatedNodes.Append(newNode);
6617 srcNodes.Append( node );
6618 listNewNodes.push_back( newNode );
6620 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6621 myLastCreatedNodes.Append(newNode);
6622 srcNodes.Append( node );
6623 listNewNodes.push_back( newNode );
6631 else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6633 // if current elem is quadratic and current node is not medium
6634 // we have to check - may be it is needed to insert additional nodes
6635 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6636 if ((int) listNewNodes.size() == aNbTP-1 )
6638 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6639 gp_XYZ P(node->X(), node->Y(), node->Z());
6640 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6642 for(i=0; i<aNbTP-1; i++) {
6643 const SMDS_MeshNode* N = *it;
6644 double x = ( N->X() + P.X() )/2.;
6645 double y = ( N->Y() + P.Y() )/2.;
6646 double z = ( N->Z() + P.Z() )/2.;
6647 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6648 srcNodes.Append( node );
6649 myLastCreatedNodes.Append(newN);
6652 P = gp_XYZ(N->X(),N->Y(),N->Z());
6654 listNewNodes.clear();
6655 for(i=0; i<2*(aNbTP-1); i++) {
6656 listNewNodes.push_back(aNodes[i]);
6661 newNodesItVec.push_back( nIt );
6664 // make new elements
6665 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6669 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6671 if ( theMakeGroups )
6672 generateGroups( srcNodes, srcElems, "extruded");
6678 //=======================================================================
6679 //function : LinearAngleVariation
6680 //purpose : auxilary for ExtrusionAlongTrack
6681 //=======================================================================
6682 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6683 list<double>& Angles)
6685 int nbAngles = Angles.size();
6686 if( nbSteps > nbAngles ) {
6687 vector<double> theAngles(nbAngles);
6688 list<double>::iterator it = Angles.begin();
6690 for(; it!=Angles.end(); it++) {
6692 theAngles[i] = (*it);
6695 double rAn2St = double( nbAngles ) / double( nbSteps );
6696 double angPrev = 0, angle;
6697 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6698 double angCur = rAn2St * ( iSt+1 );
6699 double angCurFloor = floor( angCur );
6700 double angPrevFloor = floor( angPrev );
6701 if ( angPrevFloor == angCurFloor )
6702 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6704 int iP = int( angPrevFloor );
6705 double angPrevCeil = ceil(angPrev);
6706 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6708 int iC = int( angCurFloor );
6709 if ( iC < nbAngles )
6710 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6712 iP = int( angPrevCeil );
6714 angle += theAngles[ iC ];
6716 res.push_back(angle);
6721 for(; it!=res.end(); it++)
6722 Angles.push_back( *it );
6727 //================================================================================
6729 * \brief Move or copy theElements applying theTrsf to their nodes
6730 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6731 * \param theTrsf - transformation to apply
6732 * \param theCopy - if true, create translated copies of theElems
6733 * \param theMakeGroups - if true and theCopy, create translated groups
6734 * \param theTargetMesh - mesh to copy translated elements into
6735 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6737 //================================================================================
6739 SMESH_MeshEditor::PGroupIDs
6740 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6741 const gp_Trsf& theTrsf,
6743 const bool theMakeGroups,
6744 SMESH_Mesh* theTargetMesh)
6746 myLastCreatedElems.Clear();
6747 myLastCreatedNodes.Clear();
6749 bool needReverse = false;
6750 string groupPostfix;
6751 switch ( theTrsf.Form() ) {
6753 MESSAGE("gp_PntMirror");
6755 groupPostfix = "mirrored";
6758 MESSAGE("gp_Ax1Mirror");
6759 groupPostfix = "mirrored";
6762 MESSAGE("gp_Ax2Mirror");
6764 groupPostfix = "mirrored";
6767 MESSAGE("gp_Rotation");
6768 groupPostfix = "rotated";
6770 case gp_Translation:
6771 MESSAGE("gp_Translation");
6772 groupPostfix = "translated";
6775 MESSAGE("gp_Scale");
6776 groupPostfix = "scaled";
6778 case gp_CompoundTrsf: // different scale by axis
6779 MESSAGE("gp_CompoundTrsf");
6780 groupPostfix = "scaled";
6784 needReverse = false;
6785 groupPostfix = "transformed";
6788 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6789 SMESHDS_Mesh* aMesh = GetMeshDS();
6791 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6792 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6793 SMESH_MeshEditor::ElemFeatures elemType;
6795 // map old node to new one
6796 TNodeNodeMap nodeMap;
6798 // elements sharing moved nodes; those of them which have all
6799 // nodes mirrored but are not in theElems are to be reversed
6800 TIDSortedElemSet inverseElemSet;
6802 // source elements for each generated one
6803 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6805 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6806 TIDSortedElemSet orphanNode;
6808 if ( theElems.empty() ) // transform the whole mesh
6811 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6812 while ( eIt->more() ) theElems.insert( eIt->next() );
6814 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6815 while ( nIt->more() )
6817 const SMDS_MeshNode* node = nIt->next();
6818 if ( node->NbInverseElements() == 0)
6819 orphanNode.insert( node );
6823 // loop on elements to transform nodes : first orphan nodes then elems
6824 TIDSortedElemSet::iterator itElem;
6825 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6826 for (int i=0; i<2; i++)
6827 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6829 const SMDS_MeshElement* elem = *itElem;
6833 // loop on elem nodes
6835 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6836 while ( itN->more() )
6838 const SMDS_MeshNode* node = cast2Node( itN->next() );
6839 // check if a node has been already transformed
6840 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6841 nodeMap.insert( make_pair ( node, node ));
6842 if ( !n2n_isnew.second )
6845 node->GetXYZ( coord );
6846 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6847 if ( theTargetMesh ) {
6848 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6849 n2n_isnew.first->second = newNode;
6850 myLastCreatedNodes.Append(newNode);
6851 srcNodes.Append( node );
6853 else if ( theCopy ) {
6854 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6855 n2n_isnew.first->second = newNode;
6856 myLastCreatedNodes.Append(newNode);
6857 srcNodes.Append( node );
6860 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6861 // node position on shape becomes invalid
6862 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6863 ( SMDS_SpacePosition::originSpacePosition() );
6866 // keep inverse elements
6867 if ( !theCopy && !theTargetMesh && needReverse ) {
6868 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6869 while ( invElemIt->more() ) {
6870 const SMDS_MeshElement* iel = invElemIt->next();
6871 inverseElemSet.insert( iel );
6875 } // loop on elems in { &orphanNode, &theElems };
6877 // either create new elements or reverse mirrored ones
6878 if ( !theCopy && !needReverse && !theTargetMesh )
6881 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6883 // Replicate or reverse elements
6885 std::vector<int> iForw;
6886 vector<const SMDS_MeshNode*> nodes;
6887 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6889 const SMDS_MeshElement* elem = *itElem;
6890 if ( !elem ) continue;
6892 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6893 size_t nbNodes = elem->NbNodes();
6894 if ( geomType == SMDSGeom_NONE ) continue; // node
6896 nodes.resize( nbNodes );
6898 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6900 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6904 bool allTransformed = true;
6905 int nbFaces = aPolyedre->NbFaces();
6906 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6908 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6909 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6911 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6912 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6913 if ( nodeMapIt == nodeMap.end() )
6914 allTransformed = false; // not all nodes transformed
6916 nodes.push_back((*nodeMapIt).second);
6918 if ( needReverse && allTransformed )
6919 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6921 if ( !allTransformed )
6922 continue; // not all nodes transformed
6924 else // ----------------------- the rest element types
6926 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6927 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6928 const vector<int>& i = needReverse ? iRev : iForw;
6930 // find transformed nodes
6932 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6933 while ( itN->more() ) {
6934 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6935 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6936 if ( nodeMapIt == nodeMap.end() )
6937 break; // not all nodes transformed
6938 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6940 if ( iNode != nbNodes )
6941 continue; // not all nodes transformed
6945 // copy in this or a new mesh
6946 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6947 srcElems.Append( elem );
6950 // reverse element as it was reversed by transformation
6952 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6955 } // loop on elements
6957 if ( editor && editor != this )
6958 myLastCreatedElems = editor->myLastCreatedElems;
6960 PGroupIDs newGroupIDs;
6962 if ( ( theMakeGroups && theCopy ) ||
6963 ( theMakeGroups && theTargetMesh ) )
6964 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6969 //=======================================================================
6971 * \brief Create groups of elements made during transformation
6972 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6973 * \param elemGens - elements making corresponding myLastCreatedElems
6974 * \param postfix - to append to names of new groups
6975 * \param targetMesh - mesh to create groups in
6976 * \param topPresent - is there "top" elements that are created by sweeping
6978 //=======================================================================
6980 SMESH_MeshEditor::PGroupIDs
6981 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6982 const SMESH_SequenceOfElemPtr& elemGens,
6983 const std::string& postfix,
6984 SMESH_Mesh* targetMesh,
6985 const bool topPresent)
6987 PGroupIDs newGroupIDs( new list<int> );
6988 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6990 // Sort existing groups by types and collect their names
6992 // containers to store an old group and generated new ones;
6993 // 1st new group is for result elems of different type than a source one;
6994 // 2nd new group is for same type result elems ("top" group at extrusion)
6996 using boost::make_tuple;
6997 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6998 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6999 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7001 set< string > groupNames;
7003 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7004 if ( !groupIt->more() ) return newGroupIDs;
7006 int newGroupID = mesh->GetGroupIds().back()+1;
7007 while ( groupIt->more() )
7009 SMESH_Group * group = groupIt->next();
7010 if ( !group ) continue;
7011 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7012 if ( !groupDS || groupDS->IsEmpty() ) continue;
7013 groupNames.insert ( group->GetName() );
7014 groupDS->SetStoreName( group->GetName() );
7015 const SMDSAbs_ElementType type = groupDS->GetType();
7016 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7017 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7018 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7019 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7022 // Loop on nodes and elements to add them in new groups
7024 vector< const SMDS_MeshElement* > resultElems;
7025 for ( int isNodes = 0; isNodes < 2; ++isNodes )
7027 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
7028 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7029 if ( gens.Length() != elems.Length() )
7030 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7032 // loop on created elements
7033 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7035 const SMDS_MeshElement* sourceElem = gens( iElem );
7036 if ( !sourceElem ) {
7037 MESSAGE("generateGroups(): NULL source element");
7040 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7041 if ( groupsOldNew.empty() ) { // no groups of this type at all
7042 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7043 ++iElem; // skip all elements made by sourceElem
7046 // collect all elements made by the iElem-th sourceElem
7047 resultElems.clear();
7048 if ( const SMDS_MeshElement* resElem = elems( iElem ))
7049 if ( resElem != sourceElem )
7050 resultElems.push_back( resElem );
7051 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7052 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7053 if ( resElem != sourceElem )
7054 resultElems.push_back( resElem );
7056 const SMDS_MeshElement* topElem = 0;
7057 if ( isNodes ) // there must be a top element
7059 topElem = resultElems.back();
7060 resultElems.pop_back();
7064 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7065 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7066 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7068 topElem = *resElemIt;
7069 *resElemIt = 0; // erase *resElemIt
7073 // add resultElems to groups originted from ones the sourceElem belongs to
7074 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7075 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7077 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7078 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7080 // fill in a new group
7081 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7082 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7083 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7085 newGroup.Add( *resElemIt );
7087 // fill a "top" group
7090 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7091 newTopGroup.Add( topElem );
7095 } // loop on created elements
7096 }// loop on nodes and elements
7098 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7100 list<int> topGrouIds;
7101 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7103 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
7104 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7105 orderedOldNewGroups[i]->get<2>() };
7106 for ( int is2nd = 0; is2nd < 2; ++is2nd )
7108 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7109 if ( newGroupDS->IsEmpty() )
7111 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7116 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7119 const bool isTop = ( topPresent &&
7120 newGroupDS->GetType() == oldGroupDS->GetType() &&
7123 string name = oldGroupDS->GetStoreName();
7124 { // remove trailing whitespaces (issue 22599)
7125 size_t size = name.size();
7126 while ( size > 1 && isspace( name[ size-1 ]))
7128 if ( size != name.size() )
7130 name.resize( size );
7131 oldGroupDS->SetStoreName( name.c_str() );
7134 if ( !targetMesh ) {
7135 string suffix = ( isTop ? "top": postfix.c_str() );
7139 while ( !groupNames.insert( name ).second ) // name exists
7140 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7145 newGroupDS->SetStoreName( name.c_str() );
7147 // make a SMESH_Groups
7148 mesh->AddGroup( newGroupDS );
7150 topGrouIds.push_back( newGroupDS->GetID() );
7152 newGroupIDs->push_back( newGroupDS->GetID() );
7156 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7161 //================================================================================
7163 * * \brief Return list of group of nodes close to each other within theTolerance
7164 * * Search among theNodes or in the whole mesh if theNodes is empty using
7165 * * an Octree algorithm
7166 * \param [in,out] theNodes - the nodes to treat
7167 * \param [in] theTolerance - the tolerance
7168 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7169 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7170 * corner and medium nodes in separate groups
7172 //================================================================================
7174 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7175 const double theTolerance,
7176 TListOfListOfNodes & theGroupsOfNodes,
7177 bool theSeparateCornersAndMedium)
7179 myLastCreatedElems.Clear();
7180 myLastCreatedNodes.Clear();
7182 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7183 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7184 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7185 theSeparateCornersAndMedium = false;
7187 TIDSortedNodeSet& corners = theNodes;
7188 TIDSortedNodeSet medium;
7190 if ( theNodes.empty() ) // get all nodes in the mesh
7192 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7193 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7194 if ( theSeparateCornersAndMedium )
7195 while ( nIt->more() )
7197 const SMDS_MeshNode* n = nIt->next();
7198 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7199 nodeSet->insert( nodeSet->end(), n );
7202 while ( nIt->more() )
7203 theNodes.insert( theNodes.end(),nIt->next() );
7205 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7207 TIDSortedNodeSet::iterator nIt = corners.begin();
7208 while ( nIt != corners.end() )
7209 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7211 medium.insert( medium.end(), *nIt );
7212 corners.erase( nIt++ );
7220 if ( !corners.empty() )
7221 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7222 if ( !medium.empty() )
7223 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7226 //=======================================================================
7227 //function : SimplifyFace
7228 //purpose : split a chain of nodes into several closed chains
7229 //=======================================================================
7231 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7232 vector<const SMDS_MeshNode *>& poly_nodes,
7233 vector<int>& quantities) const
7235 int nbNodes = faceNodes.size();
7240 set<const SMDS_MeshNode*> nodeSet;
7242 // get simple seq of nodes
7243 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7246 simpleNodes[iSimple++] = faceNodes[0];
7247 for (int iCur = 1; iCur < nbNodes; iCur++) {
7248 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7249 simpleNodes[iSimple++] = faceNodes[iCur];
7250 nodeSet.insert( faceNodes[iCur] );
7253 int nbUnique = nodeSet.size();
7254 int nbSimple = iSimple;
7255 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7265 bool foundLoop = (nbSimple > nbUnique);
7268 set<const SMDS_MeshNode*> loopSet;
7269 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7270 const SMDS_MeshNode* n = simpleNodes[iSimple];
7271 if (!loopSet.insert( n ).second) {
7275 int iC = 0, curLast = iSimple;
7276 for (; iC < curLast; iC++) {
7277 if (simpleNodes[iC] == n) break;
7279 int loopLen = curLast - iC;
7281 // create sub-element
7283 quantities.push_back(loopLen);
7284 for (; iC < curLast; iC++) {
7285 poly_nodes.push_back(simpleNodes[iC]);
7288 // shift the rest nodes (place from the first loop position)
7289 for (iC = curLast + 1; iC < nbSimple; iC++) {
7290 simpleNodes[iC - loopLen] = simpleNodes[iC];
7292 nbSimple -= loopLen;
7295 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7296 } // while (foundLoop)
7300 quantities.push_back(iSimple);
7301 for (int i = 0; i < iSimple; i++)
7302 poly_nodes.push_back(simpleNodes[i]);
7308 //=======================================================================
7309 //function : MergeNodes
7310 //purpose : In each group, the cdr of nodes are substituted by the first one
7312 //=======================================================================
7314 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7316 MESSAGE("MergeNodes");
7317 myLastCreatedElems.Clear();
7318 myLastCreatedNodes.Clear();
7320 SMESHDS_Mesh* aMesh = GetMeshDS();
7322 TNodeNodeMap nodeNodeMap; // node to replace - new node
7323 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7324 list< int > rmElemIds, rmNodeIds;
7326 // Fill nodeNodeMap and elems
7328 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7329 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7331 list<const SMDS_MeshNode*>& nodes = *grIt;
7332 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7333 const SMDS_MeshNode* nToKeep = *nIt;
7334 for ( ++nIt; nIt != nodes.end(); nIt++ )
7336 const SMDS_MeshNode* nToRemove = *nIt;
7337 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7338 if ( nToRemove != nToKeep )
7340 rmNodeIds.push_back( nToRemove->GetID() );
7341 AddToSameGroups( nToKeep, nToRemove, aMesh );
7342 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7343 // after MergeNodes() w/o creating node in place of merged ones.
7344 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7345 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7346 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7347 sm->SetIsAlwaysComputed( true );
7349 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7350 while ( invElemIt->more() ) {
7351 const SMDS_MeshElement* elem = invElemIt->next();
7356 // Change element nodes or remove an element
7358 set<const SMDS_MeshNode*> nodeSet;
7359 vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7361 ElemFeatures elemType;
7363 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7364 for ( ; eIt != elems.end(); eIt++ )
7366 const SMDS_MeshElement* elem = *eIt;
7367 const int nbNodes = elem->NbNodes();
7368 const int aShapeId = FindShape( elem );
7371 curNodes.resize( nbNodes );
7372 uniqueNodes.resize( nbNodes );
7373 iRepl.resize( nbNodes );
7374 int iUnique = 0, iCur = 0, nbRepl = 0;
7376 // get new seq of nodes
7377 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7378 while ( itN->more() )
7380 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7382 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7383 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7385 { ////////// BUG 0020185: begin
7386 bool stopRecur = false;
7387 set<const SMDS_MeshNode*> nodesRecur;
7388 nodesRecur.insert(n);
7389 while (!stopRecur) {
7390 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7391 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7392 n = (*nnIt_i).second;
7393 if (!nodesRecur.insert(n).second) {
7394 // error: recursive dependancy
7401 } ////////// BUG 0020185: end
7403 curNodes[ iCur ] = n;
7404 bool isUnique = nodeSet.insert( n ).second;
7406 uniqueNodes[ iUnique++ ] = n;
7408 iRepl[ nbRepl++ ] = iCur;
7412 // Analyse element topology after replacement
7415 int nbUniqueNodes = nodeSet.size();
7416 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7418 if (elem->IsPoly()) // Polygons and Polyhedral volumes
7420 if (elem->GetType() == SMDSAbs_Face) // Polygon
7422 elemType.Init( elem );
7423 const bool isQuad = elemType.myIsQuad;
7425 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7426 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7428 // a polygon can divide into several elements
7429 vector<const SMDS_MeshNode *> polygons_nodes;
7430 vector<int> quantities;
7431 int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7434 vector<const SMDS_MeshNode *> face_nodes;
7436 for (int iface = 0; iface < nbNew; iface++)
7438 int nbNewNodes = quantities[iface];
7439 face_nodes.assign( polygons_nodes.begin() + inode,
7440 polygons_nodes.begin() + inode + nbNewNodes );
7441 inode += nbNewNodes;
7442 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7444 bool isValid = ( nbNewNodes % 2 == 0 );
7445 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7446 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7447 elemType.SetQuad( isValid );
7448 if ( isValid ) // put medium nodes after corners
7449 SMDS_MeshCell::applyInterlaceRev
7450 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7451 nbNewNodes ), face_nodes );
7453 elemType.SetPoly(( nbNewNodes / ( elemType.myIsQuad + 1 ) > 4 ));
7455 SMDS_MeshElement* newElem = AddElement( face_nodes, elemType );
7457 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7460 rmElemIds.push_back(elem->GetID());
7464 else if (elem->GetType() == SMDSAbs_Volume) // Polyhedral volume
7466 if (nbUniqueNodes < 4) {
7467 rmElemIds.push_back(elem->GetID());
7470 // each face has to be analyzed in order to check volume validity
7471 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
7474 int nbFaces = aPolyedre->NbFaces();
7476 vector<const SMDS_MeshNode *> poly_nodes;
7477 vector<int> quantities;
7479 for (int iface = 1; iface <= nbFaces; iface++) {
7480 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7481 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7483 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7484 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7485 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7486 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7487 faceNode = (*nnIt).second;
7489 faceNodes[inode - 1] = faceNode;
7492 SimplifyFace(faceNodes, poly_nodes, quantities);
7495 if (quantities.size() > 3) {
7496 // to be done: remove coincident faces
7499 if (quantities.size() > 3)
7501 const SMDS_MeshElement* newElem =
7502 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7503 myLastCreatedElems.Append(newElem);
7504 if ( aShapeId && newElem )
7505 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7506 rmElemIds.push_back(elem->GetID());
7510 rmElemIds.push_back(elem->GetID());
7521 // TODO not all the possible cases are solved. Find something more generic?
7522 switch ( nbNodes ) {
7523 case 2: ///////////////////////////////////// EDGE
7524 isOk = false; break;
7525 case 3: ///////////////////////////////////// TRIANGLE
7526 isOk = false; break;
7528 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7530 else { //////////////////////////////////// QUADRANGLE
7531 if ( nbUniqueNodes < 3 )
7533 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7534 isOk = false; // opposite nodes stick
7535 //MESSAGE("isOk " << isOk);
7538 case 6: ///////////////////////////////////// PENTAHEDRON
7539 if ( nbUniqueNodes == 4 ) {
7540 // ---------------------------------> tetrahedron
7542 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7543 // all top nodes stick: reverse a bottom
7544 uniqueNodes[ 0 ] = curNodes [ 1 ];
7545 uniqueNodes[ 1 ] = curNodes [ 0 ];
7547 else if (nbRepl == 3 &&
7548 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7549 // all bottom nodes stick: set a top before
7550 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7551 uniqueNodes[ 0 ] = curNodes [ 3 ];
7552 uniqueNodes[ 1 ] = curNodes [ 4 ];
7553 uniqueNodes[ 2 ] = curNodes [ 5 ];
7555 else if (nbRepl == 4 &&
7556 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7557 // a lateral face turns into a line: reverse a bottom
7558 uniqueNodes[ 0 ] = curNodes [ 1 ];
7559 uniqueNodes[ 1 ] = curNodes [ 0 ];
7564 else if ( nbUniqueNodes == 5 ) {
7565 // PENTAHEDRON --------------------> 2 tetrahedrons
7566 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7567 // a bottom node sticks with a linked top one
7569 SMDS_MeshElement* newElem =
7570 aMesh->AddVolume(curNodes[ 3 ],
7573 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7574 myLastCreatedElems.Append(newElem);
7576 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7577 // 2. : reverse a bottom
7578 uniqueNodes[ 0 ] = curNodes [ 1 ];
7579 uniqueNodes[ 1 ] = curNodes [ 0 ];
7589 if(elem->IsQuadratic()) { // Quadratic quadrangle
7601 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7604 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7606 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7607 uniqueNodes[0] = curNodes[0];
7608 uniqueNodes[1] = curNodes[2];
7609 uniqueNodes[2] = curNodes[3];
7610 uniqueNodes[3] = curNodes[5];
7611 uniqueNodes[4] = curNodes[6];
7612 uniqueNodes[5] = curNodes[7];
7615 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7616 uniqueNodes[0] = curNodes[0];
7617 uniqueNodes[1] = curNodes[1];
7618 uniqueNodes[2] = curNodes[2];
7619 uniqueNodes[3] = curNodes[4];
7620 uniqueNodes[4] = curNodes[5];
7621 uniqueNodes[5] = curNodes[6];
7624 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7625 uniqueNodes[0] = curNodes[1];
7626 uniqueNodes[1] = curNodes[2];
7627 uniqueNodes[2] = curNodes[3];
7628 uniqueNodes[3] = curNodes[5];
7629 uniqueNodes[4] = curNodes[6];
7630 uniqueNodes[5] = curNodes[0];
7633 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7634 uniqueNodes[0] = curNodes[0];
7635 uniqueNodes[1] = curNodes[1];
7636 uniqueNodes[2] = curNodes[3];
7637 uniqueNodes[3] = curNodes[4];
7638 uniqueNodes[4] = curNodes[6];
7639 uniqueNodes[5] = curNodes[7];
7642 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7643 uniqueNodes[0] = curNodes[0];
7644 uniqueNodes[1] = curNodes[2];
7645 uniqueNodes[2] = curNodes[3];
7646 uniqueNodes[3] = curNodes[1];
7647 uniqueNodes[4] = curNodes[6];
7648 uniqueNodes[5] = curNodes[7];
7651 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7652 uniqueNodes[0] = curNodes[0];
7653 uniqueNodes[1] = curNodes[1];
7654 uniqueNodes[2] = curNodes[2];
7655 uniqueNodes[3] = curNodes[4];
7656 uniqueNodes[4] = curNodes[5];
7657 uniqueNodes[5] = curNodes[7];
7660 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7661 uniqueNodes[0] = curNodes[0];
7662 uniqueNodes[1] = curNodes[1];
7663 uniqueNodes[2] = curNodes[3];
7664 uniqueNodes[3] = curNodes[4];
7665 uniqueNodes[4] = curNodes[2];
7666 uniqueNodes[5] = curNodes[7];
7669 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7670 uniqueNodes[0] = curNodes[0];
7671 uniqueNodes[1] = curNodes[1];
7672 uniqueNodes[2] = curNodes[2];
7673 uniqueNodes[3] = curNodes[4];
7674 uniqueNodes[4] = curNodes[5];
7675 uniqueNodes[5] = curNodes[3];
7680 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7683 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7687 //////////////////////////////////// HEXAHEDRON
7689 SMDS_VolumeTool hexa (elem);
7690 hexa.SetExternalNormal();
7691 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7692 //////////////////////// HEX ---> 1 tetrahedron
7693 for ( int iFace = 0; iFace < 6; iFace++ ) {
7694 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7695 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7696 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7697 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7698 // one face turns into a point ...
7699 int iOppFace = hexa.GetOppFaceIndex( iFace );
7700 ind = hexa.GetFaceNodesIndices( iOppFace );
7702 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7703 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7706 if ( nbStick == 1 ) {
7707 // ... and the opposite one - into a triangle.
7709 ind = hexa.GetFaceNodesIndices( iFace );
7710 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7717 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7718 //////////////////////// HEX ---> 1 prism
7719 int nbTria = 0, iTria[3];
7720 const int *ind; // indices of face nodes
7721 // look for triangular faces
7722 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7723 ind = hexa.GetFaceNodesIndices( iFace );
7724 TIDSortedNodeSet faceNodes;
7725 for ( iCur = 0; iCur < 4; iCur++ )
7726 faceNodes.insert( curNodes[ind[iCur]] );
7727 if ( faceNodes.size() == 3 )
7728 iTria[ nbTria++ ] = iFace;
7730 // check if triangles are opposite
7731 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7734 // set nodes of the bottom triangle
7735 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7737 for ( iCur = 0; iCur < 4; iCur++ )
7738 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7739 indB.push_back( ind[iCur] );
7740 if ( !hexa.IsForward() )
7741 std::swap( indB[0], indB[2] );
7742 for ( iCur = 0; iCur < 3; iCur++ )
7743 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7744 // set nodes of the top triangle
7745 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7746 for ( iCur = 0; iCur < 3; ++iCur )
7747 for ( int j = 0; j < 4; ++j )
7748 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7750 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7756 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7757 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7758 for ( int iFace = 0; iFace < 6; iFace++ ) {
7759 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7760 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7761 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7762 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7763 // one face turns into a point ...
7764 int iOppFace = hexa.GetOppFaceIndex( iFace );
7765 ind = hexa.GetFaceNodesIndices( iOppFace );
7767 iUnique = 2; // reverse a tetrahedron 1 bottom
7768 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7769 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7771 else if ( iUnique >= 0 )
7772 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7774 if ( nbStick == 0 ) {
7775 // ... and the opposite one is a quadrangle
7777 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7778 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7781 SMDS_MeshElement* newElem =
7782 aMesh->AddVolume(curNodes[ind[ 0 ]],
7785 curNodes[indTop[ 0 ]]);
7786 myLastCreatedElems.Append(newElem);
7788 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7795 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7796 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7797 // find indices of quad and tri faces
7798 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7799 for ( iFace = 0; iFace < 6; iFace++ ) {
7800 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7802 for ( iCur = 0; iCur < 4; iCur++ )
7803 nodeSet.insert( curNodes[ind[ iCur ]] );
7804 nbUniqueNodes = nodeSet.size();
7805 if ( nbUniqueNodes == 3 )
7806 iTriFace[ nbTri++ ] = iFace;
7807 else if ( nbUniqueNodes == 4 )
7808 iQuadFace[ nbQuad++ ] = iFace;
7810 if (nbQuad == 2 && nbTri == 4 &&
7811 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7812 // 2 opposite quadrangles stuck with a diagonal;
7813 // sample groups of merged indices: (0-4)(2-6)
7814 // --------------------------------------------> 2 tetrahedrons
7815 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7816 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7817 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7818 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7819 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7820 // stuck with 0-2 diagonal
7828 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7829 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7830 // stuck with 1-3 diagonal
7842 uniqueNodes[ 0 ] = curNodes [ i0 ];
7843 uniqueNodes[ 1 ] = curNodes [ i1d ];
7844 uniqueNodes[ 2 ] = curNodes [ i3d ];
7845 uniqueNodes[ 3 ] = curNodes [ i0t ];
7848 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7852 myLastCreatedElems.Append(newElem);
7854 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7857 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7858 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7859 // --------------------------------------------> prism
7860 // find 2 opposite triangles
7862 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7863 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7864 // find indices of kept and replaced nodes
7865 // and fill unique nodes of 2 opposite triangles
7866 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7867 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7868 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7869 // fill unique nodes
7872 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7873 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7874 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7876 // iCur of a linked node of the opposite face (make normals co-directed):
7877 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7878 // check that correspondent corners of triangles are linked
7879 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7882 uniqueNodes[ iUnique ] = n;
7883 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7892 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7895 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7902 } // switch ( nbNodes )
7904 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7906 if ( isOk ) // the non-poly elem remains valid after sticking nodes
7908 if ( nbNodes != nbUniqueNodes ||
7909 !aMesh->ChangeElementNodes( elem, & curNodes[0], nbNodes ))
7911 elemType.Init( elem ).SetID( elem->GetID() );
7913 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7914 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7916 uniqueNodes.resize(nbUniqueNodes);
7917 SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7918 if ( sm && newElem )
7919 sm->AddElement( newElem );
7920 if ( elem != newElem )
7921 ReplaceElemInGroups( elem, newElem, aMesh );
7925 // Remove invalid regular element or invalid polygon
7926 rmElemIds.push_back( elem->GetID() );
7929 } // loop on elements
7931 // Remove bad elements, then equal nodes (order important)
7933 Remove( rmElemIds, false );
7934 Remove( rmNodeIds, true );
7940 // ========================================================
7941 // class : SortableElement
7942 // purpose : allow sorting elements basing on their nodes
7943 // ========================================================
7944 class SortableElement : public set <const SMDS_MeshElement*>
7948 SortableElement( const SMDS_MeshElement* theElem )
7951 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7952 while ( nodeIt->more() )
7953 this->insert( nodeIt->next() );
7956 const SMDS_MeshElement* Get() const
7960 mutable const SMDS_MeshElement* myElem;
7963 //=======================================================================
7964 //function : FindEqualElements
7965 //purpose : Return list of group of elements built on the same nodes.
7966 // Search among theElements or in the whole mesh if theElements is empty
7967 //=======================================================================
7969 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7970 TListOfListOfElementsID & theGroupsOfElementsID)
7972 myLastCreatedElems.Clear();
7973 myLastCreatedNodes.Clear();
7975 typedef map< SortableElement, int > TMapOfNodeSet;
7976 typedef list<int> TGroupOfElems;
7978 if ( theElements.empty() )
7979 { // get all elements in the mesh
7980 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7981 while ( eIt->more() )
7982 theElements.insert( theElements.end(), eIt->next() );
7985 vector< TGroupOfElems > arrayOfGroups;
7986 TGroupOfElems groupOfElems;
7987 TMapOfNodeSet mapOfNodeSet;
7989 TIDSortedElemSet::iterator elemIt = theElements.begin();
7990 for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7992 const SMDS_MeshElement* curElem = *elemIt;
7993 SortableElement SE(curElem);
7995 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7996 if ( !pp.second ) { // one more coincident elem
7997 TMapOfNodeSet::iterator& itSE = pp.first;
7998 int ind = (*itSE).second;
7999 arrayOfGroups[ind].push_back( curElem->GetID() );
8002 arrayOfGroups.push_back( groupOfElems );
8003 arrayOfGroups.back().push_back( curElem->GetID() );
8008 groupOfElems.clear();
8009 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8010 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8012 if ( groupIt->size() > 1 ) {
8013 //groupOfElems.sort(); -- theElements is sorted already
8014 theGroupsOfElementsID.push_back( groupOfElems );
8015 theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8020 //=======================================================================
8021 //function : MergeElements
8022 //purpose : In each given group, substitute all elements by the first one.
8023 //=======================================================================
8025 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8027 myLastCreatedElems.Clear();
8028 myLastCreatedNodes.Clear();
8030 typedef list<int> TListOfIDs;
8031 TListOfIDs rmElemIds; // IDs of elems to remove
8033 SMESHDS_Mesh* aMesh = GetMeshDS();
8035 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8036 while ( groupsIt != theGroupsOfElementsID.end() ) {
8037 TListOfIDs& aGroupOfElemID = *groupsIt;
8038 aGroupOfElemID.sort();
8039 int elemIDToKeep = aGroupOfElemID.front();
8040 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8041 aGroupOfElemID.pop_front();
8042 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8043 while ( idIt != aGroupOfElemID.end() ) {
8044 int elemIDToRemove = *idIt;
8045 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8046 // add the kept element in groups of removed one (PAL15188)
8047 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8048 rmElemIds.push_back( elemIDToRemove );
8054 Remove( rmElemIds, false );
8057 //=======================================================================
8058 //function : MergeEqualElements
8059 //purpose : Remove all but one of elements built on the same nodes.
8060 //=======================================================================
8062 void SMESH_MeshEditor::MergeEqualElements()
8064 TIDSortedElemSet aMeshElements; /* empty input ==
8065 to merge equal elements in the whole mesh */
8066 TListOfListOfElementsID aGroupsOfElementsID;
8067 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8068 MergeElements(aGroupsOfElementsID);
8071 //=======================================================================
8072 //function : findAdjacentFace
8074 //=======================================================================
8076 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8077 const SMDS_MeshNode* n2,
8078 const SMDS_MeshElement* elem)
8080 TIDSortedElemSet elemSet, avoidSet;
8082 avoidSet.insert ( elem );
8083 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8086 //=======================================================================
8087 //function : findSegment
8088 //purpose : Return a mesh segment by two nodes one of which can be medium
8089 //=======================================================================
8091 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8092 const SMDS_MeshNode* n2)
8094 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8095 while ( it->more() )
8097 const SMDS_MeshElement* seg = it->next();
8098 if ( seg->GetNodeIndex( n2 ) >= 0 )
8104 //=======================================================================
8105 //function : FindFreeBorder
8107 //=======================================================================
8109 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8111 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8112 const SMDS_MeshNode* theSecondNode,
8113 const SMDS_MeshNode* theLastNode,
8114 list< const SMDS_MeshNode* > & theNodes,
8115 list< const SMDS_MeshElement* >& theFaces)
8117 if ( !theFirstNode || !theSecondNode )
8119 // find border face between theFirstNode and theSecondNode
8120 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8124 theFaces.push_back( curElem );
8125 theNodes.push_back( theFirstNode );
8126 theNodes.push_back( theSecondNode );
8128 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8129 TIDSortedElemSet foundElems;
8130 bool needTheLast = ( theLastNode != 0 );
8132 while ( nStart != theLastNode ) {
8133 if ( nStart == theFirstNode )
8134 return !needTheLast;
8136 // find all free border faces sharing form nStart
8138 list< const SMDS_MeshElement* > curElemList;
8139 list< const SMDS_MeshNode* > nStartList;
8140 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8141 while ( invElemIt->more() ) {
8142 const SMDS_MeshElement* e = invElemIt->next();
8143 if ( e == curElem || foundElems.insert( e ).second ) {
8145 int iNode = 0, nbNodes = e->NbNodes();
8146 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8148 if ( e->IsQuadratic() ) {
8149 const SMDS_VtkFace* F =
8150 dynamic_cast<const SMDS_VtkFace*>(e);
8151 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8152 // use special nodes iterator
8153 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8154 while( anIter->more() ) {
8155 nodes[ iNode++ ] = cast2Node(anIter->next());
8159 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8160 while ( nIt->more() )
8161 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8163 nodes[ iNode ] = nodes[ 0 ];
8165 for ( iNode = 0; iNode < nbNodes; iNode++ )
8166 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8167 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8168 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8170 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8171 curElemList.push_back( e );
8175 // analyse the found
8177 int nbNewBorders = curElemList.size();
8178 if ( nbNewBorders == 0 ) {
8179 // no free border furthermore
8180 return !needTheLast;
8182 else if ( nbNewBorders == 1 ) {
8183 // one more element found
8185 nStart = nStartList.front();
8186 curElem = curElemList.front();
8187 theFaces.push_back( curElem );
8188 theNodes.push_back( nStart );
8191 // several continuations found
8192 list< const SMDS_MeshElement* >::iterator curElemIt;
8193 list< const SMDS_MeshNode* >::iterator nStartIt;
8194 // check if one of them reached the last node
8195 if ( needTheLast ) {
8196 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8197 curElemIt!= curElemList.end();
8198 curElemIt++, nStartIt++ )
8199 if ( *nStartIt == theLastNode ) {
8200 theFaces.push_back( *curElemIt );
8201 theNodes.push_back( *nStartIt );
8205 // find the best free border by the continuations
8206 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8207 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8208 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8209 curElemIt!= curElemList.end();
8210 curElemIt++, nStartIt++ )
8212 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8213 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8214 // find one more free border
8215 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8219 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8220 // choice: clear a worse one
8221 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8222 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8223 contNodes[ iWorse ].clear();
8224 contFaces[ iWorse ].clear();
8227 if ( contNodes[0].empty() && contNodes[1].empty() )
8230 // append the best free border
8231 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8232 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8233 theNodes.pop_back(); // remove nIgnore
8234 theNodes.pop_back(); // remove nStart
8235 theFaces.pop_back(); // remove curElem
8236 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8237 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8238 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8239 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8242 } // several continuations found
8243 } // while ( nStart != theLastNode )
8248 //=======================================================================
8249 //function : CheckFreeBorderNodes
8250 //purpose : Return true if the tree nodes are on a free border
8251 //=======================================================================
8253 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8254 const SMDS_MeshNode* theNode2,
8255 const SMDS_MeshNode* theNode3)
8257 list< const SMDS_MeshNode* > nodes;
8258 list< const SMDS_MeshElement* > faces;
8259 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8262 //=======================================================================
8263 //function : SewFreeBorder
8265 //warning : for border-to-side sewing theSideSecondNode is considered as
8266 // the last side node and theSideThirdNode is not used
8267 //=======================================================================
8269 SMESH_MeshEditor::Sew_Error
8270 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8271 const SMDS_MeshNode* theBordSecondNode,
8272 const SMDS_MeshNode* theBordLastNode,
8273 const SMDS_MeshNode* theSideFirstNode,
8274 const SMDS_MeshNode* theSideSecondNode,
8275 const SMDS_MeshNode* theSideThirdNode,
8276 const bool theSideIsFreeBorder,
8277 const bool toCreatePolygons,
8278 const bool toCreatePolyedrs)
8280 myLastCreatedElems.Clear();
8281 myLastCreatedNodes.Clear();
8283 MESSAGE("::SewFreeBorder()");
8284 Sew_Error aResult = SEW_OK;
8286 // ====================================
8287 // find side nodes and elements
8288 // ====================================
8290 list< const SMDS_MeshNode* > nSide[ 2 ];
8291 list< const SMDS_MeshElement* > eSide[ 2 ];
8292 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8293 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8297 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8298 nSide[0], eSide[0])) {
8299 MESSAGE(" Free Border 1 not found " );
8300 aResult = SEW_BORDER1_NOT_FOUND;
8302 if (theSideIsFreeBorder) {
8305 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8306 nSide[1], eSide[1])) {
8307 MESSAGE(" Free Border 2 not found " );
8308 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8311 if ( aResult != SEW_OK )
8314 if (!theSideIsFreeBorder) {
8318 // -------------------------------------------------------------------------
8320 // 1. If nodes to merge are not coincident, move nodes of the free border
8321 // from the coord sys defined by the direction from the first to last
8322 // nodes of the border to the correspondent sys of the side 2
8323 // 2. On the side 2, find the links most co-directed with the correspondent
8324 // links of the free border
8325 // -------------------------------------------------------------------------
8327 // 1. Since sewing may break if there are volumes to split on the side 2,
8328 // we wont move nodes but just compute new coordinates for them
8329 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8330 TNodeXYZMap nBordXYZ;
8331 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8332 list< const SMDS_MeshNode* >::iterator nBordIt;
8334 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8335 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8336 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8337 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8338 double tol2 = 1.e-8;
8339 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8340 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8341 // Need node movement.
8343 // find X and Z axes to create trsf
8344 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8346 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8348 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8351 gp_Ax3 toBordAx( Pb1, Zb, X );
8352 gp_Ax3 fromSideAx( Ps1, Zs, X );
8353 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8355 gp_Trsf toBordSys, fromSide2Sys;
8356 toBordSys.SetTransformation( toBordAx );
8357 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8358 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8361 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8362 const SMDS_MeshNode* n = *nBordIt;
8363 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8364 toBordSys.Transforms( xyz );
8365 fromSide2Sys.Transforms( xyz );
8366 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8370 // just insert nodes XYZ in the nBordXYZ map
8371 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8372 const SMDS_MeshNode* n = *nBordIt;
8373 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8377 // 2. On the side 2, find the links most co-directed with the correspondent
8378 // links of the free border
8380 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8381 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8382 sideNodes.push_back( theSideFirstNode );
8384 bool hasVolumes = false;
8385 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8386 set<long> foundSideLinkIDs, checkedLinkIDs;
8387 SMDS_VolumeTool volume;
8388 //const SMDS_MeshNode* faceNodes[ 4 ];
8390 const SMDS_MeshNode* sideNode;
8391 const SMDS_MeshElement* sideElem;
8392 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8393 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8394 nBordIt = bordNodes.begin();
8396 // border node position and border link direction to compare with
8397 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8398 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8399 // choose next side node by link direction or by closeness to
8400 // the current border node:
8401 bool searchByDir = ( *nBordIt != theBordLastNode );
8403 // find the next node on the Side 2
8405 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8407 checkedLinkIDs.clear();
8408 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8410 // loop on inverse elements of current node (prevSideNode) on the Side 2
8411 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8412 while ( invElemIt->more() )
8414 const SMDS_MeshElement* elem = invElemIt->next();
8415 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8416 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8417 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8418 bool isVolume = volume.Set( elem );
8419 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8420 if ( isVolume ) // --volume
8422 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8423 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8424 if(elem->IsQuadratic()) {
8425 const SMDS_VtkFace* F =
8426 dynamic_cast<const SMDS_VtkFace*>(elem);
8427 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8428 // use special nodes iterator
8429 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8430 while( anIter->more() ) {
8431 nodes[ iNode ] = cast2Node(anIter->next());
8432 if ( nodes[ iNode++ ] == prevSideNode )
8433 iPrevNode = iNode - 1;
8437 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8438 while ( nIt->more() ) {
8439 nodes[ iNode ] = cast2Node( nIt->next() );
8440 if ( nodes[ iNode++ ] == prevSideNode )
8441 iPrevNode = iNode - 1;
8444 // there are 2 links to check
8449 // loop on links, to be precise, on the second node of links
8450 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8451 const SMDS_MeshNode* n = nodes[ iNode ];
8453 if ( !volume.IsLinked( n, prevSideNode ))
8457 if ( iNode ) // a node before prevSideNode
8458 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8459 else // a node after prevSideNode
8460 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8462 // check if this link was already used
8463 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8464 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8465 if (!isJustChecked &&
8466 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8468 // test a link geometrically
8469 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8470 bool linkIsBetter = false;
8471 double dot = 0.0, dist = 0.0;
8472 if ( searchByDir ) { // choose most co-directed link
8473 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8474 linkIsBetter = ( dot > maxDot );
8476 else { // choose link with the node closest to bordPos
8477 dist = ( nextXYZ - bordPos ).SquareModulus();
8478 linkIsBetter = ( dist < minDist );
8480 if ( linkIsBetter ) {
8489 } // loop on inverse elements of prevSideNode
8492 MESSAGE(" Cant find path by links of the Side 2 ");
8493 return SEW_BAD_SIDE_NODES;
8495 sideNodes.push_back( sideNode );
8496 sideElems.push_back( sideElem );
8497 foundSideLinkIDs.insert ( linkID );
8498 prevSideNode = sideNode;
8500 if ( *nBordIt == theBordLastNode )
8501 searchByDir = false;
8503 // find the next border link to compare with
8504 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8505 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8506 // move to next border node if sideNode is before forward border node (bordPos)
8507 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8508 prevBordNode = *nBordIt;
8510 bordPos = nBordXYZ[ *nBordIt ];
8511 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8512 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8516 while ( sideNode != theSideSecondNode );
8518 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8519 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8520 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8522 } // end nodes search on the side 2
8524 // ============================
8525 // sew the border to the side 2
8526 // ============================
8528 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8529 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8531 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8532 if ( toMergeConformal && toCreatePolygons )
8534 // do not merge quadrangles if polygons are OK (IPAL0052824)
8535 eIt[0] = eSide[0].begin();
8536 eIt[1] = eSide[1].begin();
8537 bool allQuads[2] = { true, true };
8538 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8539 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8540 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8542 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8545 TListOfListOfNodes nodeGroupsToMerge;
8546 if (( toMergeConformal ) ||
8547 ( theSideIsFreeBorder && !theSideThirdNode )) {
8549 // all nodes are to be merged
8551 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8552 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8553 nIt[0]++, nIt[1]++ )
8555 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8556 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8557 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8562 // insert new nodes into the border and the side to get equal nb of segments
8564 // get normalized parameters of nodes on the borders
8565 vector< double > param[ 2 ];
8566 param[0].resize( maxNbNodes );
8567 param[1].resize( maxNbNodes );
8569 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8570 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8571 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8572 const SMDS_MeshNode* nPrev = *nIt;
8573 double bordLength = 0;
8574 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8575 const SMDS_MeshNode* nCur = *nIt;
8576 gp_XYZ segment (nCur->X() - nPrev->X(),
8577 nCur->Y() - nPrev->Y(),
8578 nCur->Z() - nPrev->Z());
8579 double segmentLen = segment.Modulus();
8580 bordLength += segmentLen;
8581 param[ iBord ][ iNode ] = bordLength;
8584 // normalize within [0,1]
8585 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8586 param[ iBord ][ iNode ] /= bordLength;
8590 // loop on border segments
8591 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8592 int i[ 2 ] = { 0, 0 };
8593 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8594 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8596 TElemOfNodeListMap insertMap;
8597 TElemOfNodeListMap::iterator insertMapIt;
8599 // key: elem to insert nodes into
8600 // value: 2 nodes to insert between + nodes to be inserted
8602 bool next[ 2 ] = { false, false };
8604 // find min adjacent segment length after sewing
8605 double nextParam = 10., prevParam = 0;
8606 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8607 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8608 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8609 if ( i[ iBord ] > 0 )
8610 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8612 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8613 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8614 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8616 // choose to insert or to merge nodes
8617 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8618 if ( Abs( du ) <= minSegLen * 0.2 ) {
8621 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8622 const SMDS_MeshNode* n0 = *nIt[0];
8623 const SMDS_MeshNode* n1 = *nIt[1];
8624 nodeGroupsToMerge.back().push_back( n1 );
8625 nodeGroupsToMerge.back().push_back( n0 );
8626 // position of node of the border changes due to merge
8627 param[ 0 ][ i[0] ] += du;
8628 // move n1 for the sake of elem shape evaluation during insertion.
8629 // n1 will be removed by MergeNodes() anyway
8630 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8631 next[0] = next[1] = true;
8636 int intoBord = ( du < 0 ) ? 0 : 1;
8637 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8638 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8639 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8640 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8641 if ( intoBord == 1 ) {
8642 // move node of the border to be on a link of elem of the side
8643 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8644 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8645 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8646 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8647 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8649 insertMapIt = insertMap.find( elem );
8650 bool notFound = ( insertMapIt == insertMap.end() );
8651 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8653 // insert into another link of the same element:
8654 // 1. perform insertion into the other link of the elem
8655 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8656 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8657 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8658 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8659 // 2. perform insertion into the link of adjacent faces
8660 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8661 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8663 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8664 InsertNodesIntoLink( seg, n12, n22, nodeList );
8666 if (toCreatePolyedrs) {
8667 // perform insertion into the links of adjacent volumes
8668 UpdateVolumes(n12, n22, nodeList);
8670 // 3. find an element appeared on n1 and n2 after the insertion
8671 insertMap.erase( elem );
8672 elem = findAdjacentFace( n1, n2, 0 );
8674 if ( notFound || otherLink ) {
8675 // add element and nodes of the side into the insertMap
8676 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8677 (*insertMapIt).second.push_back( n1 );
8678 (*insertMapIt).second.push_back( n2 );
8680 // add node to be inserted into elem
8681 (*insertMapIt).second.push_back( nIns );
8682 next[ 1 - intoBord ] = true;
8685 // go to the next segment
8686 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8687 if ( next[ iBord ] ) {
8688 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8690 nPrev[ iBord ] = *nIt[ iBord ];
8691 nIt[ iBord ]++; i[ iBord ]++;
8695 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8697 // perform insertion of nodes into elements
8699 for (insertMapIt = insertMap.begin();
8700 insertMapIt != insertMap.end();
8703 const SMDS_MeshElement* elem = (*insertMapIt).first;
8704 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8705 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8706 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8708 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8710 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8711 InsertNodesIntoLink( seg, n1, n2, nodeList );
8714 if ( !theSideIsFreeBorder ) {
8715 // look for and insert nodes into the faces adjacent to elem
8716 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8717 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8720 if (toCreatePolyedrs) {
8721 // perform insertion into the links of adjacent volumes
8722 UpdateVolumes(n1, n2, nodeList);
8725 } // end: insert new nodes
8727 MergeNodes ( nodeGroupsToMerge );
8730 // Remove coincident segments
8733 TIDSortedElemSet segments;
8734 SMESH_SequenceOfElemPtr newFaces;
8735 for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8737 if ( !myLastCreatedElems(i) ) continue;
8738 if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8739 segments.insert( segments.end(), myLastCreatedElems(i) );
8741 newFaces.Append( myLastCreatedElems(i) );
8743 // get segments adjacent to merged nodes
8744 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8745 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8747 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8748 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8749 while ( segIt->more() )
8750 segments.insert( segIt->next() );
8754 TListOfListOfElementsID equalGroups;
8755 if ( !segments.empty() )
8756 FindEqualElements( segments, equalGroups );
8757 if ( !equalGroups.empty() )
8759 // remove from segments those that will be removed
8760 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8761 for ( ; itGroups != equalGroups.end(); ++itGroups )
8763 list< int >& group = *itGroups;
8764 list< int >::iterator id = group.begin();
8765 for ( ++id; id != group.end(); ++id )
8766 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8767 segments.erase( seg );
8769 // remove equal segments
8770 MergeElements( equalGroups );
8772 // restore myLastCreatedElems
8773 myLastCreatedElems = newFaces;
8774 TIDSortedElemSet::iterator seg = segments.begin();
8775 for ( ; seg != segments.end(); ++seg )
8776 myLastCreatedElems.Append( *seg );
8782 //=======================================================================
8783 //function : InsertNodesIntoLink
8784 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8785 // and theBetweenNode2 and split theElement
8786 //=======================================================================
8788 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8789 const SMDS_MeshNode* theBetweenNode1,
8790 const SMDS_MeshNode* theBetweenNode2,
8791 list<const SMDS_MeshNode*>& theNodesToInsert,
8792 const bool toCreatePoly)
8794 if ( !theElement ) return;
8796 SMESHDS_Mesh *aMesh = GetMeshDS();
8797 vector<const SMDS_MeshElement*> newElems;
8799 if ( theElement->GetType() == SMDSAbs_Edge )
8801 theNodesToInsert.push_front( theBetweenNode1 );
8802 theNodesToInsert.push_back ( theBetweenNode2 );
8803 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8804 const SMDS_MeshNode* n1 = *n;
8805 for ( ++n; n != theNodesToInsert.end(); ++n )
8807 const SMDS_MeshNode* n2 = *n;
8808 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8809 AddToSameGroups( seg, theElement, aMesh );
8811 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8814 theNodesToInsert.pop_front();
8815 theNodesToInsert.pop_back();
8817 if ( theElement->IsQuadratic() ) // add a not split part
8819 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8820 theElement->end_nodes() );
8821 int iOther = 0, nbN = nodes.size();
8822 for ( ; iOther < nbN; ++iOther )
8823 if ( nodes[iOther] != theBetweenNode1 &&
8824 nodes[iOther] != theBetweenNode2 )
8828 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8829 AddToSameGroups( seg, theElement, aMesh );
8831 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8833 else if ( iOther == 2 )
8835 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8836 AddToSameGroups( seg, theElement, aMesh );
8838 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8841 // treat new elements
8842 for ( size_t i = 0; i < newElems.size(); ++i )
8845 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8846 myLastCreatedElems.Append( newElems[i] );
8848 ReplaceElemInGroups( theElement, newElems, aMesh );
8849 aMesh->RemoveElement( theElement );
8852 } // if ( theElement->GetType() == SMDSAbs_Edge )
8854 const SMDS_MeshElement* theFace = theElement;
8855 if ( theFace->GetType() != SMDSAbs_Face ) return;
8857 // find indices of 2 link nodes and of the rest nodes
8858 int iNode = 0, il1, il2, i3, i4;
8859 il1 = il2 = i3 = i4 = -1;
8860 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8862 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8863 while ( nodeIt->more() ) {
8864 const SMDS_MeshNode* n = nodeIt->next();
8865 if ( n == theBetweenNode1 )
8867 else if ( n == theBetweenNode2 )
8873 nodes[ iNode++ ] = n;
8875 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8878 // arrange link nodes to go one after another regarding the face orientation
8879 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8880 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8885 aNodesToInsert.reverse();
8887 // check that not link nodes of a quadrangles are in good order
8888 int nbFaceNodes = theFace->NbNodes();
8889 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8895 if (toCreatePoly || theFace->IsPoly()) {
8898 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8900 // add nodes of face up to first node of link
8903 if ( theFace->IsQuadratic() ) {
8904 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8905 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8906 // use special nodes iterator
8907 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8908 while( anIter->more() && !isFLN ) {
8909 const SMDS_MeshNode* n = cast2Node(anIter->next());
8910 poly_nodes[iNode++] = n;
8911 if (n == nodes[il1]) {
8915 // add nodes to insert
8916 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8917 for (; nIt != aNodesToInsert.end(); nIt++) {
8918 poly_nodes[iNode++] = *nIt;
8920 // add nodes of face starting from last node of link
8921 while ( anIter->more() ) {
8922 poly_nodes[iNode++] = cast2Node(anIter->next());
8926 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8927 while ( nodeIt->more() && !isFLN ) {
8928 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8929 poly_nodes[iNode++] = n;
8930 if (n == nodes[il1]) {
8934 // add nodes to insert
8935 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8936 for (; nIt != aNodesToInsert.end(); nIt++) {
8937 poly_nodes[iNode++] = *nIt;
8939 // add nodes of face starting from last node of link
8940 while ( nodeIt->more() ) {
8941 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8942 poly_nodes[iNode++] = n;
8947 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8950 else if ( !theFace->IsQuadratic() )
8952 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8953 int nbLinkNodes = 2 + aNodesToInsert.size();
8954 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8955 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8956 linkNodes[ 0 ] = nodes[ il1 ];
8957 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8958 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8959 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8960 linkNodes[ iNode++ ] = *nIt;
8962 // decide how to split a quadrangle: compare possible variants
8963 // and choose which of splits to be a quadrangle
8964 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8965 if ( nbFaceNodes == 3 ) {
8966 iBestQuad = nbSplits;
8969 else if ( nbFaceNodes == 4 ) {
8970 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8971 double aBestRate = DBL_MAX;
8972 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8974 double aBadRate = 0;
8975 // evaluate elements quality
8976 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8977 if ( iSplit == iQuad ) {
8978 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8982 aBadRate += getBadRate( &quad, aCrit );
8985 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8987 nodes[ iSplit < iQuad ? i4 : i3 ]);
8988 aBadRate += getBadRate( &tria, aCrit );
8992 if ( aBadRate < aBestRate ) {
8994 aBestRate = aBadRate;
8999 // create new elements
9001 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
9003 if ( iSplit == iBestQuad )
9004 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9009 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9011 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
9014 const SMDS_MeshNode* newNodes[ 4 ];
9015 newNodes[ 0 ] = linkNodes[ i1 ];
9016 newNodes[ 1 ] = linkNodes[ i2 ];
9017 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9018 newNodes[ 3 ] = nodes[ i4 ];
9019 if (iSplit == iBestQuad)
9020 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
9022 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
9024 } // end if(!theFace->IsQuadratic())
9026 else { // theFace is quadratic
9027 // we have to split theFace on simple triangles and one simple quadrangle
9029 int nbshift = tmp*2;
9030 // shift nodes in nodes[] by nbshift
9032 for(i=0; i<nbshift; i++) {
9033 const SMDS_MeshNode* n = nodes[0];
9034 for(j=0; j<nbFaceNodes-1; j++) {
9035 nodes[j] = nodes[j+1];
9037 nodes[nbFaceNodes-1] = n;
9039 il1 = il1 - nbshift;
9040 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9041 // n0 n1 n2 n0 n1 n2
9042 // +-----+-----+ +-----+-----+
9051 // create new elements
9053 if ( nbFaceNodes == 6 ) { // quadratic triangle
9054 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9055 if ( theFace->IsMediumNode(nodes[il1]) ) {
9056 // create quadrangle
9057 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9063 // create quadrangle
9064 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9070 else { // nbFaceNodes==8 - quadratic quadrangle
9071 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9072 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9073 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9074 if ( theFace->IsMediumNode( nodes[ il1 ])) {
9075 // create quadrangle
9076 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9082 // create quadrangle
9083 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9089 // create needed triangles using n1,n2,n3 and inserted nodes
9090 int nbn = 2 + aNodesToInsert.size();
9091 vector<const SMDS_MeshNode*> aNodes(nbn);
9092 aNodes[0 ] = nodes[n1];
9093 aNodes[nbn-1] = nodes[n2];
9094 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9095 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9096 aNodes[iNode++] = *nIt;
9098 for ( i = 1; i < nbn; i++ )
9099 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9102 // remove the old face
9103 for ( size_t i = 0; i < newElems.size(); ++i )
9106 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9107 myLastCreatedElems.Append( newElems[i] );
9109 ReplaceElemInGroups( theFace, newElems, aMesh );
9110 aMesh->RemoveElement(theFace);
9112 } // InsertNodesIntoLink()
9114 //=======================================================================
9115 //function : UpdateVolumes
9117 //=======================================================================
9119 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9120 const SMDS_MeshNode* theBetweenNode2,
9121 list<const SMDS_MeshNode*>& theNodesToInsert)
9123 myLastCreatedElems.Clear();
9124 myLastCreatedNodes.Clear();
9126 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9127 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9128 const SMDS_MeshElement* elem = invElemIt->next();
9130 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9131 SMDS_VolumeTool aVolume (elem);
9132 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9135 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9136 int iface, nbFaces = aVolume.NbFaces();
9137 vector<const SMDS_MeshNode *> poly_nodes;
9138 vector<int> quantities (nbFaces);
9140 for (iface = 0; iface < nbFaces; iface++) {
9141 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9142 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9143 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9145 for (int inode = 0; inode < nbFaceNodes; inode++) {
9146 poly_nodes.push_back(faceNodes[inode]);
9148 if (nbInserted == 0) {
9149 if (faceNodes[inode] == theBetweenNode1) {
9150 if (faceNodes[inode + 1] == theBetweenNode2) {
9151 nbInserted = theNodesToInsert.size();
9153 // add nodes to insert
9154 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9155 for (; nIt != theNodesToInsert.end(); nIt++) {
9156 poly_nodes.push_back(*nIt);
9160 else if (faceNodes[inode] == theBetweenNode2) {
9161 if (faceNodes[inode + 1] == theBetweenNode1) {
9162 nbInserted = theNodesToInsert.size();
9164 // add nodes to insert in reversed order
9165 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9167 for (; nIt != theNodesToInsert.begin(); nIt--) {
9168 poly_nodes.push_back(*nIt);
9170 poly_nodes.push_back(*nIt);
9177 quantities[iface] = nbFaceNodes + nbInserted;
9180 // Replace the volume
9181 SMESHDS_Mesh *aMesh = GetMeshDS();
9183 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9185 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9186 myLastCreatedElems.Append( newElem );
9187 ReplaceElemInGroups( elem, newElem, aMesh );
9189 aMesh->RemoveElement( elem );
9195 //================================================================================
9197 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9199 //================================================================================
9201 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9202 vector<const SMDS_MeshNode *> & nodes,
9203 vector<int> & nbNodeInFaces )
9206 nbNodeInFaces.clear();
9207 SMDS_VolumeTool vTool ( elem );
9208 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9210 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9211 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9212 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9217 //=======================================================================
9219 * \brief Convert elements contained in a sub-mesh to quadratic
9220 * \return int - nb of checked elements
9222 //=======================================================================
9224 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9225 SMESH_MesherHelper& theHelper,
9226 const bool theForce3d)
9229 if( !theSm ) return nbElem;
9231 vector<int> nbNodeInFaces;
9232 vector<const SMDS_MeshNode *> nodes;
9233 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9234 while(ElemItr->more())
9237 const SMDS_MeshElement* elem = ElemItr->next();
9238 if( !elem ) continue;
9240 // analyse a necessity of conversion
9241 const SMDSAbs_ElementType aType = elem->GetType();
9242 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9244 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9245 bool hasCentralNodes = false;
9246 if ( elem->IsQuadratic() )
9249 switch ( aGeomType ) {
9250 case SMDSEntity_Quad_Triangle:
9251 case SMDSEntity_Quad_Quadrangle:
9252 case SMDSEntity_Quad_Hexa:
9253 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9255 case SMDSEntity_BiQuad_Triangle:
9256 case SMDSEntity_BiQuad_Quadrangle:
9257 case SMDSEntity_TriQuad_Hexa:
9258 alreadyOK = theHelper.GetIsBiQuadratic();
9259 hasCentralNodes = true;
9264 // take into account already present modium nodes
9266 case SMDSAbs_Volume:
9267 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9269 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9271 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9277 // get elem data needed to re-create it
9279 const int id = elem->GetID();
9280 const int nbNodes = elem->NbCornerNodes();
9281 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9282 if ( aGeomType == SMDSEntity_Polyhedra )
9283 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9284 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9285 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9287 // remove a linear element
9288 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9290 // remove central nodes of biquadratic elements (biquad->quad convertion)
9291 if ( hasCentralNodes )
9292 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9293 if ( nodes[i]->NbInverseElements() == 0 )
9294 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9296 const SMDS_MeshElement* NewElem = 0;
9302 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9310 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9313 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9316 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9320 case SMDSAbs_Volume :
9324 case SMDSEntity_Tetra:
9325 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9327 case SMDSEntity_Pyramid:
9328 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9330 case SMDSEntity_Penta:
9331 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9333 case SMDSEntity_Hexa:
9334 case SMDSEntity_Quad_Hexa:
9335 case SMDSEntity_TriQuad_Hexa:
9336 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9337 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9339 case SMDSEntity_Hexagonal_Prism:
9341 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9348 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9349 if( NewElem && NewElem->getshapeId() < 1 )
9350 theSm->AddElement( NewElem );
9354 //=======================================================================
9355 //function : ConvertToQuadratic
9357 //=======================================================================
9359 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9361 SMESHDS_Mesh* meshDS = GetMeshDS();
9363 SMESH_MesherHelper aHelper(*myMesh);
9365 aHelper.SetIsQuadratic( true );
9366 aHelper.SetIsBiQuadratic( theToBiQuad );
9367 aHelper.SetElementsOnShape(true);
9368 aHelper.ToFixNodeParameters( true );
9370 // convert elements assigned to sub-meshes
9371 int nbCheckedElems = 0;
9372 if ( myMesh->HasShapeToMesh() )
9374 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9376 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9377 while ( smIt->more() ) {
9378 SMESH_subMesh* sm = smIt->next();
9379 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9380 aHelper.SetSubShape( sm->GetSubShape() );
9381 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9387 // convert elements NOT assigned to sub-meshes
9388 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9389 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9391 aHelper.SetElementsOnShape(false);
9392 SMESHDS_SubMesh *smDS = 0;
9395 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9396 while( aEdgeItr->more() )
9398 const SMDS_MeshEdge* edge = aEdgeItr->next();
9399 if ( !edge->IsQuadratic() )
9401 int id = edge->GetID();
9402 const SMDS_MeshNode* n1 = edge->GetNode(0);
9403 const SMDS_MeshNode* n2 = edge->GetNode(1);
9405 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9407 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9408 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9412 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9417 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9418 while( aFaceItr->more() )
9420 const SMDS_MeshFace* face = aFaceItr->next();
9421 if ( !face ) continue;
9423 const SMDSAbs_EntityType type = face->GetEntityType();
9427 case SMDSEntity_Quad_Triangle:
9428 case SMDSEntity_Quad_Quadrangle:
9429 alreadyOK = !theToBiQuad;
9430 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9432 case SMDSEntity_BiQuad_Triangle:
9433 case SMDSEntity_BiQuad_Quadrangle:
9434 alreadyOK = theToBiQuad;
9435 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9437 default: alreadyOK = false;
9442 const int id = face->GetID();
9443 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9445 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9447 SMDS_MeshFace * NewFace = 0;
9450 case SMDSEntity_Triangle:
9451 case SMDSEntity_Quad_Triangle:
9452 case SMDSEntity_BiQuad_Triangle:
9453 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9454 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9455 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9458 case SMDSEntity_Quadrangle:
9459 case SMDSEntity_Quad_Quadrangle:
9460 case SMDSEntity_BiQuad_Quadrangle:
9461 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9462 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9463 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9467 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9469 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9473 vector<int> nbNodeInFaces;
9474 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9475 while(aVolumeItr->more())
9477 const SMDS_MeshVolume* volume = aVolumeItr->next();
9478 if ( !volume ) continue;
9480 const SMDSAbs_EntityType type = volume->GetEntityType();
9481 if ( volume->IsQuadratic() )
9486 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9487 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9488 default: alreadyOK = true;
9492 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9496 const int id = volume->GetID();
9497 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9498 if ( type == SMDSEntity_Polyhedra )
9499 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9500 else if ( type == SMDSEntity_Hexagonal_Prism )
9501 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9503 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9505 SMDS_MeshVolume * NewVolume = 0;
9508 case SMDSEntity_Tetra:
9509 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9511 case SMDSEntity_Hexa:
9512 case SMDSEntity_Quad_Hexa:
9513 case SMDSEntity_TriQuad_Hexa:
9514 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9515 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9516 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9517 if ( nodes[i]->NbInverseElements() == 0 )
9518 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9520 case SMDSEntity_Pyramid:
9521 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9522 nodes[3], nodes[4], id, theForce3d);
9524 case SMDSEntity_Penta:
9525 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9526 nodes[3], nodes[4], nodes[5], id, theForce3d);
9528 case SMDSEntity_Hexagonal_Prism:
9530 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9532 ReplaceElemInGroups(volume, NewVolume, meshDS);
9537 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9538 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9539 // aHelper.FixQuadraticElements(myError);
9540 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9544 //================================================================================
9546 * \brief Makes given elements quadratic
9547 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9548 * \param theElements - elements to make quadratic
9550 //================================================================================
9552 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9553 TIDSortedElemSet& theElements,
9554 const bool theToBiQuad)
9556 if ( theElements.empty() ) return;
9558 // we believe that all theElements are of the same type
9559 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9561 // get all nodes shared by theElements
9562 TIDSortedNodeSet allNodes;
9563 TIDSortedElemSet::iterator eIt = theElements.begin();
9564 for ( ; eIt != theElements.end(); ++eIt )
9565 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9567 // complete theElements with elements of lower dim whose all nodes are in allNodes
9569 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9570 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9571 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9572 for ( ; nIt != allNodes.end(); ++nIt )
9574 const SMDS_MeshNode* n = *nIt;
9575 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9576 while ( invIt->more() )
9578 const SMDS_MeshElement* e = invIt->next();
9579 const SMDSAbs_ElementType type = e->GetType();
9580 if ( e->IsQuadratic() )
9582 quadAdjacentElems[ type ].insert( e );
9585 switch ( e->GetEntityType() ) {
9586 case SMDSEntity_Quad_Triangle:
9587 case SMDSEntity_Quad_Quadrangle:
9588 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9589 case SMDSEntity_BiQuad_Triangle:
9590 case SMDSEntity_BiQuad_Quadrangle:
9591 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9592 default: alreadyOK = true;
9597 if ( type >= elemType )
9598 continue; // same type or more complex linear element
9600 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9601 continue; // e is already checked
9605 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9606 while ( nodeIt->more() && allIn )
9607 allIn = allNodes.count( nodeIt->next() );
9609 theElements.insert(e );
9613 SMESH_MesherHelper helper(*myMesh);
9614 helper.SetIsQuadratic( true );
9615 helper.SetIsBiQuadratic( theToBiQuad );
9617 // add links of quadratic adjacent elements to the helper
9619 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9620 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9621 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9623 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9625 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9626 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9627 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9629 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9631 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9632 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9633 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9635 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9638 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9640 SMESHDS_Mesh* meshDS = GetMeshDS();
9641 SMESHDS_SubMesh* smDS = 0;
9642 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9644 const SMDS_MeshElement* elem = *eIt;
9647 int nbCentralNodes = 0;
9648 switch ( elem->GetEntityType() ) {
9649 // linear convertible
9650 case SMDSEntity_Edge:
9651 case SMDSEntity_Triangle:
9652 case SMDSEntity_Quadrangle:
9653 case SMDSEntity_Tetra:
9654 case SMDSEntity_Pyramid:
9655 case SMDSEntity_Hexa:
9656 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9657 // quadratic that can become bi-quadratic
9658 case SMDSEntity_Quad_Triangle:
9659 case SMDSEntity_Quad_Quadrangle:
9660 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9662 case SMDSEntity_BiQuad_Triangle:
9663 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9664 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9666 default: alreadyOK = true;
9668 if ( alreadyOK ) continue;
9670 const SMDSAbs_ElementType type = elem->GetType();
9671 const int id = elem->GetID();
9672 const int nbNodes = elem->NbCornerNodes();
9673 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9675 helper.SetSubShape( elem->getshapeId() );
9677 if ( !smDS || !smDS->Contains( elem ))
9678 smDS = meshDS->MeshElements( elem->getshapeId() );
9679 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9681 SMDS_MeshElement * newElem = 0;
9684 case 4: // cases for most frequently used element types go first (for optimization)
9685 if ( type == SMDSAbs_Volume )
9686 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9688 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9691 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9692 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9695 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9698 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9701 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9702 nodes[4], id, theForce3d);
9705 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9706 nodes[4], nodes[5], id, theForce3d);
9710 ReplaceElemInGroups( elem, newElem, meshDS);
9711 if( newElem && smDS )
9712 smDS->AddElement( newElem );
9714 // remove central nodes
9715 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9716 if ( nodes[i]->NbInverseElements() == 0 )
9717 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9719 } // loop on theElements
9722 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9723 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9724 // helper.FixQuadraticElements( myError );
9725 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9729 //=======================================================================
9731 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9732 * \return int - nb of checked elements
9734 //=======================================================================
9736 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9737 SMDS_ElemIteratorPtr theItr,
9738 const int theShapeID)
9741 SMESHDS_Mesh* meshDS = GetMeshDS();
9742 ElemFeatures elemType;
9743 vector<const SMDS_MeshNode *> nodes;
9745 while( theItr->more() )
9747 const SMDS_MeshElement* elem = theItr->next();
9749 if( elem && elem->IsQuadratic())
9752 int nbCornerNodes = elem->NbCornerNodes();
9753 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9755 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9757 //remove a quadratic element
9758 if ( !theSm || !theSm->Contains( elem ))
9759 theSm = meshDS->MeshElements( elem->getshapeId() );
9760 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9762 // remove medium nodes
9763 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9764 if ( nodes[i]->NbInverseElements() == 0 )
9765 meshDS->RemoveFreeNode( nodes[i], theSm );
9767 // add a linear element
9768 nodes.resize( nbCornerNodes );
9769 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9770 ReplaceElemInGroups(elem, newElem, meshDS);
9771 if( theSm && newElem )
9772 theSm->AddElement( newElem );
9778 //=======================================================================
9779 //function : ConvertFromQuadratic
9781 //=======================================================================
9783 bool SMESH_MeshEditor::ConvertFromQuadratic()
9785 int nbCheckedElems = 0;
9786 if ( myMesh->HasShapeToMesh() )
9788 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9790 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9791 while ( smIt->more() ) {
9792 SMESH_subMesh* sm = smIt->next();
9793 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9794 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9800 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9801 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9803 SMESHDS_SubMesh *aSM = 0;
9804 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9812 //================================================================================
9814 * \brief Return true if all medium nodes of the element are in the node set
9816 //================================================================================
9818 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9820 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9821 if ( !nodeSet.count( elem->GetNode(i) ))
9827 //================================================================================
9829 * \brief Makes given elements linear
9831 //================================================================================
9833 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9835 if ( theElements.empty() ) return;
9837 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9838 set<int> mediumNodeIDs;
9839 TIDSortedElemSet::iterator eIt = theElements.begin();
9840 for ( ; eIt != theElements.end(); ++eIt )
9842 const SMDS_MeshElement* e = *eIt;
9843 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9844 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9847 // replace given elements by linear ones
9848 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9849 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9851 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9852 // except those elements sharing medium nodes of quadratic element whose medium nodes
9853 // are not all in mediumNodeIDs
9855 // get remaining medium nodes
9856 TIDSortedNodeSet mediumNodes;
9857 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9858 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9859 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9860 mediumNodes.insert( mediumNodes.end(), n );
9862 // find more quadratic elements to convert
9863 TIDSortedElemSet moreElemsToConvert;
9864 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9865 for ( ; nIt != mediumNodes.end(); ++nIt )
9867 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9868 while ( invIt->more() )
9870 const SMDS_MeshElement* e = invIt->next();
9871 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9873 // find a more complex element including e and
9874 // whose medium nodes are not in mediumNodes
9875 bool complexFound = false;
9876 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9878 SMDS_ElemIteratorPtr invIt2 =
9879 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9880 while ( invIt2->more() )
9882 const SMDS_MeshElement* eComplex = invIt2->next();
9883 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9885 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9886 if ( nbCommonNodes == e->NbNodes())
9888 complexFound = true;
9889 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9895 if ( !complexFound )
9896 moreElemsToConvert.insert( e );
9900 elemIt = elemSetIterator( moreElemsToConvert );
9901 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9904 //=======================================================================
9905 //function : SewSideElements
9907 //=======================================================================
9909 SMESH_MeshEditor::Sew_Error
9910 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9911 TIDSortedElemSet& theSide2,
9912 const SMDS_MeshNode* theFirstNode1,
9913 const SMDS_MeshNode* theFirstNode2,
9914 const SMDS_MeshNode* theSecondNode1,
9915 const SMDS_MeshNode* theSecondNode2)
9917 myLastCreatedElems.Clear();
9918 myLastCreatedNodes.Clear();
9920 MESSAGE ("::::SewSideElements()");
9921 if ( theSide1.size() != theSide2.size() )
9922 return SEW_DIFF_NB_OF_ELEMENTS;
9924 Sew_Error aResult = SEW_OK;
9926 // 1. Build set of faces representing each side
9927 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9928 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9930 // =======================================================================
9931 // 1. Build set of faces representing each side:
9932 // =======================================================================
9933 // a. build set of nodes belonging to faces
9934 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9935 // c. create temporary faces representing side of volumes if correspondent
9936 // face does not exist
9938 SMESHDS_Mesh* aMesh = GetMeshDS();
9939 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9940 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9941 TIDSortedElemSet faceSet1, faceSet2;
9942 set<const SMDS_MeshElement*> volSet1, volSet2;
9943 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9944 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9945 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9946 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9947 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9948 int iSide, iFace, iNode;
9950 list<const SMDS_MeshElement* > tempFaceList;
9951 for ( iSide = 0; iSide < 2; iSide++ ) {
9952 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9953 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9954 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9955 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9956 set<const SMDS_MeshElement*>::iterator vIt;
9957 TIDSortedElemSet::iterator eIt;
9958 set<const SMDS_MeshNode*>::iterator nIt;
9960 // check that given nodes belong to given elements
9961 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9962 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9963 int firstIndex = -1, secondIndex = -1;
9964 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9965 const SMDS_MeshElement* elem = *eIt;
9966 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9967 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9968 if ( firstIndex > -1 && secondIndex > -1 ) break;
9970 if ( firstIndex < 0 || secondIndex < 0 ) {
9971 // we can simply return until temporary faces created
9972 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9975 // -----------------------------------------------------------
9976 // 1a. Collect nodes of existing faces
9977 // and build set of face nodes in order to detect missing
9978 // faces corresponding to sides of volumes
9979 // -----------------------------------------------------------
9981 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9983 // loop on the given element of a side
9984 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9985 //const SMDS_MeshElement* elem = *eIt;
9986 const SMDS_MeshElement* elem = *eIt;
9987 if ( elem->GetType() == SMDSAbs_Face ) {
9988 faceSet->insert( elem );
9989 set <const SMDS_MeshNode*> faceNodeSet;
9990 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9991 while ( nodeIt->more() ) {
9992 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9993 nodeSet->insert( n );
9994 faceNodeSet.insert( n );
9996 setOfFaceNodeSet.insert( faceNodeSet );
9998 else if ( elem->GetType() == SMDSAbs_Volume )
9999 volSet->insert( elem );
10001 // ------------------------------------------------------------------------------
10002 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10003 // ------------------------------------------------------------------------------
10005 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10006 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10007 while ( fIt->more() ) { // loop on faces sharing a node
10008 const SMDS_MeshElement* f = fIt->next();
10009 if ( faceSet->find( f ) == faceSet->end() ) {
10010 // check if all nodes are in nodeSet and
10011 // complete setOfFaceNodeSet if they are
10012 set <const SMDS_MeshNode*> faceNodeSet;
10013 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10014 bool allInSet = true;
10015 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10016 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10017 if ( nodeSet->find( n ) == nodeSet->end() )
10020 faceNodeSet.insert( n );
10023 faceSet->insert( f );
10024 setOfFaceNodeSet.insert( faceNodeSet );
10030 // -------------------------------------------------------------------------
10031 // 1c. Create temporary faces representing sides of volumes if correspondent
10032 // face does not exist
10033 // -------------------------------------------------------------------------
10035 if ( !volSet->empty() ) {
10036 //int nodeSetSize = nodeSet->size();
10038 // loop on given volumes
10039 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10040 SMDS_VolumeTool vol (*vIt);
10041 // loop on volume faces: find free faces
10042 // --------------------------------------
10043 list<const SMDS_MeshElement* > freeFaceList;
10044 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10045 if ( !vol.IsFreeFace( iFace ))
10047 // check if there is already a face with same nodes in a face set
10048 const SMDS_MeshElement* aFreeFace = 0;
10049 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10050 int nbNodes = vol.NbFaceNodes( iFace );
10051 set <const SMDS_MeshNode*> faceNodeSet;
10052 vol.GetFaceNodes( iFace, faceNodeSet );
10053 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10055 // no such a face is given but it still can exist, check it
10056 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10057 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10059 if ( !aFreeFace ) {
10060 // create a temporary face
10061 if ( nbNodes == 3 ) {
10062 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10063 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10065 else if ( nbNodes == 4 ) {
10066 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10067 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10070 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10071 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10072 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10075 tempFaceList.push_back( aFreeFace );
10079 freeFaceList.push_back( aFreeFace );
10081 } // loop on faces of a volume
10083 // choose one of several free faces of a volume
10084 // --------------------------------------------
10085 if ( freeFaceList.size() > 1 ) {
10086 // choose a face having max nb of nodes shared by other elems of a side
10087 int maxNbNodes = -1;
10088 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10089 while ( fIt != freeFaceList.end() ) { // loop on free faces
10090 int nbSharedNodes = 0;
10091 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10092 while ( nodeIt->more() ) { // loop on free face nodes
10093 const SMDS_MeshNode* n =
10094 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10095 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10096 while ( invElemIt->more() ) {
10097 const SMDS_MeshElement* e = invElemIt->next();
10098 nbSharedNodes += faceSet->count( e );
10099 nbSharedNodes += elemSet->count( e );
10102 if ( nbSharedNodes > maxNbNodes ) {
10103 maxNbNodes = nbSharedNodes;
10104 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10106 else if ( nbSharedNodes == maxNbNodes ) {
10110 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10113 if ( freeFaceList.size() > 1 )
10115 // could not choose one face, use another way
10116 // choose a face most close to the bary center of the opposite side
10117 gp_XYZ aBC( 0., 0., 0. );
10118 set <const SMDS_MeshNode*> addedNodes;
10119 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10120 eIt = elemSet2->begin();
10121 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10122 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10123 while ( nodeIt->more() ) { // loop on free face nodes
10124 const SMDS_MeshNode* n =
10125 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10126 if ( addedNodes.insert( n ).second )
10127 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10130 aBC /= addedNodes.size();
10131 double minDist = DBL_MAX;
10132 fIt = freeFaceList.begin();
10133 while ( fIt != freeFaceList.end() ) { // loop on free faces
10135 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10136 while ( nodeIt->more() ) { // loop on free face nodes
10137 const SMDS_MeshNode* n =
10138 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10139 gp_XYZ p( n->X(),n->Y(),n->Z() );
10140 dist += ( aBC - p ).SquareModulus();
10142 if ( dist < minDist ) {
10144 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10147 fIt = freeFaceList.erase( fIt++ );
10150 } // choose one of several free faces of a volume
10152 if ( freeFaceList.size() == 1 ) {
10153 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10154 faceSet->insert( aFreeFace );
10155 // complete a node set with nodes of a found free face
10156 // for ( iNode = 0; iNode < ; iNode++ )
10157 // nodeSet->insert( fNodes[ iNode ] );
10160 } // loop on volumes of a side
10162 // // complete a set of faces if new nodes in a nodeSet appeared
10163 // // ----------------------------------------------------------
10164 // if ( nodeSetSize != nodeSet->size() ) {
10165 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10166 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10167 // while ( fIt->more() ) { // loop on faces sharing a node
10168 // const SMDS_MeshElement* f = fIt->next();
10169 // if ( faceSet->find( f ) == faceSet->end() ) {
10170 // // check if all nodes are in nodeSet and
10171 // // complete setOfFaceNodeSet if they are
10172 // set <const SMDS_MeshNode*> faceNodeSet;
10173 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10174 // bool allInSet = true;
10175 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10176 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10177 // if ( nodeSet->find( n ) == nodeSet->end() )
10178 // allInSet = false;
10180 // faceNodeSet.insert( n );
10182 // if ( allInSet ) {
10183 // faceSet->insert( f );
10184 // setOfFaceNodeSet.insert( faceNodeSet );
10190 } // Create temporary faces, if there are volumes given
10193 if ( faceSet1.size() != faceSet2.size() ) {
10194 // delete temporary faces: they are in reverseElements of actual nodes
10195 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10196 // while ( tmpFaceIt->more() )
10197 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10198 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10199 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10200 // aMesh->RemoveElement(*tmpFaceIt);
10201 MESSAGE("Diff nb of faces");
10202 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10205 // ============================================================
10206 // 2. Find nodes to merge:
10207 // bind a node to remove to a node to put instead
10208 // ============================================================
10210 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10211 if ( theFirstNode1 != theFirstNode2 )
10212 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10213 if ( theSecondNode1 != theSecondNode2 )
10214 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10216 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10217 set< long > linkIdSet; // links to process
10218 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10220 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10221 list< NLink > linkList[2];
10222 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10223 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10224 // loop on links in linkList; find faces by links and append links
10225 // of the found faces to linkList
10226 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10227 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10229 NLink link[] = { *linkIt[0], *linkIt[1] };
10230 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10231 if ( !linkIdSet.count( linkID ) )
10234 // by links, find faces in the face sets,
10235 // and find indices of link nodes in the found faces;
10236 // in a face set, there is only one or no face sharing a link
10237 // ---------------------------------------------------------------
10239 const SMDS_MeshElement* face[] = { 0, 0 };
10240 vector<const SMDS_MeshNode*> fnodes[2];
10241 int iLinkNode[2][2];
10242 TIDSortedElemSet avoidSet;
10243 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10244 const SMDS_MeshNode* n1 = link[iSide].first;
10245 const SMDS_MeshNode* n2 = link[iSide].second;
10246 //cout << "Side " << iSide << " ";
10247 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10248 // find a face by two link nodes
10249 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10250 *faceSetPtr[ iSide ], avoidSet,
10251 &iLinkNode[iSide][0],
10252 &iLinkNode[iSide][1] );
10253 if ( face[ iSide ])
10255 //cout << " F " << face[ iSide]->GetID() <<endl;
10256 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10257 // put face nodes to fnodes
10258 if ( face[ iSide ]->IsQuadratic() )
10260 // use interlaced nodes iterator
10261 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10262 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10263 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10264 while ( nIter->more() )
10265 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10269 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10270 face[ iSide ]->end_nodes() );
10272 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10276 // check similarity of elements of the sides
10277 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10278 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10279 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10280 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10283 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10285 break; // do not return because it's necessary to remove tmp faces
10288 // set nodes to merge
10289 // -------------------
10291 if ( face[0] && face[1] ) {
10292 const int nbNodes = face[0]->NbNodes();
10293 if ( nbNodes != face[1]->NbNodes() ) {
10294 MESSAGE("Diff nb of face nodes");
10295 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10296 break; // do not return because it s necessary to remove tmp faces
10298 bool reverse[] = { false, false }; // order of nodes in the link
10299 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10300 // analyse link orientation in faces
10301 int i1 = iLinkNode[ iSide ][ 0 ];
10302 int i2 = iLinkNode[ iSide ][ 1 ];
10303 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10305 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10306 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10307 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10309 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10310 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10313 // add other links of the faces to linkList
10314 // -----------------------------------------
10316 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10317 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10318 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10319 if ( !iter_isnew.second ) { // already in a set: no need to process
10320 linkIdSet.erase( iter_isnew.first );
10322 else // new in set == encountered for the first time: add
10324 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10325 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10326 linkList[0].push_back ( NLink( n1, n2 ));
10327 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10332 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10335 } // loop on link lists
10337 if ( aResult == SEW_OK &&
10338 ( //linkIt[0] != linkList[0].end() ||
10339 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10340 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10341 " " << (faceSetPtr[1]->empty()));
10342 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10345 // ====================================================================
10346 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10347 // ====================================================================
10349 // delete temporary faces
10350 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10351 // while ( tmpFaceIt->more() )
10352 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10353 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10354 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10355 aMesh->RemoveElement(*tmpFaceIt);
10357 if ( aResult != SEW_OK)
10360 list< int > nodeIDsToRemove;
10361 vector< const SMDS_MeshNode*> nodes;
10362 ElemFeatures elemType;
10364 // loop on nodes replacement map
10365 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10366 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10367 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10369 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10370 nodeIDsToRemove.push_back( nToRemove->GetID() );
10371 // loop on elements sharing nToRemove
10372 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10373 while ( invElemIt->more() ) {
10374 const SMDS_MeshElement* e = invElemIt->next();
10375 // get a new suite of nodes: make replacement
10376 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10377 nodes.resize( nbNodes );
10378 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10379 while ( nIt->more() ) {
10380 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10381 nnIt = nReplaceMap.find( n );
10382 if ( nnIt != nReplaceMap.end() ) {
10384 n = (*nnIt).second;
10388 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10389 // elemIDsToRemove.push_back( e->GetID() );
10393 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10394 aMesh->RemoveElement( e );
10396 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10398 AddToSameGroups( newElem, e, aMesh );
10399 if ( int aShapeId = e->getshapeId() )
10400 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10406 Remove( nodeIDsToRemove, true );
10411 //================================================================================
10413 * \brief Find corresponding nodes in two sets of faces
10414 * \param theSide1 - first face set
10415 * \param theSide2 - second first face
10416 * \param theFirstNode1 - a boundary node of set 1
10417 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10418 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10419 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10420 * \param nReplaceMap - output map of corresponding nodes
10421 * \return bool - is a success or not
10423 //================================================================================
10426 //#define DEBUG_MATCHING_NODES
10429 SMESH_MeshEditor::Sew_Error
10430 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10431 set<const SMDS_MeshElement*>& theSide2,
10432 const SMDS_MeshNode* theFirstNode1,
10433 const SMDS_MeshNode* theFirstNode2,
10434 const SMDS_MeshNode* theSecondNode1,
10435 const SMDS_MeshNode* theSecondNode2,
10436 TNodeNodeMap & nReplaceMap)
10438 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10440 nReplaceMap.clear();
10441 if ( theFirstNode1 != theFirstNode2 )
10442 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10443 if ( theSecondNode1 != theSecondNode2 )
10444 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10446 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10447 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10449 list< NLink > linkList[2];
10450 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10451 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10453 // loop on links in linkList; find faces by links and append links
10454 // of the found faces to linkList
10455 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10456 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10457 NLink link[] = { *linkIt[0], *linkIt[1] };
10458 if ( linkSet.find( link[0] ) == linkSet.end() )
10461 // by links, find faces in the face sets,
10462 // and find indices of link nodes in the found faces;
10463 // in a face set, there is only one or no face sharing a link
10464 // ---------------------------------------------------------------
10466 const SMDS_MeshElement* face[] = { 0, 0 };
10467 list<const SMDS_MeshNode*> notLinkNodes[2];
10468 //bool reverse[] = { false, false }; // order of notLinkNodes
10470 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10472 const SMDS_MeshNode* n1 = link[iSide].first;
10473 const SMDS_MeshNode* n2 = link[iSide].second;
10474 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10475 set< const SMDS_MeshElement* > facesOfNode1;
10476 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10478 // during a loop of the first node, we find all faces around n1,
10479 // during a loop of the second node, we find one face sharing both n1 and n2
10480 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10481 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10482 while ( fIt->more() ) { // loop on faces sharing a node
10483 const SMDS_MeshElement* f = fIt->next();
10484 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10485 ! facesOfNode1.insert( f ).second ) // f encounters twice
10487 if ( face[ iSide ] ) {
10488 MESSAGE( "2 faces per link " );
10489 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10492 faceSet->erase( f );
10494 // get not link nodes
10495 int nbN = f->NbNodes();
10496 if ( f->IsQuadratic() )
10498 nbNodes[ iSide ] = nbN;
10499 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10500 int i1 = f->GetNodeIndex( n1 );
10501 int i2 = f->GetNodeIndex( n2 );
10502 int iEnd = nbN, iBeg = -1, iDelta = 1;
10503 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10505 std::swap( iEnd, iBeg ); iDelta = -1;
10510 if ( i == iEnd ) i = iBeg + iDelta;
10511 if ( i == i1 ) break;
10512 nodes.push_back ( f->GetNode( i ) );
10518 // check similarity of elements of the sides
10519 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10520 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10521 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10522 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10525 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10529 // set nodes to merge
10530 // -------------------
10532 if ( face[0] && face[1] ) {
10533 if ( nbNodes[0] != nbNodes[1] ) {
10534 MESSAGE("Diff nb of face nodes");
10535 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10537 #ifdef DEBUG_MATCHING_NODES
10538 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10539 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10540 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10542 int nbN = nbNodes[0];
10544 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10545 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10546 for ( int i = 0 ; i < nbN - 2; ++i ) {
10547 #ifdef DEBUG_MATCHING_NODES
10548 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10550 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10554 // add other links of the face 1 to linkList
10555 // -----------------------------------------
10557 const SMDS_MeshElement* f0 = face[0];
10558 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10559 for ( int i = 0; i < nbN; i++ )
10561 const SMDS_MeshNode* n2 = f0->GetNode( i );
10562 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10563 linkSet.insert( SMESH_TLink( n1, n2 ));
10564 if ( !iter_isnew.second ) { // already in a set: no need to process
10565 linkSet.erase( iter_isnew.first );
10567 else // new in set == encountered for the first time: add
10569 #ifdef DEBUG_MATCHING_NODES
10570 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10571 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10573 linkList[0].push_back ( NLink( n1, n2 ));
10574 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10579 } // loop on link lists
10584 //================================================================================
10586 * \brief Create elements equal (on same nodes) to given ones
10587 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10588 * elements of the uppest dimension are duplicated.
10590 //================================================================================
10592 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10594 ClearLastCreated();
10595 SMESHDS_Mesh* mesh = GetMeshDS();
10597 // get an element type and an iterator over elements
10599 SMDSAbs_ElementType type;
10600 SMDS_ElemIteratorPtr elemIt;
10601 vector< const SMDS_MeshElement* > allElems;
10602 if ( theElements.empty() )
10604 if ( mesh->NbNodes() == 0 )
10606 // get most complex type
10607 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10608 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10609 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10611 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10612 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10617 // put all elements in the vector <allElems>
10618 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10619 elemIt = mesh->elementsIterator( type );
10620 while ( elemIt->more() )
10621 allElems.push_back( elemIt->next());
10622 elemIt = elemSetIterator( allElems );
10626 type = (*theElements.begin())->GetType();
10627 elemIt = elemSetIterator( theElements );
10630 // duplicate elements
10632 ElemFeatures elemType;
10634 vector< const SMDS_MeshNode* > nodes;
10635 while ( elemIt->more() )
10637 const SMDS_MeshElement* elem = elemIt->next();
10638 if ( elem->GetType() != type )
10641 elemType.Init( elem, /*basicOnly=*/false );
10642 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10644 AddElement( nodes, elemType );
10648 //================================================================================
10650 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10651 \param theElems - the list of elements (edges or faces) to be replicated
10652 The nodes for duplication could be found from these elements
10653 \param theNodesNot - list of nodes to NOT replicate
10654 \param theAffectedElems - the list of elements (cells and edges) to which the
10655 replicated nodes should be associated to.
10656 \return TRUE if operation has been completed successfully, FALSE otherwise
10658 //================================================================================
10660 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10661 const TIDSortedElemSet& theNodesNot,
10662 const TIDSortedElemSet& theAffectedElems )
10664 myLastCreatedElems.Clear();
10665 myLastCreatedNodes.Clear();
10667 if ( theElems.size() == 0 )
10670 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10675 TNodeNodeMap anOldNodeToNewNode;
10676 // duplicate elements and nodes
10677 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10678 // replce nodes by duplications
10679 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10683 //================================================================================
10685 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10686 \param theMeshDS - mesh instance
10687 \param theElems - the elements replicated or modified (nodes should be changed)
10688 \param theNodesNot - nodes to NOT replicate
10689 \param theNodeNodeMap - relation of old node to new created node
10690 \param theIsDoubleElem - flag os to replicate element or modify
10691 \return TRUE if operation has been completed successfully, FALSE otherwise
10693 //================================================================================
10695 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10696 const TIDSortedElemSet& theElems,
10697 const TIDSortedElemSet& theNodesNot,
10698 TNodeNodeMap& theNodeNodeMap,
10699 const bool theIsDoubleElem )
10701 MESSAGE("doubleNodes");
10702 // iterate through element and duplicate them (by nodes duplication)
10704 std::vector<const SMDS_MeshNode*> newNodes;
10705 ElemFeatures elemType;
10707 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10708 for ( ; elemItr != theElems.end(); ++elemItr )
10710 const SMDS_MeshElement* anElem = *elemItr;
10714 // duplicate nodes to duplicate element
10715 bool isDuplicate = false;
10716 newNodes.resize( anElem->NbNodes() );
10717 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10719 while ( anIter->more() )
10721 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10722 const SMDS_MeshNode* aNewNode = aCurrNode;
10723 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10724 if ( n2n != theNodeNodeMap.end() )
10726 aNewNode = n2n->second;
10728 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10731 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10732 copyPosition( aCurrNode, aNewNode );
10733 theNodeNodeMap[ aCurrNode ] = aNewNode;
10734 myLastCreatedNodes.Append( aNewNode );
10736 isDuplicate |= (aCurrNode != aNewNode);
10737 newNodes[ ind++ ] = aNewNode;
10739 if ( !isDuplicate )
10742 if ( theIsDoubleElem )
10743 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10745 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10752 //================================================================================
10754 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10755 \param theNodes - identifiers of nodes to be doubled
10756 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10757 nodes. If list of element identifiers is empty then nodes are doubled but
10758 they not assigned to elements
10759 \return TRUE if operation has been completed successfully, FALSE otherwise
10761 //================================================================================
10763 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10764 const std::list< int >& theListOfModifiedElems )
10766 MESSAGE("DoubleNodes");
10767 myLastCreatedElems.Clear();
10768 myLastCreatedNodes.Clear();
10770 if ( theListOfNodes.size() == 0 )
10773 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10777 // iterate through nodes and duplicate them
10779 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10781 std::list< int >::const_iterator aNodeIter;
10782 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10784 int aCurr = *aNodeIter;
10785 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10791 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10794 copyPosition( aNode, aNewNode );
10795 anOldNodeToNewNode[ aNode ] = aNewNode;
10796 myLastCreatedNodes.Append( aNewNode );
10800 // Create map of new nodes for modified elements
10802 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10804 std::list< int >::const_iterator anElemIter;
10805 for ( anElemIter = theListOfModifiedElems.begin();
10806 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10808 int aCurr = *anElemIter;
10809 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10813 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10815 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10817 while ( anIter->more() )
10819 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10820 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10822 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10823 aNodeArr[ ind++ ] = aNewNode;
10826 aNodeArr[ ind++ ] = aCurrNode;
10828 anElemToNodes[ anElem ] = aNodeArr;
10831 // Change nodes of elements
10833 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10834 anElemToNodesIter = anElemToNodes.begin();
10835 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10837 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10838 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10841 MESSAGE("ChangeElementNodes");
10842 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10851 //================================================================================
10853 \brief Check if element located inside shape
10854 \return TRUE if IN or ON shape, FALSE otherwise
10856 //================================================================================
10858 template<class Classifier>
10859 bool isInside(const SMDS_MeshElement* theElem,
10860 Classifier& theClassifier,
10861 const double theTol)
10863 gp_XYZ centerXYZ (0, 0, 0);
10864 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10865 while (aNodeItr->more())
10866 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10868 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10869 theClassifier.Perform(aPnt, theTol);
10870 TopAbs_State aState = theClassifier.State();
10871 return (aState == TopAbs_IN || aState == TopAbs_ON );
10874 //================================================================================
10876 * \brief Classifier of the 3D point on the TopoDS_Face
10877 * with interaface suitable for isInside()
10879 //================================================================================
10881 struct _FaceClassifier
10883 Extrema_ExtPS _extremum;
10884 BRepAdaptor_Surface _surface;
10885 TopAbs_State _state;
10887 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10889 _extremum.Initialize( _surface,
10890 _surface.FirstUParameter(), _surface.LastUParameter(),
10891 _surface.FirstVParameter(), _surface.LastVParameter(),
10892 _surface.Tolerance(), _surface.Tolerance() );
10894 void Perform(const gp_Pnt& aPnt, double theTol)
10897 _state = TopAbs_OUT;
10898 _extremum.Perform(aPnt);
10899 if ( _extremum.IsDone() )
10900 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10901 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10903 TopAbs_State State() const
10910 //================================================================================
10912 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10913 This method is the first step of DoubleNodeElemGroupsInRegion.
10914 \param theElems - list of groups of elements (edges or faces) to be replicated
10915 \param theNodesNot - list of groups of nodes not to replicated
10916 \param theShape - shape to detect affected elements (element which geometric center
10917 located on or inside shape). If the shape is null, detection is done on faces orientations
10918 (select elements with a gravity center on the side given by faces normals).
10919 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10920 The replicated nodes should be associated to affected elements.
10921 \return groups of affected elements
10922 \sa DoubleNodeElemGroupsInRegion()
10924 //================================================================================
10926 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10927 const TIDSortedElemSet& theNodesNot,
10928 const TopoDS_Shape& theShape,
10929 TIDSortedElemSet& theAffectedElems)
10931 if ( theShape.IsNull() )
10933 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10934 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10935 std::set<const SMDS_MeshElement*> edgesToCheck;
10936 alreadyCheckedNodes.clear();
10937 alreadyCheckedElems.clear();
10938 edgesToCheck.clear();
10940 // --- iterates on elements to be replicated and get elements by back references from their nodes
10942 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10944 for ( ielem=1; elemItr != theElems.end(); ++elemItr )
10946 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10947 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10950 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10951 MESSAGE("element " << ielem++ << " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10952 std::set<const SMDS_MeshNode*> nodesElem;
10954 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10955 while ( nodeItr->more() )
10957 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10958 nodesElem.insert(aNode);
10960 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10961 for (; nodit != nodesElem.end(); nodit++)
10963 MESSAGE(" noeud ");
10964 const SMDS_MeshNode* aNode = *nodit;
10965 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10967 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10969 alreadyCheckedNodes.insert(aNode);
10970 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10971 while ( backElemItr->more() )
10973 MESSAGE(" backelem ");
10974 const SMDS_MeshElement* curElem = backElemItr->next();
10975 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10977 if (theElems.find(curElem) != theElems.end())
10979 alreadyCheckedElems.insert(curElem);
10980 double x=0, y=0, z=0;
10982 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10983 while ( nodeItr2->more() )
10985 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10986 x += anotherNode->X();
10987 y += anotherNode->Y();
10988 z += anotherNode->Z();
10992 p.SetCoord( x/nb -aNode->X(),
10994 z/nb -aNode->Z() );
10995 MESSAGE(" check " << p.X() << " " << p.Y() << " " << p.Z());
10998 MESSAGE(" --- inserted")
10999 theAffectedElems.insert( curElem );
11001 else if (curElem->GetType() == SMDSAbs_Edge)
11002 edgesToCheck.insert(curElem);
11006 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
11007 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
11008 for( ; eit != edgesToCheck.end(); eit++)
11010 bool onside = true;
11011 const SMDS_MeshElement* anEdge = *eit;
11012 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
11013 while ( nodeItr->more() )
11015 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11016 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
11024 MESSAGE(" --- edge onside inserted")
11025 theAffectedElems.insert(anEdge);
11031 const double aTol = Precision::Confusion();
11032 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11033 auto_ptr<_FaceClassifier> aFaceClassifier;
11034 if ( theShape.ShapeType() == TopAbs_SOLID )
11036 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11037 bsc3d->PerformInfinitePoint(aTol);
11039 else if (theShape.ShapeType() == TopAbs_FACE )
11041 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11044 // iterates on indicated elements and get elements by back references from their nodes
11045 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11047 for ( ielem = 1; elemItr != theElems.end(); ++elemItr )
11049 MESSAGE("element " << ielem++);
11050 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11053 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11054 while ( nodeItr->more() )
11056 MESSAGE(" noeud ");
11057 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11058 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11060 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11061 while ( backElemItr->more() )
11063 MESSAGE(" backelem ");
11064 const SMDS_MeshElement* curElem = backElemItr->next();
11065 if ( curElem && theElems.find(curElem) == theElems.end() &&
11067 isInside( curElem, *bsc3d, aTol ) :
11068 isInside( curElem, *aFaceClassifier, aTol )))
11069 theAffectedElems.insert( curElem );
11077 //================================================================================
11079 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11080 \param theElems - group of of elements (edges or faces) to be replicated
11081 \param theNodesNot - group of nodes not to replicate
11082 \param theShape - shape to detect affected elements (element which geometric center
11083 located on or inside shape).
11084 The replicated nodes should be associated to affected elements.
11085 \return TRUE if operation has been completed successfully, FALSE otherwise
11087 //================================================================================
11089 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11090 const TIDSortedElemSet& theNodesNot,
11091 const TopoDS_Shape& theShape )
11093 if ( theShape.IsNull() )
11096 const double aTol = Precision::Confusion();
11097 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11098 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
11099 if ( theShape.ShapeType() == TopAbs_SOLID )
11101 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11102 bsc3d->PerformInfinitePoint(aTol);
11104 else if (theShape.ShapeType() == TopAbs_FACE )
11106 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11109 // iterates on indicated elements and get elements by back references from their nodes
11110 TIDSortedElemSet anAffected;
11111 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11112 for ( ; elemItr != theElems.end(); ++elemItr )
11114 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11118 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11119 while ( nodeItr->more() )
11121 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11122 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11124 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11125 while ( backElemItr->more() )
11127 const SMDS_MeshElement* curElem = backElemItr->next();
11128 if ( curElem && theElems.find(curElem) == theElems.end() &&
11130 isInside( curElem, *bsc3d, aTol ) :
11131 isInside( curElem, *aFaceClassifier, aTol )))
11132 anAffected.insert( curElem );
11136 return DoubleNodes( theElems, theNodesNot, anAffected );
11140 * \brief compute an oriented angle between two planes defined by four points.
11141 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11142 * @param p0 base of the rotation axe
11143 * @param p1 extremity of the rotation axe
11144 * @param g1 belongs to the first plane
11145 * @param g2 belongs to the second plane
11147 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11149 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11150 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11151 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11152 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11153 gp_Vec vref(p0, p1);
11156 gp_Vec n1 = vref.Crossed(v1);
11157 gp_Vec n2 = vref.Crossed(v2);
11159 return n2.AngleWithRef(n1, vref);
11161 catch ( Standard_Failure ) {
11163 return Max( v1.Magnitude(), v2.Magnitude() );
11167 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11168 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11169 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11170 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11171 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11172 * 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.
11173 * 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.
11174 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11175 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11176 * \param theElems - list of groups of volumes, where a group of volume is a set of
11177 * SMDS_MeshElements sorted by Id.
11178 * \param createJointElems - if TRUE, create the elements
11179 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11180 * the boundary between \a theDomains and the rest mesh
11181 * \return TRUE if operation has been completed successfully, FALSE otherwise
11183 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11184 bool createJointElems,
11185 bool onAllBoundaries)
11187 MESSAGE("----------------------------------------------");
11188 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11189 MESSAGE("----------------------------------------------");
11191 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11192 meshDS->BuildDownWardConnectivity(true);
11194 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11196 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11197 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11198 // build the list of nodes shared by 2 or more domains, with their domain indexes
11200 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11201 std::map<int,int>celldom; // cell vtkId --> domain
11202 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11203 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11204 faceDomains.clear();
11206 cellDomains.clear();
11207 nodeDomains.clear();
11208 std::map<int,int> emptyMap;
11209 std::set<int> emptySet;
11212 MESSAGE(".. Number of domains :"<<theElems.size());
11214 TIDSortedElemSet theRestDomElems;
11215 const int iRestDom = -1;
11216 const int idom0 = onAllBoundaries ? iRestDom : 0;
11217 const int nbDomains = theElems.size();
11219 // Check if the domains do not share an element
11220 for (int idom = 0; idom < nbDomains-1; idom++)
11222 // MESSAGE("... Check of domain #" << idom);
11223 const TIDSortedElemSet& domain = theElems[idom];
11224 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11225 for (; elemItr != domain.end(); ++elemItr)
11227 const SMDS_MeshElement* anElem = *elemItr;
11228 int idombisdeb = idom + 1 ;
11229 // check if the element belongs to a domain further in the list
11230 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11232 const TIDSortedElemSet& domainbis = theElems[idombis];
11233 if ( domainbis.count( anElem ))
11235 MESSAGE(".... Domain #" << idom);
11236 MESSAGE(".... Domain #" << idombis);
11237 throw SALOME_Exception("The domains are not disjoint.");
11244 for (int idom = 0; idom < nbDomains; idom++)
11247 // --- build a map (face to duplicate --> volume to modify)
11248 // with all the faces shared by 2 domains (group of elements)
11249 // and corresponding volume of this domain, for each shared face.
11250 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11252 MESSAGE("... Neighbors of domain #" << idom);
11253 const TIDSortedElemSet& domain = theElems[idom];
11254 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11255 for (; elemItr != domain.end(); ++elemItr)
11257 const SMDS_MeshElement* anElem = *elemItr;
11260 int vtkId = anElem->getVtkId();
11261 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11262 int neighborsVtkIds[NBMAXNEIGHBORS];
11263 int downIds[NBMAXNEIGHBORS];
11264 unsigned char downTypes[NBMAXNEIGHBORS];
11265 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11266 for (int n = 0; n < nbNeighbors; n++)
11268 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11269 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11270 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11273 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11275 // MESSAGE("Domain " << idombis);
11276 const TIDSortedElemSet& domainbis = theElems[idombis];
11277 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11279 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11281 DownIdType face(downIds[n], downTypes[n]);
11282 if (!faceDomains[face].count(idom))
11284 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11285 celldom[vtkId] = idom;
11286 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11290 theRestDomElems.insert( elem );
11291 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11292 celldom[neighborsVtkIds[n]] = iRestDom;
11300 //MESSAGE("Number of shared faces " << faceDomains.size());
11301 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11303 // --- explore the shared faces domain by domain,
11304 // explore the nodes of the face and see if they belong to a cell in the domain,
11305 // which has only a node or an edge on the border (not a shared face)
11307 for (int idomain = idom0; idomain < nbDomains; idomain++)
11309 //MESSAGE("Domain " << idomain);
11310 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11311 itface = faceDomains.begin();
11312 for (; itface != faceDomains.end(); ++itface)
11314 const std::map<int, int>& domvol = itface->second;
11315 if (!domvol.count(idomain))
11317 DownIdType face = itface->first;
11318 //MESSAGE(" --- face " << face.cellId);
11319 std::set<int> oldNodes;
11321 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11322 std::set<int>::iterator itn = oldNodes.begin();
11323 for (; itn != oldNodes.end(); ++itn)
11326 //MESSAGE(" node " << oldId);
11327 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11328 for (int i=0; i<l.ncells; i++)
11330 int vtkId = l.cells[i];
11331 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11332 if (!domain.count(anElem))
11334 int vtkType = grid->GetCellType(vtkId);
11335 int downId = grid->CellIdToDownId(vtkId);
11338 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11339 continue; // not OK at this stage of the algorithm:
11340 //no cells created after BuildDownWardConnectivity
11342 DownIdType aCell(downId, vtkType);
11343 cellDomains[aCell][idomain] = vtkId;
11344 celldom[vtkId] = idomain;
11345 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11351 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11352 // for each shared face, get the nodes
11353 // for each node, for each domain of the face, create a clone of the node
11355 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11356 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11357 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11359 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11360 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11361 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11363 MESSAGE(".. Duplication of the nodes");
11364 for (int idomain = idom0; idomain < nbDomains; idomain++)
11366 itface = faceDomains.begin();
11367 for (; itface != faceDomains.end(); ++itface)
11369 const std::map<int, int>& domvol = itface->second;
11370 if (!domvol.count(idomain))
11372 DownIdType face = itface->first;
11373 //MESSAGE(" --- face " << face.cellId);
11374 std::set<int> oldNodes;
11376 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11377 std::set<int>::iterator itn = oldNodes.begin();
11378 for (; itn != oldNodes.end(); ++itn)
11381 if (nodeDomains[oldId].empty())
11383 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11384 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11386 std::map<int, int>::const_iterator itdom = domvol.begin();
11387 for (; itdom != domvol.end(); ++itdom)
11389 int idom = itdom->first;
11390 //MESSAGE(" domain " << idom);
11391 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11393 if (nodeDomains[oldId].size() >= 2) // a multiple node
11395 vector<int> orderedDoms;
11396 //MESSAGE("multiple node " << oldId);
11397 if (mutipleNodes.count(oldId))
11398 orderedDoms = mutipleNodes[oldId];
11401 map<int,int>::iterator it = nodeDomains[oldId].begin();
11402 for (; it != nodeDomains[oldId].end(); ++it)
11403 orderedDoms.push_back(it->first);
11405 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11406 //stringstream txt;
11407 //for (int i=0; i<orderedDoms.size(); i++)
11408 // txt << orderedDoms[i] << " ";
11409 //MESSAGE("orderedDoms " << txt.str());
11410 mutipleNodes[oldId] = orderedDoms;
11412 double *coords = grid->GetPoint(oldId);
11413 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11414 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11415 int newId = newNode->getVtkId();
11416 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11417 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11424 MESSAGE(".. Creation of elements");
11425 for (int idomain = idom0; idomain < nbDomains; idomain++)
11427 itface = faceDomains.begin();
11428 for (; itface != faceDomains.end(); ++itface)
11430 std::map<int, int> domvol = itface->second;
11431 if (!domvol.count(idomain))
11433 DownIdType face = itface->first;
11434 //MESSAGE(" --- face " << face.cellId);
11435 std::set<int> oldNodes;
11437 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11438 int nbMultipleNodes = 0;
11439 std::set<int>::iterator itn = oldNodes.begin();
11440 for (; itn != oldNodes.end(); ++itn)
11443 if (mutipleNodes.count(oldId))
11446 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11448 //MESSAGE("multiple Nodes detected on a shared face");
11449 int downId = itface->first.cellId;
11450 unsigned char cellType = itface->first.cellType;
11451 // --- shared edge or shared face ?
11452 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11455 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11456 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11457 if (mutipleNodes.count(nodes[i]))
11458 if (!mutipleNodesToFace.count(nodes[i]))
11459 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11461 else // shared face (between two volumes)
11463 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11464 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11465 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11466 for (int ie =0; ie < nbEdges; ie++)
11469 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11470 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11472 vector<int> vn0 = mutipleNodes[nodes[0]];
11473 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11475 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11476 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11477 if ( vn0[i0] == vn1[i1] )
11478 doms.push_back( vn0[ i0 ]);
11479 if ( doms.size() > 2 )
11481 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11482 double *coords = grid->GetPoint(nodes[0]);
11483 gp_Pnt p0(coords[0], coords[1], coords[2]);
11484 coords = grid->GetPoint(nodes[nbNodes - 1]);
11485 gp_Pnt p1(coords[0], coords[1], coords[2]);
11487 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11488 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11489 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11490 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11491 for ( size_t id = 0; id < doms.size(); id++ )
11493 int idom = doms[id];
11494 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11495 for ( int ivol = 0; ivol < nbvol; ivol++ )
11497 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11498 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11499 if (domain.count(elem))
11501 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11502 domvol[idom] = svol;
11503 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11505 vtkIdType npts = 0;
11506 vtkIdType* pts = 0;
11507 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11508 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11511 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11512 angleDom[idom] = 0;
11516 gp_Pnt g(values[0], values[1], values[2]);
11517 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11518 //MESSAGE(" angle=" << angleDom[idom]);
11524 map<double, int> sortedDom; // sort domains by angle
11525 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11526 sortedDom[ia->second] = ia->first;
11527 vector<int> vnodes;
11529 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11531 vdom.push_back(ib->second);
11532 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11534 for (int ino = 0; ino < nbNodes; ino++)
11535 vnodes.push_back(nodes[ino]);
11536 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11545 // --- iterate on shared faces (volumes to modify, face to extrude)
11546 // get node id's of the face (id SMDS = id VTK)
11547 // create flat element with old and new nodes if requested
11549 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11550 // (domain1 X domain2) = domain1 + MAXINT*domain2
11552 std::map<int, std::map<long,int> > nodeQuadDomains;
11553 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11555 MESSAGE(".. Creation of elements: simple junction");
11556 if (createJointElems)
11559 string joints2DName = "joints2D";
11560 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11561 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11562 string joints3DName = "joints3D";
11563 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11564 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11566 itface = faceDomains.begin();
11567 for (; itface != faceDomains.end(); ++itface)
11569 DownIdType face = itface->first;
11570 std::set<int> oldNodes;
11571 std::set<int>::iterator itn;
11573 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11575 std::map<int, int> domvol = itface->second;
11576 std::map<int, int>::iterator itdom = domvol.begin();
11577 int dom1 = itdom->first;
11578 int vtkVolId = itdom->second;
11580 int dom2 = itdom->first;
11581 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11583 stringstream grpname;
11586 grpname << dom1 << "_" << dom2;
11588 grpname << dom2 << "_" << dom1;
11589 string namegrp = grpname.str();
11590 if (!mapOfJunctionGroups.count(namegrp))
11591 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11592 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11594 sgrp->Add(vol->GetID());
11595 if (vol->GetType() == SMDSAbs_Volume)
11596 joints3DGrp->Add(vol->GetID());
11597 else if (vol->GetType() == SMDSAbs_Face)
11598 joints2DGrp->Add(vol->GetID());
11602 // --- create volumes on multiple domain intersection if requested
11603 // iterate on mutipleNodesToFace
11604 // iterate on edgesMultiDomains
11606 MESSAGE(".. Creation of elements: multiple junction");
11607 if (createJointElems)
11609 // --- iterate on mutipleNodesToFace
11611 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11612 for (; itn != mutipleNodesToFace.end(); ++itn)
11614 int node = itn->first;
11615 vector<int> orderDom = itn->second;
11616 vector<vtkIdType> orderedNodes;
11617 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11618 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11619 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11621 stringstream grpname;
11623 grpname << 0 << "_" << 0;
11625 string namegrp = grpname.str();
11626 if (!mapOfJunctionGroups.count(namegrp))
11627 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11628 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11630 sgrp->Add(face->GetID());
11633 // --- iterate on edgesMultiDomains
11635 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11636 for (; ite != edgesMultiDomains.end(); ++ite)
11638 vector<int> nodes = ite->first;
11639 vector<int> orderDom = ite->second;
11640 vector<vtkIdType> orderedNodes;
11641 if (nodes.size() == 2)
11643 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11644 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11645 if ( orderDom.size() == 3 )
11646 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11647 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11649 for (int idom = orderDom.size()-1; idom >=0; idom--)
11650 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11651 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11654 string namegrp = "jointsMultiples";
11655 if (!mapOfJunctionGroups.count(namegrp))
11656 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11657 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11659 sgrp->Add(vol->GetID());
11663 //INFOS("Quadratic multiple joints not implemented");
11664 // TODO quadratic nodes
11669 // --- list the explicit faces and edges of the mesh that need to be modified,
11670 // i.e. faces and edges built with one or more duplicated nodes.
11671 // associate these faces or edges to their corresponding domain.
11672 // only the first domain found is kept when a face or edge is shared
11674 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11675 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11676 faceOrEdgeDom.clear();
11679 MESSAGE(".. Modification of elements");
11680 for (int idomain = idom0; idomain < nbDomains; idomain++)
11682 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11683 for (; itnod != nodeDomains.end(); ++itnod)
11685 int oldId = itnod->first;
11686 //MESSAGE(" node " << oldId);
11687 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11688 for (int i = 0; i < l.ncells; i++)
11690 int vtkId = l.cells[i];
11691 int vtkType = grid->GetCellType(vtkId);
11692 int downId = grid->CellIdToDownId(vtkId);
11694 continue; // new cells: not to be modified
11695 DownIdType aCell(downId, vtkType);
11696 int volParents[1000];
11697 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11698 for (int j = 0; j < nbvol; j++)
11699 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11700 if (!feDom.count(vtkId))
11702 feDom[vtkId] = idomain;
11703 faceOrEdgeDom[aCell] = emptyMap;
11704 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11705 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11706 // << " type " << vtkType << " downId " << downId);
11712 // --- iterate on shared faces (volumes to modify, face to extrude)
11713 // get node id's of the face
11714 // replace old nodes by new nodes in volumes, and update inverse connectivity
11716 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11717 for (int m=0; m<3; m++)
11719 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11720 itface = (*amap).begin();
11721 for (; itface != (*amap).end(); ++itface)
11723 DownIdType face = itface->first;
11724 std::set<int> oldNodes;
11725 std::set<int>::iterator itn;
11727 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11728 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11729 std::map<int, int> localClonedNodeIds;
11731 std::map<int, int> domvol = itface->second;
11732 std::map<int, int>::iterator itdom = domvol.begin();
11733 for (; itdom != domvol.end(); ++itdom)
11735 int idom = itdom->first;
11736 int vtkVolId = itdom->second;
11737 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11738 localClonedNodeIds.clear();
11739 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11742 if (nodeDomains[oldId].count(idom))
11744 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11745 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11748 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11753 // Remove empty groups (issue 0022812)
11754 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11755 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11757 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11758 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11761 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11762 grid->BuildLinks();
11770 * \brief Double nodes on some external faces and create flat elements.
11771 * Flat elements are mainly used by some types of mechanic calculations.
11773 * Each group of the list must be constituted of faces.
11774 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11775 * @param theElems - list of groups of faces, where a group of faces is a set of
11776 * SMDS_MeshElements sorted by Id.
11777 * @return TRUE if operation has been completed successfully, FALSE otherwise
11779 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11781 MESSAGE("-------------------------------------------------");
11782 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11783 MESSAGE("-------------------------------------------------");
11785 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11787 // --- For each group of faces
11788 // duplicate the nodes, create a flat element based on the face
11789 // replace the nodes of the faces by their clones
11791 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11792 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11793 clonedNodes.clear();
11794 intermediateNodes.clear();
11795 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11796 mapOfJunctionGroups.clear();
11798 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11800 const TIDSortedElemSet& domain = theElems[idom];
11801 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11802 for ( ; elemItr != domain.end(); ++elemItr )
11804 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11805 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11808 // MESSAGE("aFace=" << aFace->GetID());
11809 bool isQuad = aFace->IsQuadratic();
11810 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11812 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11814 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11815 while (nodeIt->more())
11817 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11818 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11820 ln2.push_back(node);
11822 ln0.push_back(node);
11824 const SMDS_MeshNode* clone = 0;
11825 if (!clonedNodes.count(node))
11827 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11828 copyPosition( node, clone );
11829 clonedNodes[node] = clone;
11832 clone = clonedNodes[node];
11835 ln3.push_back(clone);
11837 ln1.push_back(clone);
11839 const SMDS_MeshNode* inter = 0;
11840 if (isQuad && (!isMedium))
11842 if (!intermediateNodes.count(node))
11844 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11845 copyPosition( node, inter );
11846 intermediateNodes[node] = inter;
11849 inter = intermediateNodes[node];
11850 ln4.push_back(inter);
11854 // --- extrude the face
11856 vector<const SMDS_MeshNode*> ln;
11857 SMDS_MeshVolume* vol = 0;
11858 vtkIdType aType = aFace->GetVtkType();
11862 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11863 // MESSAGE("vol prism " << vol->GetID());
11864 ln.push_back(ln1[0]);
11865 ln.push_back(ln1[1]);
11866 ln.push_back(ln1[2]);
11869 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11870 // MESSAGE("vol hexa " << vol->GetID());
11871 ln.push_back(ln1[0]);
11872 ln.push_back(ln1[1]);
11873 ln.push_back(ln1[2]);
11874 ln.push_back(ln1[3]);
11876 case VTK_QUADRATIC_TRIANGLE:
11877 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11878 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11879 // MESSAGE("vol quad prism " << vol->GetID());
11880 ln.push_back(ln1[0]);
11881 ln.push_back(ln1[1]);
11882 ln.push_back(ln1[2]);
11883 ln.push_back(ln3[0]);
11884 ln.push_back(ln3[1]);
11885 ln.push_back(ln3[2]);
11887 case VTK_QUADRATIC_QUAD:
11888 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11889 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11890 // ln4[0], ln4[1], ln4[2], ln4[3]);
11891 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11892 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11893 ln4[0], ln4[1], ln4[2], ln4[3]);
11894 // MESSAGE("vol quad hexa " << vol->GetID());
11895 ln.push_back(ln1[0]);
11896 ln.push_back(ln1[1]);
11897 ln.push_back(ln1[2]);
11898 ln.push_back(ln1[3]);
11899 ln.push_back(ln3[0]);
11900 ln.push_back(ln3[1]);
11901 ln.push_back(ln3[2]);
11902 ln.push_back(ln3[3]);
11912 stringstream grpname;
11916 string namegrp = grpname.str();
11917 if (!mapOfJunctionGroups.count(namegrp))
11918 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11919 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11921 sgrp->Add(vol->GetID());
11924 // --- modify the face
11926 aFace->ChangeNodes(&ln[0], ln.size());
11933 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11934 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11935 * groups of faces to remove inside the object, (idem edges).
11936 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11938 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11939 const TopoDS_Shape& theShape,
11940 SMESH_NodeSearcher* theNodeSearcher,
11941 const char* groupName,
11942 std::vector<double>& nodesCoords,
11943 std::vector<std::vector<int> >& listOfListOfNodes)
11945 MESSAGE("--------------------------------");
11946 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11947 MESSAGE("--------------------------------");
11949 // --- zone of volumes to remove is given :
11950 // 1 either by a geom shape (one or more vertices) and a radius,
11951 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11952 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11953 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11954 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11955 // defined by it's name.
11957 SMESHDS_GroupBase* groupDS = 0;
11958 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11959 while ( groupIt->more() )
11962 SMESH_Group * group = groupIt->next();
11963 if ( !group ) continue;
11964 groupDS = group->GetGroupDS();
11965 if ( !groupDS || groupDS->IsEmpty() ) continue;
11966 std::string grpName = group->GetName();
11967 //MESSAGE("grpName=" << grpName);
11968 if (grpName == groupName)
11974 bool isNodeGroup = false;
11975 bool isNodeCoords = false;
11978 if (groupDS->GetType() != SMDSAbs_Node)
11980 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11983 if (nodesCoords.size() > 0)
11984 isNodeCoords = true; // a list o nodes given by their coordinates
11985 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11987 // --- define groups to build
11989 int idg; // --- group of SMDS volumes
11990 string grpvName = groupName;
11991 grpvName += "_vol";
11992 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11995 MESSAGE("group not created " << grpvName);
11998 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12000 int idgs; // --- group of SMDS faces on the skin
12001 string grpsName = groupName;
12002 grpsName += "_skin";
12003 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12006 MESSAGE("group not created " << grpsName);
12009 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12011 int idgi; // --- group of SMDS faces internal (several shapes)
12012 string grpiName = groupName;
12013 grpiName += "_internalFaces";
12014 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12017 MESSAGE("group not created " << grpiName);
12020 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12022 int idgei; // --- group of SMDS faces internal (several shapes)
12023 string grpeiName = groupName;
12024 grpeiName += "_internalEdges";
12025 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12028 MESSAGE("group not created " << grpeiName);
12031 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12033 // --- build downward connectivity
12035 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12036 meshDS->BuildDownWardConnectivity(true);
12037 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12039 // --- set of volumes detected inside
12041 std::set<int> setOfInsideVol;
12042 std::set<int> setOfVolToCheck;
12044 std::vector<gp_Pnt> gpnts;
12047 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12049 MESSAGE("group of nodes provided");
12050 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12051 while ( elemIt->more() )
12053 const SMDS_MeshElement* elem = elemIt->next();
12056 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12059 SMDS_MeshElement* vol = 0;
12060 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12061 while (volItr->more())
12063 vol = (SMDS_MeshElement*)volItr->next();
12064 setOfInsideVol.insert(vol->getVtkId());
12065 sgrp->Add(vol->GetID());
12069 else if (isNodeCoords)
12071 MESSAGE("list of nodes coordinates provided");
12074 while ( i < nodesCoords.size()-2 )
12076 double x = nodesCoords[i++];
12077 double y = nodesCoords[i++];
12078 double z = nodesCoords[i++];
12079 gp_Pnt p = gp_Pnt(x, y ,z);
12080 gpnts.push_back(p);
12081 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12085 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12087 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12088 TopTools_IndexedMapOfShape vertexMap;
12089 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12090 gp_Pnt p = gp_Pnt(0,0,0);
12091 if (vertexMap.Extent() < 1)
12094 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12096 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12097 p = BRep_Tool::Pnt(vertex);
12098 gpnts.push_back(p);
12099 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12103 if (gpnts.size() > 0)
12106 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12108 nodeId = startNode->GetID();
12109 MESSAGE("nodeId " << nodeId);
12111 double radius2 = radius*radius;
12112 MESSAGE("radius2 " << radius2);
12114 // --- volumes on start node
12116 setOfVolToCheck.clear();
12117 SMDS_MeshElement* startVol = 0;
12118 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12119 while (volItr->more())
12121 startVol = (SMDS_MeshElement*)volItr->next();
12122 setOfVolToCheck.insert(startVol->getVtkId());
12124 if (setOfVolToCheck.empty())
12126 MESSAGE("No volumes found");
12130 // --- starting with central volumes then their neighbors, check if they are inside
12131 // or outside the domain, until no more new neighbor volume is inside.
12132 // Fill the group of inside volumes
12134 std::map<int, double> mapOfNodeDistance2;
12135 mapOfNodeDistance2.clear();
12136 std::set<int> setOfOutsideVol;
12137 while (!setOfVolToCheck.empty())
12139 std::set<int>::iterator it = setOfVolToCheck.begin();
12141 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12142 bool volInside = false;
12143 vtkIdType npts = 0;
12144 vtkIdType* pts = 0;
12145 grid->GetCellPoints(vtkId, npts, pts);
12146 for (int i=0; i<npts; i++)
12148 double distance2 = 0;
12149 if (mapOfNodeDistance2.count(pts[i]))
12151 distance2 = mapOfNodeDistance2[pts[i]];
12152 MESSAGE("point " << pts[i] << " distance2 " << distance2);
12156 double *coords = grid->GetPoint(pts[i]);
12157 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12159 for ( size_t j = 0; j < gpnts.size(); j++ )
12161 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12162 if (d2 < distance2)
12165 if (distance2 < radius2)
12169 mapOfNodeDistance2[pts[i]] = distance2;
12170 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12172 if (distance2 < radius2)
12174 volInside = true; // one or more nodes inside the domain
12175 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12181 setOfInsideVol.insert(vtkId);
12182 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12183 int neighborsVtkIds[NBMAXNEIGHBORS];
12184 int downIds[NBMAXNEIGHBORS];
12185 unsigned char downTypes[NBMAXNEIGHBORS];
12186 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12187 for (int n = 0; n < nbNeighbors; n++)
12188 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12189 setOfVolToCheck.insert(neighborsVtkIds[n]);
12193 setOfOutsideVol.insert(vtkId);
12194 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12196 setOfVolToCheck.erase(vtkId);
12200 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12201 // If yes, add the volume to the inside set
12203 bool addedInside = true;
12204 std::set<int> setOfVolToReCheck;
12205 while (addedInside)
12207 MESSAGE(" --------------------------- re check");
12208 addedInside = false;
12209 std::set<int>::iterator itv = setOfInsideVol.begin();
12210 for (; itv != setOfInsideVol.end(); ++itv)
12213 int neighborsVtkIds[NBMAXNEIGHBORS];
12214 int downIds[NBMAXNEIGHBORS];
12215 unsigned char downTypes[NBMAXNEIGHBORS];
12216 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12217 for (int n = 0; n < nbNeighbors; n++)
12218 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12219 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12221 setOfVolToCheck = setOfVolToReCheck;
12222 setOfVolToReCheck.clear();
12223 while (!setOfVolToCheck.empty())
12225 std::set<int>::iterator it = setOfVolToCheck.begin();
12227 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12229 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12230 int countInside = 0;
12231 int neighborsVtkIds[NBMAXNEIGHBORS];
12232 int downIds[NBMAXNEIGHBORS];
12233 unsigned char downTypes[NBMAXNEIGHBORS];
12234 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12235 for (int n = 0; n < nbNeighbors; n++)
12236 if (setOfInsideVol.count(neighborsVtkIds[n]))
12238 MESSAGE("countInside " << countInside);
12239 if (countInside > 1)
12241 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12242 setOfInsideVol.insert(vtkId);
12243 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12244 addedInside = true;
12247 setOfVolToReCheck.insert(vtkId);
12249 setOfVolToCheck.erase(vtkId);
12253 // --- map of Downward faces at the boundary, inside the global volume
12254 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12255 // fill group of SMDS faces inside the volume (when several volume shapes)
12256 // fill group of SMDS faces on the skin of the global volume (if skin)
12258 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12259 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12260 std::set<int>::iterator it = setOfInsideVol.begin();
12261 for (; it != setOfInsideVol.end(); ++it)
12264 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12265 int neighborsVtkIds[NBMAXNEIGHBORS];
12266 int downIds[NBMAXNEIGHBORS];
12267 unsigned char downTypes[NBMAXNEIGHBORS];
12268 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12269 for (int n = 0; n < nbNeighbors; n++)
12271 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12272 if (neighborDim == 3)
12274 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12276 DownIdType face(downIds[n], downTypes[n]);
12277 boundaryFaces[face] = vtkId;
12279 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12280 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12281 if (vtkFaceId >= 0)
12283 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12284 // find also the smds edges on this face
12285 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12286 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12287 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12288 for (int i = 0; i < nbEdges; i++)
12290 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12291 if (vtkEdgeId >= 0)
12292 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12296 else if (neighborDim == 2) // skin of the volume
12298 DownIdType face(downIds[n], downTypes[n]);
12299 skinFaces[face] = vtkId;
12300 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12301 if (vtkFaceId >= 0)
12302 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12307 // --- identify the edges constituting the wire of each subshape on the skin
12308 // define polylines with the nodes of edges, equivalent to wires
12309 // project polylines on subshapes, and partition, to get geom faces
12311 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12312 std::set<int> emptySet;
12314 std::set<int> shapeIds;
12316 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12317 while (itelem->more())
12319 const SMDS_MeshElement *elem = itelem->next();
12320 int shapeId = elem->getshapeId();
12321 int vtkId = elem->getVtkId();
12322 if (!shapeIdToVtkIdSet.count(shapeId))
12324 shapeIdToVtkIdSet[shapeId] = emptySet;
12325 shapeIds.insert(shapeId);
12327 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12330 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12331 std::set<DownIdType, DownIdCompare> emptyEdges;
12332 emptyEdges.clear();
12334 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12335 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12337 int shapeId = itShape->first;
12338 MESSAGE(" --- Shape ID --- "<< shapeId);
12339 shapeIdToEdges[shapeId] = emptyEdges;
12341 std::vector<int> nodesEdges;
12343 std::set<int>::iterator its = itShape->second.begin();
12344 for (; its != itShape->second.end(); ++its)
12347 MESSAGE(" " << vtkId);
12348 int neighborsVtkIds[NBMAXNEIGHBORS];
12349 int downIds[NBMAXNEIGHBORS];
12350 unsigned char downTypes[NBMAXNEIGHBORS];
12351 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12352 for (int n = 0; n < nbNeighbors; n++)
12354 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12356 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12357 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12358 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12360 DownIdType edge(downIds[n], downTypes[n]);
12361 if (!shapeIdToEdges[shapeId].count(edge))
12363 shapeIdToEdges[shapeId].insert(edge);
12365 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12366 nodesEdges.push_back(vtkNodeId[0]);
12367 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12368 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12374 std::list<int> order;
12376 if (nodesEdges.size() > 0)
12378 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12379 nodesEdges[0] = -1;
12380 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12381 nodesEdges[1] = -1; // do not reuse this edge
12385 int nodeTofind = order.back(); // try first to push back
12387 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12388 if (nodesEdges[i] == nodeTofind)
12390 if ( i == (int) nodesEdges.size() )
12391 found = false; // no follower found on back
12394 if (i%2) // odd ==> use the previous one
12395 if (nodesEdges[i-1] < 0)
12399 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12400 nodesEdges[i-1] = -1;
12402 else // even ==> use the next one
12403 if (nodesEdges[i+1] < 0)
12407 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12408 nodesEdges[i+1] = -1;
12413 // try to push front
12415 nodeTofind = order.front(); // try to push front
12416 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12417 if ( nodesEdges[i] == nodeTofind )
12419 if ( i == (int)nodesEdges.size() )
12421 found = false; // no predecessor found on front
12424 if (i%2) // odd ==> use the previous one
12425 if (nodesEdges[i-1] < 0)
12429 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12430 nodesEdges[i-1] = -1;
12432 else // even ==> use the next one
12433 if (nodesEdges[i+1] < 0)
12437 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12438 nodesEdges[i+1] = -1;
12444 std::vector<int> nodes;
12445 nodes.push_back(shapeId);
12446 std::list<int>::iterator itl = order.begin();
12447 for (; itl != order.end(); itl++)
12449 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12450 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12452 listOfListOfNodes.push_back(nodes);
12455 // partition geom faces with blocFissure
12456 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12457 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12463 //================================================================================
12465 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12466 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12467 * \return TRUE if operation has been completed successfully, FALSE otherwise
12469 //================================================================================
12471 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12473 // iterates on volume elements and detect all free faces on them
12474 SMESHDS_Mesh* aMesh = GetMeshDS();
12478 ElemFeatures faceType( SMDSAbs_Face );
12479 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12480 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12483 const SMDS_MeshVolume* volume = vIt->next();
12484 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12485 vTool.SetExternalNormal();
12486 const int iQuad = volume->IsQuadratic();
12487 faceType.SetQuad( iQuad );
12488 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12490 if (!vTool.IsFreeFace(iface))
12493 vector<const SMDS_MeshNode *> nodes;
12494 int nbFaceNodes = vTool.NbFaceNodes(iface);
12495 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12497 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12498 nodes.push_back(faceNodes[inode]);
12500 if (iQuad) // add medium nodes
12502 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12503 nodes.push_back(faceNodes[inode]);
12504 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12505 nodes.push_back(faceNodes[8]);
12507 // add new face based on volume nodes
12508 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12510 nbExisted++; // face already exsist
12514 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12519 return ( nbFree == ( nbExisted + nbCreated ));
12524 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12526 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12528 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12531 //================================================================================
12533 * \brief Creates missing boundary elements
12534 * \param elements - elements whose boundary is to be checked
12535 * \param dimension - defines type of boundary elements to create
12536 * \param group - a group to store created boundary elements in
12537 * \param targetMesh - a mesh to store created boundary elements in
12538 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12539 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12540 * boundary elements will be copied into the targetMesh
12541 * \param toAddExistingBondary - if true, not only new but also pre-existing
12542 * boundary elements will be added into the new group
12543 * \param aroundElements - if true, elements will be created on boundary of given
12544 * elements else, on boundary of the whole mesh.
12545 * \return nb of added boundary elements
12547 //================================================================================
12549 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12550 Bnd_Dimension dimension,
12551 SMESH_Group* group/*=0*/,
12552 SMESH_Mesh* targetMesh/*=0*/,
12553 bool toCopyElements/*=false*/,
12554 bool toCopyExistingBoundary/*=false*/,
12555 bool toAddExistingBondary/*= false*/,
12556 bool aroundElements/*= false*/)
12558 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12559 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12560 // hope that all elements are of the same type, do not check them all
12561 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12562 throw SALOME_Exception(LOCALIZED("wrong element type"));
12565 toCopyElements = toCopyExistingBoundary = false;
12567 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12568 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12569 int nbAddedBnd = 0;
12571 // editor adding present bnd elements and optionally holding elements to add to the group
12572 SMESH_MeshEditor* presentEditor;
12573 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12574 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12576 SMESH_MesherHelper helper( *myMesh );
12577 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12578 SMDS_VolumeTool vTool;
12579 TIDSortedElemSet avoidSet;
12580 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12583 typedef vector<const SMDS_MeshNode*> TConnectivity;
12584 TConnectivity tgtNodes;
12585 ElemFeatures elemKind( missType ), elemToCopy;
12587 vector<const SMDS_MeshElement*> presentBndElems;
12588 vector<TConnectivity> missingBndElems;
12589 vector<int> freeFacets;
12590 TConnectivity nodes, elemNodes;
12592 SMDS_ElemIteratorPtr eIt;
12593 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12594 else eIt = elemSetIterator( elements );
12596 while (eIt->more())
12598 const SMDS_MeshElement* elem = eIt->next();
12599 const int iQuad = elem->IsQuadratic();
12600 elemKind.SetQuad( iQuad );
12602 // ------------------------------------------------------------------------------------
12603 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12604 // ------------------------------------------------------------------------------------
12605 presentBndElems.clear();
12606 missingBndElems.clear();
12607 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12608 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12610 const SMDS_MeshElement* otherVol = 0;
12611 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12613 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12614 ( !aroundElements || elements.count( otherVol )))
12616 freeFacets.push_back( iface );
12618 if ( missType == SMDSAbs_Face )
12619 vTool.SetExternalNormal();
12620 for ( size_t i = 0; i < freeFacets.size(); ++i )
12622 int iface = freeFacets[i];
12623 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12624 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12625 if ( missType == SMDSAbs_Edge ) // boundary edges
12627 nodes.resize( 2+iQuad );
12628 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12630 for ( size_t j = 0; j < nodes.size(); ++j )
12631 nodes[ j ] = nn[ i+j ];
12632 if ( const SMDS_MeshElement* edge =
12633 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12634 presentBndElems.push_back( edge );
12636 missingBndElems.push_back( nodes );
12639 else // boundary face
12642 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12643 nodes.push_back( nn[inode] ); // add corner nodes
12645 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12646 nodes.push_back( nn[inode] ); // add medium nodes
12647 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12649 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12651 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12652 SMDSAbs_Face, /*noMedium=*/false ))
12653 presentBndElems.push_back( f );
12655 missingBndElems.push_back( nodes );
12657 if ( targetMesh != myMesh )
12659 // add 1D elements on face boundary to be added to a new mesh
12660 const SMDS_MeshElement* edge;
12661 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12664 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12666 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12667 if ( edge && avoidSet.insert( edge ).second )
12668 presentBndElems.push_back( edge );
12674 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12676 avoidSet.clear(), avoidSet.insert( elem );
12677 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12678 SMDS_MeshElement::iterator() );
12679 elemNodes.push_back( elemNodes[0] );
12680 nodes.resize( 2 + iQuad );
12681 const int nbLinks = elem->NbCornerNodes();
12682 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12684 nodes[0] = elemNodes[iN];
12685 nodes[1] = elemNodes[iN+1+iQuad];
12686 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12687 continue; // not free link
12689 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12690 if ( const SMDS_MeshElement* edge =
12691 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12692 presentBndElems.push_back( edge );
12694 missingBndElems.push_back( nodes );
12698 // ---------------------------------
12699 // 2. Add missing boundary elements
12700 // ---------------------------------
12701 if ( targetMesh != myMesh )
12702 // instead of making a map of nodes in this mesh and targetMesh,
12703 // we create nodes with same IDs.
12704 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12706 TConnectivity& srcNodes = missingBndElems[i];
12707 tgtNodes.resize( srcNodes.size() );
12708 for ( inode = 0; inode < srcNodes.size(); ++inode )
12709 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12710 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12712 /*noMedium=*/false))
12714 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12718 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12720 TConnectivity& nodes = missingBndElems[ i ];
12721 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12723 /*noMedium=*/false))
12725 SMDS_MeshElement* newElem =
12726 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12727 nbAddedBnd += bool( newElem );
12729 // try to set a new element to a shape
12730 if ( myMesh->HasShapeToMesh() )
12733 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12734 const size_t nbN = nodes.size() / (iQuad+1 );
12735 for ( inode = 0; inode < nbN && ok; ++inode )
12737 pair<int, TopAbs_ShapeEnum> i_stype =
12738 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12739 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12740 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12742 if ( ok && mediumShapes.size() > 1 )
12744 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12745 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12746 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12748 if (( ok = ( stype_i->first != stype_i_0.first )))
12749 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12750 aMesh->IndexToShape( stype_i_0.second ));
12753 if ( ok && mediumShapes.begin()->first == missShapeType )
12754 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12758 // ----------------------------------
12759 // 3. Copy present boundary elements
12760 // ----------------------------------
12761 if ( toCopyExistingBoundary )
12762 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12764 const SMDS_MeshElement* e = presentBndElems[i];
12765 tgtNodes.resize( e->NbNodes() );
12766 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12767 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12768 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12770 else // store present elements to add them to a group
12771 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12773 presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
12776 } // loop on given elements
12778 // ---------------------------------------------
12779 // 4. Fill group with boundary elements
12780 // ---------------------------------------------
12783 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12784 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12785 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12787 tgtEditor.myLastCreatedElems.Clear();
12788 tgtEditor2.myLastCreatedElems.Clear();
12790 // -----------------------
12791 // 5. Copy given elements
12792 // -----------------------
12793 if ( toCopyElements && targetMesh != myMesh )
12795 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12796 else eIt = elemSetIterator( elements );
12797 while (eIt->more())
12799 const SMDS_MeshElement* elem = eIt->next();
12800 tgtNodes.resize( elem->NbNodes() );
12801 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12802 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12803 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12805 tgtEditor.myLastCreatedElems.Clear();
12811 //================================================================================
12813 * \brief Copy node position and set \a to node on the same geometry
12815 //================================================================================
12817 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12818 const SMDS_MeshNode* to )
12820 if ( !from || !to ) return;
12822 SMDS_PositionPtr pos = from->GetPosition();
12823 if ( !pos || from->getshapeId() < 1 ) return;
12825 switch ( pos->GetTypeOfPosition() )
12827 case SMDS_TOP_3DSPACE: break;
12829 case SMDS_TOP_FACE:
12831 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12832 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12833 fPos->GetUParameter(), fPos->GetVParameter() );
12836 case SMDS_TOP_EDGE:
12838 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12839 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12840 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12843 case SMDS_TOP_VERTEX:
12845 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12848 case SMDS_TOP_UNSPEC: