1 // Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MeshAlgos.hxx"
46 #include "SMESH_MesherHelper.hxx"
47 #include "SMESH_OctreeNode.hxx"
48 #include "SMESH_subMesh.hxx"
50 #include <Basics_OCCTVersion.hxx>
52 #include "utilities.h"
55 #include <BRepAdaptor_Surface.hxx>
56 #include <BRepBuilderAPI_MakeEdge.hxx>
57 #include <BRepClass3d_SolidClassifier.hxx>
58 #include <BRep_Tool.hxx>
60 #include <Extrema_GenExtPS.hxx>
61 #include <Extrema_POnCurv.hxx>
62 #include <Extrema_POnSurf.hxx>
63 #include <Geom2d_Curve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Surface.hxx>
67 #include <Precision.hxx>
68 #include <TColStd_ListOfInteger.hxx>
69 #include <TopAbs_State.hxx>
71 #include <TopExp_Explorer.hxx>
72 #include <TopTools_ListIteratorOfListOfShape.hxx>
73 #include <TopTools_ListOfShape.hxx>
74 #include <TopTools_SequenceOfShape.hxx>
76 #include <TopoDS_Face.hxx>
77 #include <TopoDS_Solid.hxx>
83 #include <gp_Trsf.hxx>
97 #include <boost/tuple/tuple.hpp>
99 #include <Standard_Failure.hxx>
100 #include <Standard_ErrorHandler.hxx>
102 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
105 using namespace SMESH::Controls;
109 template < class ELEM_SET >
110 SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
112 typedef SMDS_SetIterator
113 < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
114 return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
118 //=======================================================================
119 //function : SMESH_MeshEditor
121 //=======================================================================
123 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
124 :myMesh( theMesh ) // theMesh may be NULL
128 //================================================================================
130 * \brief Clears myLastCreatedNodes and myLastCreatedElems
132 //================================================================================
134 void SMESH_MeshEditor::ClearLastCreated()
136 myLastCreatedNodes.Clear();
137 myLastCreatedElems.Clear();
140 //================================================================================
142 * \brief Initializes members by an existing element
143 * \param [in] elem - the source element
144 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
146 //================================================================================
148 SMESH_MeshEditor::ElemFeatures&
149 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
153 myType = elem->GetType();
154 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
156 myIsPoly = elem->IsPoly();
159 myIsQuad = elem->IsQuadratic();
160 if ( myType == SMDSAbs_Volume && !basicOnly )
162 vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
163 myPolyhedQuantities.swap( quant );
167 else if ( myType == SMDSAbs_Ball && !basicOnly )
169 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
175 //=======================================================================
179 //=======================================================================
182 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
183 const ElemFeatures& features)
185 SMDS_MeshElement* e = 0;
186 int nbnode = node.size();
187 SMESHDS_Mesh* mesh = GetMeshDS();
188 const int ID = features.myID;
190 switch ( features.myType ) {
192 if ( !features.myIsPoly ) {
194 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
195 else e = mesh->AddFace (node[0], node[1], node[2] );
197 else if (nbnode == 4) {
198 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
199 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
201 else if (nbnode == 6) {
202 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
203 node[4], node[5], ID);
204 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
207 else if (nbnode == 7) {
208 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
209 node[4], node[5], node[6], ID);
210 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
211 node[4], node[5], node[6] );
213 else if (nbnode == 8) {
214 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
215 node[4], node[5], node[6], node[7], ID);
216 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
217 node[4], node[5], node[6], node[7] );
219 else if (nbnode == 9) {
220 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
221 node[4], node[5], node[6], node[7], node[8], ID);
222 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
223 node[4], node[5], node[6], node[7], node[8] );
226 else if ( !features.myIsQuad )
228 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
229 else e = mesh->AddPolygonalFace (node );
231 else if ( nbnode % 2 == 0 ) // just a protection
233 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
234 else e = mesh->AddQuadPolygonalFace (node );
239 if ( !features.myIsPoly ) {
241 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
242 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
244 else if (nbnode == 5) {
245 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
247 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
250 else if (nbnode == 6) {
251 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
252 node[4], node[5], ID);
253 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
256 else if (nbnode == 8) {
257 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
258 node[4], node[5], node[6], node[7], ID);
259 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
260 node[4], node[5], node[6], node[7] );
262 else if (nbnode == 10) {
263 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
264 node[4], node[5], node[6], node[7],
265 node[8], node[9], ID);
266 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
267 node[4], node[5], node[6], node[7],
270 else if (nbnode == 12) {
271 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
272 node[4], node[5], node[6], node[7],
273 node[8], node[9], node[10], node[11], ID);
274 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
275 node[4], node[5], node[6], node[7],
276 node[8], node[9], node[10], node[11] );
278 else if (nbnode == 13) {
279 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
280 node[4], node[5], node[6], node[7],
281 node[8], node[9], node[10],node[11],
283 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
284 node[4], node[5], node[6], node[7],
285 node[8], node[9], node[10],node[11],
288 else if (nbnode == 15) {
289 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
290 node[4], node[5], node[6], node[7],
291 node[8], node[9], node[10],node[11],
292 node[12],node[13],node[14],ID);
293 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
294 node[4], node[5], node[6], node[7],
295 node[8], node[9], node[10],node[11],
296 node[12],node[13],node[14] );
298 else if (nbnode == 20) {
299 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
300 node[4], node[5], node[6], node[7],
301 node[8], node[9], node[10],node[11],
302 node[12],node[13],node[14],node[15],
303 node[16],node[17],node[18],node[19],ID);
304 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
305 node[4], node[5], node[6], node[7],
306 node[8], node[9], node[10],node[11],
307 node[12],node[13],node[14],node[15],
308 node[16],node[17],node[18],node[19] );
310 else if (nbnode == 27) {
311 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
312 node[4], node[5], node[6], node[7],
313 node[8], node[9], node[10],node[11],
314 node[12],node[13],node[14],node[15],
315 node[16],node[17],node[18],node[19],
316 node[20],node[21],node[22],node[23],
317 node[24],node[25],node[26], ID);
318 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
319 node[4], node[5], node[6], node[7],
320 node[8], node[9], node[10],node[11],
321 node[12],node[13],node[14],node[15],
322 node[16],node[17],node[18],node[19],
323 node[20],node[21],node[22],node[23],
324 node[24],node[25],node[26] );
327 else if ( !features.myIsQuad )
329 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
330 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
334 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
335 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
341 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
342 else e = mesh->AddEdge (node[0], node[1] );
344 else if ( nbnode == 3 ) {
345 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
346 else e = mesh->AddEdge (node[0], node[1], node[2] );
350 case SMDSAbs_0DElement:
352 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
353 else e = mesh->Add0DElement (node[0] );
358 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
359 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
363 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
364 else e = mesh->AddBall (node[0], features.myBallDiameter );
369 if ( e ) myLastCreatedElems.Append( e );
373 //=======================================================================
377 //=======================================================================
379 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
380 const ElemFeatures& features)
382 vector<const SMDS_MeshNode*> nodes;
383 nodes.reserve( nodeIDs.size() );
384 vector<int>::const_iterator id = nodeIDs.begin();
385 while ( id != nodeIDs.end() ) {
386 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
387 nodes.push_back( node );
391 return AddElement( nodes, features );
394 //=======================================================================
396 //purpose : Remove a node or an element.
397 // Modify a compute state of sub-meshes which become empty
398 //=======================================================================
400 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
403 myLastCreatedElems.Clear();
404 myLastCreatedNodes.Clear();
406 SMESHDS_Mesh* aMesh = GetMeshDS();
407 set< SMESH_subMesh *> smmap;
410 list<int>::const_iterator it = theIDs.begin();
411 for ( ; it != theIDs.end(); it++ ) {
412 const SMDS_MeshElement * elem;
414 elem = aMesh->FindNode( *it );
416 elem = aMesh->FindElement( *it );
420 // Notify VERTEX sub-meshes about modification
422 const SMDS_MeshNode* node = cast2Node( elem );
423 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
424 if ( int aShapeID = node->getshapeId() )
425 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
428 // Find sub-meshes to notify about modification
429 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
430 // while ( nodeIt->more() ) {
431 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
432 // const SMDS_PositionPtr& aPosition = node->GetPosition();
433 // if ( aPosition.get() ) {
434 // if ( int aShapeID = aPosition->GetShapeId() ) {
435 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
436 // smmap.insert( sm );
443 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
445 aMesh->RemoveElement( elem );
449 // Notify sub-meshes about modification
450 if ( !smmap.empty() ) {
451 set< SMESH_subMesh *>::iterator smIt;
452 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
453 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
456 // // Check if the whole mesh becomes empty
457 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
458 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
463 //================================================================================
465 * \brief Create 0D elements on all nodes of the given object except those
466 * nodes on which a 0D element already exists.
467 * \param elements - Elements on whose nodes to create 0D elements; if empty,
468 * the all mesh is treated
469 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
471 //================================================================================
473 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
474 TIDSortedElemSet& all0DElems )
476 SMDS_ElemIteratorPtr elemIt;
477 vector< const SMDS_MeshElement* > allNodes;
478 if ( elements.empty() )
480 allNodes.reserve( GetMeshDS()->NbNodes() );
481 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
482 while ( elemIt->more() )
483 allNodes.push_back( elemIt->next() );
485 elemIt = elemSetIterator( allNodes );
489 elemIt = elemSetIterator( elements );
492 while ( elemIt->more() )
494 const SMDS_MeshElement* e = elemIt->next();
495 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
496 while ( nodeIt->more() )
498 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
499 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
501 all0DElems.insert( it0D->next() );
503 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
504 all0DElems.insert( myLastCreatedElems.Last() );
510 //=======================================================================
511 //function : FindShape
512 //purpose : Return an index of the shape theElem is on
513 // or zero if a shape not found
514 //=======================================================================
516 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
518 myLastCreatedElems.Clear();
519 myLastCreatedNodes.Clear();
521 SMESHDS_Mesh * aMesh = GetMeshDS();
522 if ( aMesh->ShapeToMesh().IsNull() )
525 int aShapeID = theElem->getshapeId();
529 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
530 if ( sm->Contains( theElem ))
533 if ( theElem->GetType() == SMDSAbs_Node ) {
534 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
537 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
540 TopoDS_Shape aShape; // the shape a node of theElem is on
541 if ( theElem->GetType() != SMDSAbs_Node )
543 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
544 while ( nodeIt->more() ) {
545 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
546 if ((aShapeID = node->getshapeId()) > 0) {
547 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
548 if ( sm->Contains( theElem ))
550 if ( aShape.IsNull() )
551 aShape = aMesh->IndexToShape( aShapeID );
557 // None of nodes is on a proper shape,
558 // find the shape among ancestors of aShape on which a node is
559 if ( !aShape.IsNull() ) {
560 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
561 for ( ; ancIt.More(); ancIt.Next() ) {
562 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
563 if ( sm && sm->Contains( theElem ))
564 return aMesh->ShapeToIndex( ancIt.Value() );
569 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
570 while ( const SMESHDS_SubMesh* sm = smIt->next() )
571 if ( sm->Contains( theElem ))
578 //=======================================================================
579 //function : IsMedium
581 //=======================================================================
583 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
584 const SMDSAbs_ElementType typeToCheck)
586 bool isMedium = false;
587 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
588 while (it->more() && !isMedium ) {
589 const SMDS_MeshElement* elem = it->next();
590 isMedium = elem->IsMediumNode(node);
595 //=======================================================================
596 //function : shiftNodesQuadTria
597 //purpose : Shift nodes in the array corresponded to quadratic triangle
598 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
599 //=======================================================================
601 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
603 const SMDS_MeshNode* nd1 = aNodes[0];
604 aNodes[0] = aNodes[1];
605 aNodes[1] = aNodes[2];
607 const SMDS_MeshNode* nd2 = aNodes[3];
608 aNodes[3] = aNodes[4];
609 aNodes[4] = aNodes[5];
613 //=======================================================================
614 //function : nbEdgeConnectivity
615 //purpose : return number of the edges connected with the theNode.
616 // if theEdges has connections with the other type of the
617 // elements, return -1
618 //=======================================================================
620 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
622 // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
624 // while(elemIt->more()) {
629 return theNode->NbInverseElements();
632 //=======================================================================
633 //function : getNodesFromTwoTria
635 //=======================================================================
637 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
638 const SMDS_MeshElement * theTria2,
639 vector< const SMDS_MeshNode*>& N1,
640 vector< const SMDS_MeshNode*>& N2)
642 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
643 if ( N1.size() < 6 ) return false;
644 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
645 if ( N2.size() < 6 ) return false;
647 int sames[3] = {-1,-1,-1};
659 if(nbsames!=2) return false;
661 shiftNodesQuadTria(N1);
663 shiftNodesQuadTria(N1);
666 i = sames[0] + sames[1] + sames[2];
668 shiftNodesQuadTria(N2);
670 // now we receive following N1 and N2 (using numeration as in the image below)
671 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
672 // i.e. first nodes from both arrays form a new diagonal
676 //=======================================================================
677 //function : InverseDiag
678 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
679 // but having other common link.
680 // Return False if args are improper
681 //=======================================================================
683 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
684 const SMDS_MeshElement * theTria2 )
686 MESSAGE("InverseDiag");
687 myLastCreatedElems.Clear();
688 myLastCreatedNodes.Clear();
690 if (!theTria1 || !theTria2)
693 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
694 if (!F1) return false;
695 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
696 if (!F2) return false;
697 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
698 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
700 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
701 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
705 // put nodes in array and find out indices of the same ones
706 const SMDS_MeshNode* aNodes [6];
707 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
709 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
710 while ( it->more() ) {
711 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
713 if ( i > 2 ) // theTria2
714 // find same node of theTria1
715 for ( int j = 0; j < 3; j++ )
716 if ( aNodes[ i ] == aNodes[ j ]) {
725 return false; // theTria1 is not a triangle
726 it = theTria2->nodesIterator();
728 if ( i == 6 && it->more() )
729 return false; // theTria2 is not a triangle
732 // find indices of 1,2 and of A,B in theTria1
733 int iA = -1, iB = 0, i1 = 0, i2 = 0;
734 for ( i = 0; i < 6; i++ ) {
735 if ( sameInd [ i ] == -1 ) {
740 if ( iA >= 0) iB = i;
744 // nodes 1 and 2 should not be the same
745 if ( aNodes[ i1 ] == aNodes[ i2 ] )
749 aNodes[ iA ] = aNodes[ i2 ];
751 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
753 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
754 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
758 } // end if(F1 && F2)
760 // check case of quadratic faces
761 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
762 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
764 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
765 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
769 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
770 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
778 vector< const SMDS_MeshNode* > N1;
779 vector< const SMDS_MeshNode* > N2;
780 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
782 // now we receive following N1 and N2 (using numeration as above image)
783 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
784 // i.e. first nodes from both arrays determ new diagonal
786 vector< const SMDS_MeshNode*> N1new( N1.size() );
787 vector< const SMDS_MeshNode*> N2new( N2.size() );
788 N1new.back() = N1.back(); // central node of biquadratic
789 N2new.back() = N2.back();
790 N1new[0] = N1[0]; N2new[0] = N1[0];
791 N1new[1] = N2[0]; N2new[1] = N1[1];
792 N1new[2] = N2[1]; N2new[2] = N2[0];
793 N1new[3] = N1[4]; N2new[3] = N1[3];
794 N1new[4] = N2[3]; N2new[4] = N2[5];
795 N1new[5] = N1[5]; N2new[5] = N1[4];
796 // change nodes in faces
797 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
798 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
800 // move the central node of biquadratic triangle
801 SMESH_MesherHelper helper( *GetMesh() );
802 for ( int is2nd = 0; is2nd < 2; ++is2nd )
804 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
805 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
806 if ( nodes.size() < 7 )
808 helper.SetSubShape( tria->getshapeId() );
809 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
813 xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
814 SMESH_TNodeXYZ( nodes[4] ) +
815 SMESH_TNodeXYZ( nodes[5] )) / 3.;
820 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
821 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
822 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
824 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
825 xyz = S->Value( uv.X(), uv.Y() );
826 xyz.Transform( loc );
827 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
828 nodes[6]->getshapeId() > 0 )
829 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
831 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
836 //=======================================================================
837 //function : findTriangles
838 //purpose : find triangles sharing theNode1-theNode2 link
839 //=======================================================================
841 static bool findTriangles(const SMDS_MeshNode * theNode1,
842 const SMDS_MeshNode * theNode2,
843 const SMDS_MeshElement*& theTria1,
844 const SMDS_MeshElement*& theTria2)
846 if ( !theNode1 || !theNode2 ) return false;
848 theTria1 = theTria2 = 0;
850 set< const SMDS_MeshElement* > emap;
851 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
853 const SMDS_MeshElement* elem = it->next();
854 if ( elem->NbCornerNodes() == 3 )
857 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
859 const SMDS_MeshElement* elem = it->next();
860 if ( emap.count( elem )) {
868 // theTria1 must be element with minimum ID
869 if ( theTria2->GetID() < theTria1->GetID() )
870 std::swap( theTria2, theTria1 );
878 //=======================================================================
879 //function : InverseDiag
880 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
881 // with ones built on the same 4 nodes but having other common link.
882 // Return false if proper faces not found
883 //=======================================================================
885 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
886 const SMDS_MeshNode * theNode2)
888 myLastCreatedElems.Clear();
889 myLastCreatedNodes.Clear();
891 MESSAGE( "::InverseDiag()" );
893 const SMDS_MeshElement *tr1, *tr2;
894 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
897 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
898 if (!F1) return false;
899 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
900 if (!F2) return false;
901 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
902 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
904 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
905 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
909 // put nodes in array
910 // and find indices of 1,2 and of A in tr1 and of B in tr2
911 int i, iA1 = 0, i1 = 0;
912 const SMDS_MeshNode* aNodes1 [3];
913 SMDS_ElemIteratorPtr it;
914 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
915 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
916 if ( aNodes1[ i ] == theNode1 )
917 iA1 = i; // node A in tr1
918 else if ( aNodes1[ i ] != theNode2 )
922 const SMDS_MeshNode* aNodes2 [3];
923 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
924 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
925 if ( aNodes2[ i ] == theNode2 )
926 iB2 = i; // node B in tr2
927 else if ( aNodes2[ i ] != theNode1 )
931 // nodes 1 and 2 should not be the same
932 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
936 aNodes1[ iA1 ] = aNodes2[ i2 ];
938 aNodes2[ iB2 ] = aNodes1[ i1 ];
940 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
941 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
946 // check case of quadratic faces
947 return InverseDiag(tr1,tr2);
950 //=======================================================================
951 //function : getQuadrangleNodes
952 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
953 // fusion of triangles tr1 and tr2 having shared link on
954 // theNode1 and theNode2
955 //=======================================================================
957 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
958 const SMDS_MeshNode * theNode1,
959 const SMDS_MeshNode * theNode2,
960 const SMDS_MeshElement * tr1,
961 const SMDS_MeshElement * tr2 )
963 if( tr1->NbNodes() != tr2->NbNodes() )
965 // find the 4-th node to insert into tr1
966 const SMDS_MeshNode* n4 = 0;
967 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
969 while ( !n4 && i<3 ) {
970 const SMDS_MeshNode * n = cast2Node( it->next() );
972 bool isDiag = ( n == theNode1 || n == theNode2 );
976 // Make an array of nodes to be in a quadrangle
977 int iNode = 0, iFirstDiag = -1;
978 it = tr1->nodesIterator();
981 const SMDS_MeshNode * n = cast2Node( it->next() );
983 bool isDiag = ( n == theNode1 || n == theNode2 );
985 if ( iFirstDiag < 0 )
987 else if ( iNode - iFirstDiag == 1 )
988 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
990 else if ( n == n4 ) {
991 return false; // tr1 and tr2 should not have all the same nodes
993 theQuadNodes[ iNode++ ] = n;
995 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
996 theQuadNodes[ iNode ] = n4;
1001 //=======================================================================
1002 //function : DeleteDiag
1003 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
1004 // with a quadrangle built on the same 4 nodes.
1005 // Return false if proper faces not found
1006 //=======================================================================
1008 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1009 const SMDS_MeshNode * theNode2)
1011 myLastCreatedElems.Clear();
1012 myLastCreatedNodes.Clear();
1014 MESSAGE( "::DeleteDiag()" );
1016 const SMDS_MeshElement *tr1, *tr2;
1017 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1020 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1021 if (!F1) return false;
1022 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1023 if (!F2) return false;
1024 SMESHDS_Mesh * aMesh = GetMeshDS();
1026 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1027 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1029 const SMDS_MeshNode* aNodes [ 4 ];
1030 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1033 const SMDS_MeshElement* newElem = 0;
1034 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1035 myLastCreatedElems.Append(newElem);
1036 AddToSameGroups( newElem, tr1, aMesh );
1037 int aShapeId = tr1->getshapeId();
1040 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1042 aMesh->RemoveElement( tr1 );
1043 aMesh->RemoveElement( tr2 );
1048 // check case of quadratic faces
1049 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1051 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1055 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1056 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1064 vector< const SMDS_MeshNode* > N1;
1065 vector< const SMDS_MeshNode* > N2;
1066 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1068 // now we receive following N1 and N2 (using numeration as above image)
1069 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1070 // i.e. first nodes from both arrays determ new diagonal
1072 const SMDS_MeshNode* aNodes[8];
1082 const SMDS_MeshElement* newElem = 0;
1083 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1084 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1085 myLastCreatedElems.Append(newElem);
1086 AddToSameGroups( newElem, tr1, aMesh );
1087 int aShapeId = tr1->getshapeId();
1090 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1092 aMesh->RemoveElement( tr1 );
1093 aMesh->RemoveElement( tr2 );
1095 // remove middle node (9)
1096 GetMeshDS()->RemoveNode( N1[4] );
1101 //=======================================================================
1102 //function : Reorient
1103 //purpose : Reverse theElement orientation
1104 //=======================================================================
1106 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1108 MESSAGE("Reorient");
1109 myLastCreatedElems.Clear();
1110 myLastCreatedNodes.Clear();
1114 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1115 if ( !it || !it->more() )
1118 const SMDSAbs_ElementType type = theElem->GetType();
1119 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1122 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1123 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1125 const SMDS_VtkVolume* aPolyedre =
1126 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1128 MESSAGE("Warning: bad volumic element");
1131 const int nbFaces = aPolyedre->NbFaces();
1132 vector<const SMDS_MeshNode *> poly_nodes;
1133 vector<int> quantities (nbFaces);
1135 // reverse each face of the polyedre
1136 for (int iface = 1; iface <= nbFaces; iface++) {
1137 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1138 quantities[iface - 1] = nbFaceNodes;
1140 for (inode = nbFaceNodes; inode >= 1; inode--) {
1141 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1142 poly_nodes.push_back(curNode);
1145 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1147 else // other elements
1149 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1150 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1151 if ( interlace.empty() )
1153 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1157 SMDS_MeshCell::applyInterlace( interlace, nodes );
1159 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1164 //================================================================================
1166 * \brief Reorient faces.
1167 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1168 * \param theDirection - desired direction of normal of \a theFace
1169 * \param theFace - one of \a theFaces that sould be oriented according to
1170 * \a theDirection and whose orientation defines orientation of other faces
1171 * \return number of reoriented faces.
1173 //================================================================================
1175 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1176 const gp_Dir& theDirection,
1177 const SMDS_MeshElement * theFace)
1180 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1182 if ( theFaces.empty() )
1184 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1185 while ( fIt->more() )
1186 theFaces.insert( theFaces.end(), fIt->next() );
1189 // orient theFace according to theDirection
1191 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1192 if ( normal * theDirection.XYZ() < 0 )
1193 nbReori += Reorient( theFace );
1195 // Orient other faces
1197 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1198 TIDSortedElemSet avoidSet;
1199 set< SMESH_TLink > checkedLinks;
1200 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1202 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1203 theFaces.erase( theFace );
1204 startFaces.insert( theFace );
1206 int nodeInd1, nodeInd2;
1207 const SMDS_MeshElement* otherFace;
1208 vector< const SMDS_MeshElement* > facesNearLink;
1209 vector< std::pair< int, int > > nodeIndsOfFace;
1211 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1212 while ( !startFaces.empty() )
1214 startFace = startFaces.begin();
1215 theFace = *startFace;
1216 startFaces.erase( startFace );
1217 if ( !visitedFaces.insert( theFace ).second )
1221 avoidSet.insert(theFace);
1223 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1225 const int nbNodes = theFace->NbCornerNodes();
1226 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1228 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1229 linkIt_isNew = checkedLinks.insert( link );
1230 if ( !linkIt_isNew.second )
1232 // link has already been checked and won't be encountered more
1233 // if the group (theFaces) is manifold
1234 //checkedLinks.erase( linkIt_isNew.first );
1238 facesNearLink.clear();
1239 nodeIndsOfFace.clear();
1240 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1242 &nodeInd1, &nodeInd2 )))
1243 if ( otherFace != theFace)
1245 facesNearLink.push_back( otherFace );
1246 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1247 avoidSet.insert( otherFace );
1249 if ( facesNearLink.size() > 1 )
1251 // NON-MANIFOLD mesh shell !
1252 // select a face most co-directed with theFace,
1253 // other faces won't be visited this time
1255 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1256 double proj, maxProj = -1;
1257 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1258 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1259 if (( proj = Abs( NF * NOF )) > maxProj ) {
1261 otherFace = facesNearLink[i];
1262 nodeInd1 = nodeIndsOfFace[i].first;
1263 nodeInd2 = nodeIndsOfFace[i].second;
1266 // not to visit rejected faces
1267 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1268 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1269 visitedFaces.insert( facesNearLink[i] );
1271 else if ( facesNearLink.size() == 1 )
1273 otherFace = facesNearLink[0];
1274 nodeInd1 = nodeIndsOfFace.back().first;
1275 nodeInd2 = nodeIndsOfFace.back().second;
1277 if ( otherFace && otherFace != theFace)
1279 // link must be reverse in otherFace if orientation ot otherFace
1280 // is same as that of theFace
1281 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1283 nbReori += Reorient( otherFace );
1285 startFaces.insert( otherFace );
1288 std::swap( link.first, link.second ); // reverse the link
1294 //================================================================================
1296 * \brief Reorient faces basing on orientation of adjacent volumes.
1297 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1298 * \param theVolumes - reference volumes.
1299 * \param theOutsideNormal - to orient faces to have their normal
1300 * pointing either \a outside or \a inside the adjacent volumes.
1301 * \return number of reoriented faces.
1303 //================================================================================
1305 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1306 TIDSortedElemSet & theVolumes,
1307 const bool theOutsideNormal)
1311 SMDS_ElemIteratorPtr faceIt;
1312 if ( theFaces.empty() )
1313 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1315 faceIt = elemSetIterator( theFaces );
1317 vector< const SMDS_MeshNode* > faceNodes;
1318 TIDSortedElemSet checkedVolumes;
1319 set< const SMDS_MeshNode* > faceNodesSet;
1320 SMDS_VolumeTool volumeTool;
1322 while ( faceIt->more() ) // loop on given faces
1324 const SMDS_MeshElement* face = faceIt->next();
1325 if ( face->GetType() != SMDSAbs_Face )
1328 const int nbCornersNodes = face->NbCornerNodes();
1329 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1331 checkedVolumes.clear();
1332 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1333 while ( vIt->more() )
1335 const SMDS_MeshElement* volume = vIt->next();
1337 if ( !checkedVolumes.insert( volume ).second )
1339 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1342 // is volume adjacent?
1343 bool allNodesCommon = true;
1344 for ( int iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1345 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1346 if ( !allNodesCommon )
1349 // get nodes of a corresponding volume facet
1350 faceNodesSet.clear();
1351 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1352 volumeTool.Set( volume );
1353 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1354 if ( facetID < 0 ) continue;
1355 volumeTool.SetExternalNormal();
1356 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1358 // compare order of faceNodes and facetNodes
1359 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1361 for ( int i = 0; i < 2; ++i )
1363 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1364 for ( int iN = 0; iN < nbCornersNodes; ++iN )
1365 if ( faceNodes[ iN ] == n )
1371 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1372 if ( isOutside != theOutsideNormal )
1373 nbReori += Reorient( face );
1375 } // loop on given faces
1380 //=======================================================================
1381 //function : getBadRate
1383 //=======================================================================
1385 static double getBadRate (const SMDS_MeshElement* theElem,
1386 SMESH::Controls::NumericalFunctorPtr& theCrit)
1388 SMESH::Controls::TSequenceOfXYZ P;
1389 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1391 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1392 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1395 //=======================================================================
1396 //function : QuadToTri
1397 //purpose : Cut quadrangles into triangles.
1398 // theCrit is used to select a diagonal to cut
1399 //=======================================================================
1401 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1402 SMESH::Controls::NumericalFunctorPtr theCrit)
1404 myLastCreatedElems.Clear();
1405 myLastCreatedNodes.Clear();
1407 if ( !theCrit.get() )
1410 SMESHDS_Mesh * aMesh = GetMeshDS();
1412 Handle(Geom_Surface) surface;
1413 SMESH_MesherHelper helper( *GetMesh() );
1415 TIDSortedElemSet::iterator itElem;
1416 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1418 const SMDS_MeshElement* elem = *itElem;
1419 if ( !elem || elem->GetType() != SMDSAbs_Face )
1421 if ( elem->NbCornerNodes() != 4 )
1424 // retrieve element nodes
1425 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1427 // compare two sets of possible triangles
1428 double aBadRate1, aBadRate2; // to what extent a set is bad
1429 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1430 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1431 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1433 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1434 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1435 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1437 const int aShapeId = FindShape( elem );
1438 const SMDS_MeshElement* newElem1 = 0;
1439 const SMDS_MeshElement* newElem2 = 0;
1441 if ( !elem->IsQuadratic() ) // split liner quadrangle
1443 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1444 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1445 if ( aBadRate1 <= aBadRate2 ) {
1446 // tr1 + tr2 is better
1447 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1448 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1451 // tr3 + tr4 is better
1452 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1453 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1456 else // split quadratic quadrangle
1458 helper.SetIsQuadratic( true );
1459 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1461 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1462 if ( aNodes.size() == 9 )
1464 helper.SetIsBiQuadratic( true );
1465 if ( aBadRate1 <= aBadRate2 )
1466 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1468 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1470 // create a new element
1471 if ( aBadRate1 <= aBadRate2 ) {
1472 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1473 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1476 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1477 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1481 // care of a new element
1483 myLastCreatedElems.Append(newElem1);
1484 myLastCreatedElems.Append(newElem2);
1485 AddToSameGroups( newElem1, elem, aMesh );
1486 AddToSameGroups( newElem2, elem, aMesh );
1488 // put a new triangle on the same shape
1490 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1491 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1493 aMesh->RemoveElement( elem );
1498 //=======================================================================
1500 * \brief Split each of given quadrangles into 4 triangles.
1501 * \param theElems - The faces to be splitted. If empty all faces are split.
1503 //=======================================================================
1505 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1507 myLastCreatedElems.Clear();
1508 myLastCreatedNodes.Clear();
1510 SMESH_MesherHelper helper( *GetMesh() );
1511 helper.SetElementsOnShape( true );
1513 SMDS_ElemIteratorPtr faceIt;
1514 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1515 else faceIt = elemSetIterator( theElems );
1518 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1520 vector< const SMDS_MeshNode* > nodes;
1521 SMESHDS_SubMesh* subMeshDS;
1523 Handle(Geom_Surface) surface;
1524 TopLoc_Location loc;
1526 while ( faceIt->more() )
1528 const SMDS_MeshElement* quad = faceIt->next();
1529 if ( !quad || quad->NbCornerNodes() != 4 )
1532 // get a surface the quad is on
1534 if ( quad->getshapeId() < 1 )
1537 helper.SetSubShape( 0 );
1540 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1542 helper.SetSubShape( quad->getshapeId() );
1543 if ( !helper.GetSubShape().IsNull() &&
1544 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1546 F = TopoDS::Face( helper.GetSubShape() );
1547 surface = BRep_Tool::Surface( F, loc );
1548 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1552 helper.SetSubShape( 0 );
1557 // create a central node
1559 const SMDS_MeshNode* nCentral;
1560 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1562 if ( nodes.size() == 9 )
1564 nCentral = nodes.back();
1571 for ( ; iN < nodes.size(); ++iN )
1572 xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1574 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1575 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1577 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1578 xyz[0], xyz[1], xyz[2], xyz[3],
1579 xyz[4], xyz[5], xyz[6], xyz[7] );
1583 for ( ; iN < nodes.size(); ++iN )
1584 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1586 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1587 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1589 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1590 uv[0], uv[1], uv[2], uv[3],
1591 uv[4], uv[5], uv[6], uv[7] );
1593 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1597 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1598 uv[8].X(), uv[8].Y() );
1599 myLastCreatedNodes.Append( nCentral );
1602 // create 4 triangles
1604 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1606 helper.SetIsQuadratic ( nodes.size() > 4 );
1607 helper.SetIsBiQuadratic( nodes.size() == 9 );
1608 if ( helper.GetIsQuadratic() )
1609 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1611 for ( int i = 0; i < 4; ++i )
1613 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1616 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1617 myLastCreatedElems.Append( tria );
1622 //=======================================================================
1623 //function : BestSplit
1624 //purpose : Find better diagonal for cutting.
1625 //=======================================================================
1627 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1628 SMESH::Controls::NumericalFunctorPtr theCrit)
1630 myLastCreatedElems.Clear();
1631 myLastCreatedNodes.Clear();
1636 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1639 if( theQuad->NbNodes()==4 ||
1640 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1642 // retrieve element nodes
1643 const SMDS_MeshNode* aNodes [4];
1644 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1646 //while (itN->more())
1648 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1650 // compare two sets of possible triangles
1651 double aBadRate1, aBadRate2; // to what extent a set is bad
1652 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1653 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1654 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1656 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1657 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1658 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1659 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1660 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1661 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1662 return 1; // diagonal 1-3
1664 return 2; // diagonal 2-4
1671 // Methods of splitting volumes into tetra
1673 const int theHexTo5_1[5*4+1] =
1675 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1677 const int theHexTo5_2[5*4+1] =
1679 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1681 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1683 const int theHexTo6_1[6*4+1] =
1685 1, 5, 6, 0, 0, 1, 2, 6, 0, 4, 5, 6, 0, 4, 6, 7, 0, 2, 3, 6, 0, 3, 7, 6, -1
1687 const int theHexTo6_2[6*4+1] =
1689 2, 6, 7, 1, 1, 2, 3, 7, 1, 5, 6, 7, 1, 5, 7, 4, 1, 3, 0, 7, 1, 0, 4, 7, -1
1691 const int theHexTo6_3[6*4+1] =
1693 3, 7, 4, 2, 2, 3, 0, 4, 2, 6, 7, 4, 2, 6, 4, 5, 2, 0, 1, 4, 2, 1, 5, 4, -1
1695 const int theHexTo6_4[6*4+1] =
1697 0, 4, 5, 3, 3, 0, 1, 5, 3, 7, 4, 5, 3, 7, 5, 6, 3, 1, 2, 5, 3, 2, 6, 5, -1
1699 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1701 const int thePyraTo2_1[2*4+1] =
1703 0, 1, 2, 4, 0, 2, 3, 4, -1
1705 const int thePyraTo2_2[2*4+1] =
1707 1, 2, 3, 4, 1, 3, 0, 4, -1
1709 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1711 const int thePentaTo3_1[3*4+1] =
1713 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1715 const int thePentaTo3_2[3*4+1] =
1717 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1719 const int thePentaTo3_3[3*4+1] =
1721 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1723 const int thePentaTo3_4[3*4+1] =
1725 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1727 const int thePentaTo3_5[3*4+1] =
1729 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1731 const int thePentaTo3_6[3*4+1] =
1733 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1735 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1736 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1738 // Methods of splitting hexahedron into prisms
1740 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1742 0, 1, 8, 4, 5, 9, 1, 2, 8, 5, 6, 9, 2, 3, 8, 6, 7, 9, 3, 0, 8, 7, 4, 9, -1
1744 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1746 1, 0, 8, 2, 3, 9, 0, 4, 8, 3, 7, 9, 4, 5, 8, 7, 6, 9, 5, 1, 8, 6, 2, 9, -1
1748 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1750 0, 3, 9, 1, 2, 8, 3, 7, 9, 2, 6, 8, 7, 4, 9, 6, 5, 8, 4, 0, 9, 5, 1, 8, -1
1753 const int theHexTo2Prisms_BT_1[6*2+1] =
1755 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1757 const int theHexTo2Prisms_BT_2[6*2+1] =
1759 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1761 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1763 const int theHexTo2Prisms_LR_1[6*2+1] =
1765 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1767 const int theHexTo2Prisms_LR_2[6*2+1] =
1769 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1771 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1773 const int theHexTo2Prisms_FB_1[6*2+1] =
1775 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1777 const int theHexTo2Prisms_FB_2[6*2+1] =
1779 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1781 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1784 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1787 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1788 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1789 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1790 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1796 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1797 bool _baryNode; //!< additional node is to be created at cell barycenter
1798 bool _ownConn; //!< to delete _connectivity in destructor
1799 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1801 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1802 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1803 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1804 bool hasFacet( const TTriangleFacet& facet ) const
1806 if ( _nbCorners == 4 )
1808 const int* tetConn = _connectivity;
1809 for ( ; tetConn[0] >= 0; tetConn += 4 )
1810 if (( facet.contains( tetConn[0] ) +
1811 facet.contains( tetConn[1] ) +
1812 facet.contains( tetConn[2] ) +
1813 facet.contains( tetConn[3] )) == 3 )
1816 else // prism, _nbCorners == 6
1818 const int* prismConn = _connectivity;
1819 for ( ; prismConn[0] >= 0; prismConn += 6 )
1821 if (( facet.contains( prismConn[0] ) &&
1822 facet.contains( prismConn[1] ) &&
1823 facet.contains( prismConn[2] ))
1825 ( facet.contains( prismConn[3] ) &&
1826 facet.contains( prismConn[4] ) &&
1827 facet.contains( prismConn[5] )))
1835 //=======================================================================
1837 * \brief return TSplitMethod for the given element to split into tetrahedra
1839 //=======================================================================
1841 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1843 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1845 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1846 // an edge and a face barycenter; tertaherdons are based on triangles and
1847 // a volume barycenter
1848 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1850 // Find out how adjacent volumes are split
1852 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1853 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1854 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1856 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1857 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1858 if ( nbNodes < 4 ) continue;
1860 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1861 const int* nInd = vol.GetFaceNodesIndices( iF );
1864 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1865 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1866 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1867 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1871 int iCom = 0; // common node of triangle faces to split into
1872 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1874 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1875 nInd[ iQ * ( (iCom+1)%nbNodes )],
1876 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1877 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1878 nInd[ iQ * ( (iCom+2)%nbNodes )],
1879 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1880 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1882 triaSplits.push_back( t012 );
1883 triaSplits.push_back( t023 );
1888 if ( !triaSplits.empty() )
1889 hasAdjacentSplits = true;
1892 // Among variants of split method select one compliant with adjacent volumes
1894 TSplitMethod method;
1895 if ( !vol.Element()->IsPoly() && !is24TetMode )
1897 int nbVariants = 2, nbTet = 0;
1898 const int** connVariants = 0;
1899 switch ( vol.Element()->GetEntityType() )
1901 case SMDSEntity_Hexa:
1902 case SMDSEntity_Quad_Hexa:
1903 case SMDSEntity_TriQuad_Hexa:
1904 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1905 connVariants = theHexTo5, nbTet = 5;
1907 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1909 case SMDSEntity_Pyramid:
1910 case SMDSEntity_Quad_Pyramid:
1911 connVariants = thePyraTo2; nbTet = 2;
1913 case SMDSEntity_Penta:
1914 case SMDSEntity_Quad_Penta:
1915 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1920 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1922 // check method compliancy with adjacent tetras,
1923 // all found splits must be among facets of tetras described by this method
1924 method = TSplitMethod( nbTet, connVariants[variant] );
1925 if ( hasAdjacentSplits && method._nbSplits > 0 )
1927 bool facetCreated = true;
1928 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1930 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1931 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1932 facetCreated = method.hasFacet( *facet );
1934 if ( !facetCreated )
1935 method = TSplitMethod(0); // incompatible method
1939 if ( method._nbSplits < 1 )
1941 // No standard method is applicable, use a generic solution:
1942 // each facet of a volume is split into triangles and
1943 // each of triangles and a volume barycenter form a tetrahedron.
1945 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1947 int* connectivity = new int[ maxTetConnSize + 1 ];
1948 method._connectivity = connectivity;
1949 method._ownConn = true;
1950 method._baryNode = !isHex27; // to create central node or not
1953 int baryCenInd = vol.NbNodes() - int( isHex27 );
1954 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1956 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1957 const int* nInd = vol.GetFaceNodesIndices( iF );
1958 // find common node of triangle facets of tetra to create
1959 int iCommon = 0; // index in linear numeration
1960 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1961 if ( !triaSplits.empty() )
1964 const TTriangleFacet* facet = &triaSplits.front();
1965 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1966 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1967 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1970 else if ( nbNodes > 3 && !is24TetMode )
1972 // find the best method of splitting into triangles by aspect ratio
1973 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1974 map< double, int > badness2iCommon;
1975 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1976 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1977 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1980 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1982 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1983 nodes[ iQ*((iLast-1)%nbNodes)],
1984 nodes[ iQ*((iLast )%nbNodes)]);
1985 badness += getBadRate( &tria, aspectRatio );
1987 badness2iCommon.insert( make_pair( badness, iCommon ));
1989 // use iCommon with lowest badness
1990 iCommon = badness2iCommon.begin()->second;
1992 if ( iCommon >= nbNodes )
1993 iCommon = 0; // something wrong
1995 // fill connectivity of tetrahedra based on a current face
1996 int nbTet = nbNodes - 2;
1997 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2002 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2003 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2007 method._faceBaryNode[ iF ] = 0;
2008 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2011 for ( int i = 0; i < nbTet; ++i )
2013 int i1 = i, i2 = (i+1) % nbNodes;
2014 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2015 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2016 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2017 connectivity[ connSize++ ] = faceBaryCenInd;
2018 connectivity[ connSize++ ] = baryCenInd;
2023 for ( int i = 0; i < nbTet; ++i )
2025 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2026 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2027 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2028 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2029 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2030 connectivity[ connSize++ ] = baryCenInd;
2033 method._nbSplits += nbTet;
2035 } // loop on volume faces
2037 connectivity[ connSize++ ] = -1;
2039 } // end of generic solution
2043 //=======================================================================
2045 * \brief return TSplitMethod to split haxhedron into prisms
2047 //=======================================================================
2049 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2050 const int methodFlags,
2051 const int facetToSplit)
2053 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2055 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2057 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2059 static TSplitMethod to4methods[4]; // order BT, LR, FB
2060 if ( to4methods[iF]._nbSplits == 0 )
2064 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2065 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2066 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2069 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2070 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2071 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2074 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2075 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2076 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2078 default: return to4methods[3];
2080 to4methods[iF]._nbSplits = 4;
2081 to4methods[iF]._nbCorners = 6;
2083 return to4methods[iF];
2085 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2087 TSplitMethod method;
2089 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2091 const int nbVariants = 2, nbSplits = 2;
2092 const int** connVariants = 0;
2094 case 0: connVariants = theHexTo2Prisms_BT; break;
2095 case 1: connVariants = theHexTo2Prisms_LR; break;
2096 case 2: connVariants = theHexTo2Prisms_FB; break;
2097 default: return method;
2100 // look for prisms adjacent via facetToSplit and an opposite one
2101 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2103 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2104 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2105 if ( nbNodes != 4 ) return method;
2107 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2108 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2109 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2111 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2113 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2118 // there are adjacent prism
2119 for ( int variant = 0; variant < nbVariants; ++variant )
2121 // check method compliancy with adjacent prisms,
2122 // the found prism facets must be among facets of prisms described by current method
2123 method._nbSplits = nbSplits;
2124 method._nbCorners = 6;
2125 method._connectivity = connVariants[ variant ];
2126 if ( method.hasFacet( *t ))
2131 // No adjacent prisms. Select a variant with a best aspect ratio.
2133 double badness[2] = { 0, 0 };
2134 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2135 const SMDS_MeshNode** nodes = vol.GetNodes();
2136 for ( int variant = 0; variant < nbVariants; ++variant )
2137 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2139 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2140 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2142 method._connectivity = connVariants[ variant ];
2143 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2144 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2145 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2147 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2150 badness[ variant ] += getBadRate( &tria, aspectRatio );
2152 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2154 method._nbSplits = nbSplits;
2155 method._nbCorners = 6;
2156 method._connectivity = connVariants[ iBetter ];
2161 //================================================================================
2163 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2165 //================================================================================
2167 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2168 const SMDSAbs_GeometryType geom ) const
2170 // find the tetrahedron including the three nodes of facet
2171 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2172 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2173 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2174 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2175 while ( volIt1->more() )
2177 const SMDS_MeshElement* v = volIt1->next();
2178 if ( v->GetGeomType() != geom )
2180 const int lastCornerInd = v->NbCornerNodes() - 1;
2181 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2182 continue; // medium node not allowed
2183 const int ind2 = v->GetNodeIndex( n2 );
2184 if ( ind2 < 0 || lastCornerInd < ind2 )
2186 const int ind3 = v->GetNodeIndex( n3 );
2187 if ( ind3 < 0 || lastCornerInd < ind3 )
2194 //=======================================================================
2196 * \brief A key of a face of volume
2198 //=======================================================================
2200 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2202 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2204 TIDSortedNodeSet sortedNodes;
2205 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2206 int nbNodes = vol.NbFaceNodes( iF );
2207 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2208 for ( int i = 0; i < nbNodes; i += iQ )
2209 sortedNodes.insert( fNodes[i] );
2210 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2211 first.first = (*(n++))->GetID();
2212 first.second = (*(n++))->GetID();
2213 second.first = (*(n++))->GetID();
2214 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2219 //=======================================================================
2220 //function : SplitVolumes
2221 //purpose : Split volume elements into tetrahedra or prisms.
2222 // If facet ID < 0, element is split into tetrahedra,
2223 // else a hexahedron is split into prisms so that the given facet is
2224 // split into triangles
2225 //=======================================================================
2227 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2228 const int theMethodFlags)
2230 SMDS_VolumeTool volTool;
2231 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2232 fHelper.ToFixNodeParameters( true );
2234 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2235 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2237 SMESH_SequenceOfElemPtr newNodes, newElems;
2239 // map face of volume to it's baricenrtic node
2240 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2242 vector<const SMDS_MeshElement* > splitVols;
2244 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2245 for ( ; elem2facet != theElems.end(); ++elem2facet )
2247 const SMDS_MeshElement* elem = elem2facet->first;
2248 const int facetToSplit = elem2facet->second;
2249 if ( elem->GetType() != SMDSAbs_Volume )
2251 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2252 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2255 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2257 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2258 getTetraSplitMethod( volTool, theMethodFlags ) :
2259 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2260 if ( splitMethod._nbSplits < 1 ) continue;
2262 // find submesh to add new tetras to
2263 if ( !subMesh || !subMesh->Contains( elem ))
2265 int shapeID = FindShape( elem );
2266 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2267 subMesh = GetMeshDS()->MeshElements( shapeID );
2270 if ( elem->IsQuadratic() )
2273 // add quadratic links to the helper
2274 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2276 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2277 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2278 for ( int iN = 0; iN < nbN; iN += iQ )
2279 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2281 helper.SetIsQuadratic( true );
2286 helper.SetIsQuadratic( false );
2288 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2289 volTool.GetNodes() + elem->NbNodes() );
2290 helper.SetElementsOnShape( true );
2291 if ( splitMethod._baryNode )
2293 // make a node at barycenter
2294 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2295 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2296 nodes.push_back( gcNode );
2297 newNodes.Append( gcNode );
2299 if ( !splitMethod._faceBaryNode.empty() )
2301 // make or find baricentric nodes of faces
2302 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2303 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2305 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2306 volFace2BaryNode.insert
2307 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2310 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2311 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2313 nodes.push_back( iF_n->second = f_n->second );
2318 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2319 const int* volConn = splitMethod._connectivity;
2320 if ( splitMethod._nbCorners == 4 ) // tetra
2321 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2322 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2323 nodes[ volConn[1] ],
2324 nodes[ volConn[2] ],
2325 nodes[ volConn[3] ]));
2327 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2328 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2329 nodes[ volConn[1] ],
2330 nodes[ volConn[2] ],
2331 nodes[ volConn[3] ],
2332 nodes[ volConn[4] ],
2333 nodes[ volConn[5] ]));
2335 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2337 // Split faces on sides of the split volume
2339 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2340 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2342 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2343 if ( nbNodes < 4 ) continue;
2345 // find an existing face
2346 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2347 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2348 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2349 /*noMedium=*/false))
2352 helper.SetElementsOnShape( false );
2353 vector< const SMDS_MeshElement* > triangles;
2355 // find submesh to add new triangles in
2356 if ( !fSubMesh || !fSubMesh->Contains( face ))
2358 int shapeID = FindShape( face );
2359 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2361 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2362 if ( iF_n != splitMethod._faceBaryNode.end() )
2364 const SMDS_MeshNode *baryNode = iF_n->second;
2365 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2367 const SMDS_MeshNode* n1 = fNodes[iN];
2368 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2369 const SMDS_MeshNode *n3 = baryNode;
2370 if ( !volTool.IsFaceExternal( iF ))
2372 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2374 if ( fSubMesh ) // update position of the bary node on geometry
2377 subMesh->RemoveNode( baryNode, false );
2378 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2379 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2380 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2382 fHelper.SetSubShape( s );
2383 gp_XY uv( 1e100, 1e100 );
2385 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2386 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2389 // node is too far from the surface
2390 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2391 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2392 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2399 // among possible triangles create ones discribed by split method
2400 const int* nInd = volTool.GetFaceNodesIndices( iF );
2401 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2402 int iCom = 0; // common node of triangle faces to split into
2403 list< TTriangleFacet > facets;
2404 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2406 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2407 nInd[ iQ * ( (iCom+1)%nbNodes )],
2408 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2409 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2410 nInd[ iQ * ( (iCom+2)%nbNodes )],
2411 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2412 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2414 facets.push_back( t012 );
2415 facets.push_back( t023 );
2416 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2417 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2418 nInd[ iQ * ((iLast-1)%nbNodes )],
2419 nInd[ iQ * ((iLast )%nbNodes )]));
2423 list< TTriangleFacet >::iterator facet = facets.begin();
2424 if ( facet == facets.end() )
2426 for ( ; facet != facets.end(); ++facet )
2428 if ( !volTool.IsFaceExternal( iF ))
2429 swap( facet->_n2, facet->_n3 );
2430 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2431 volNodes[ facet->_n2 ],
2432 volNodes[ facet->_n3 ]));
2435 for ( int i = 0; i < triangles.size(); ++i )
2437 if ( !triangles[i] ) continue;
2439 fSubMesh->AddElement( triangles[i]);
2440 newElems.Append( triangles[i] );
2442 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2443 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2445 } // while a face based on facet nodes exists
2446 } // loop on volume faces to split them into triangles
2448 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2450 if ( geomType == SMDSEntity_TriQuad_Hexa )
2452 // remove medium nodes that could become free
2453 for ( int i = 20; i < volTool.NbNodes(); ++i )
2454 if ( volNodes[i]->NbInverseElements() == 0 )
2455 GetMeshDS()->RemoveNode( volNodes[i] );
2457 } // loop on volumes to split
2459 myLastCreatedNodes = newNodes;
2460 myLastCreatedElems = newElems;
2463 //=======================================================================
2464 //function : GetHexaFacetsToSplit
2465 //purpose : For hexahedra that will be split into prisms, finds facets to
2466 // split into triangles. Only hexahedra adjacent to the one closest
2467 // to theFacetNormal.Location() are returned.
2468 //param [in,out] theHexas - the hexahedra
2469 //param [in] theFacetNormal - facet normal
2470 //param [out] theFacets - the hexahedra and found facet IDs
2471 //=======================================================================
2473 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2474 const gp_Ax1& theFacetNormal,
2475 TFacetOfElem & theFacets)
2477 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2479 // Find a hexa closest to the location of theFacetNormal
2481 const SMDS_MeshElement* startHex;
2483 // get SMDS_ElemIteratorPtr on theHexas
2484 typedef const SMDS_MeshElement* TValue;
2485 typedef TIDSortedElemSet::iterator TSetIterator;
2486 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2487 typedef SMDS_MeshElement::GeomFilter TFilter;
2488 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2489 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2490 ( new TElemSetIter( theHexas.begin(),
2492 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2494 SMESH_ElementSearcher* searcher =
2495 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2497 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2502 throw SALOME_Exception( THIS_METHOD "startHex not found");
2505 // Select a facet of startHex by theFacetNormal
2507 SMDS_VolumeTool vTool( startHex );
2508 double norm[3], dot, maxDot = 0;
2510 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2511 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2513 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2521 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2523 // Fill theFacets starting from facetID of startHex
2525 // facets used for seach of volumes adjacent to already treated ones
2526 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2527 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2528 TFacetMap facetsToCheck;
2530 set<const SMDS_MeshNode*> facetNodes;
2531 const SMDS_MeshElement* curHex;
2533 const bool allHex = ( theHexas.size() == myMesh->NbHexas() );
2537 // move in two directions from startHex via facetID
2538 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2541 int curFacet = facetID;
2542 if ( is2nd ) // do not treat startHex twice
2544 vTool.Set( curHex );
2545 if ( vTool.IsFreeFace( curFacet, &curHex ))
2551 vTool.GetFaceNodes( curFacet, facetNodes );
2552 vTool.Set( curHex );
2553 curFacet = vTool.GetFaceIndex( facetNodes );
2558 // store a facet to split
2559 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2561 theFacets.insert( make_pair( curHex, -1 ));
2564 if ( !allHex && !theHexas.count( curHex ))
2567 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2568 theFacets.insert( make_pair( curHex, curFacet ));
2569 if ( !facetIt2isNew.second )
2572 // remember not-to-split facets in facetsToCheck
2573 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2574 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2576 if ( iF == curFacet && iF == oppFacet )
2578 TVolumeFaceKey facetKey ( vTool, iF );
2579 TElemFacets elemFacet( facetIt2isNew.first, iF );
2580 pair< TFacetMap::iterator, bool > it2isnew =
2581 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2582 if ( !it2isnew.second )
2583 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2585 // pass to a volume adjacent via oppFacet
2586 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2592 // get a new curFacet
2593 vTool.GetFaceNodes( oppFacet, facetNodes );
2594 vTool.Set( curHex );
2595 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2598 } // move in two directions from startHex via facetID
2600 // Find a new startHex by facetsToCheck
2604 TFacetMap::iterator fIt = facetsToCheck.begin();
2605 while ( !startHex && fIt != facetsToCheck.end() )
2607 const TElemFacets& elemFacets = fIt->second;
2608 const SMDS_MeshElement* hex = elemFacets.first->first;
2609 int splitFacet = elemFacets.first->second;
2610 int lateralFacet = elemFacets.second;
2611 facetsToCheck.erase( fIt );
2612 fIt = facetsToCheck.begin();
2615 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2616 curHex->GetGeomType() != SMDSGeom_HEXA )
2618 if ( !allHex && !theHexas.count( curHex ))
2623 // find a facet of startHex to split
2625 set<const SMDS_MeshNode*> lateralNodes;
2626 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2627 vTool.GetFaceNodes( splitFacet, facetNodes );
2628 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2629 vTool.Set( startHex );
2630 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2632 // look for a facet of startHex having common nodes with facetNodes
2633 // but not lateralFacet
2634 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2636 if ( iF == lateralFacet )
2638 int nbCommonNodes = 0;
2639 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2640 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2641 nbCommonNodes += facetNodes.count( nn[ iN ]);
2643 if ( nbCommonNodes >= 2 )
2650 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2652 } // while ( startHex )
2659 //================================================================================
2661 * \brief Selects nodes of several elements according to a given interlace
2662 * \param [in] srcNodes - nodes to select from
2663 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2664 * \param [in] interlace - indices of nodes for all elements
2665 * \param [in] nbElems - nb of elements
2666 * \param [in] nbNodes - nb of nodes in each element
2667 * \param [in] mesh - the mesh
2668 * \param [out] elemQueue - a list to push elements found by the selected nodes
2669 * \param [in] type - type of elements to look for
2671 //================================================================================
2673 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2674 vector< const SMDS_MeshNode* >* tgtNodesVec,
2675 const int* interlace,
2678 SMESHDS_Mesh* mesh = 0,
2679 list< const SMDS_MeshElement* >* elemQueue=0,
2680 SMDSAbs_ElementType type=SMDSAbs_All)
2682 for ( int iE = 0; iE < nbElems; ++iE )
2684 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2685 const int* select = & interlace[iE*nbNodes];
2686 elemNodes.resize( nbNodes );
2687 for ( int iN = 0; iN < nbNodes; ++iN )
2688 elemNodes[iN] = srcNodes[ select[ iN ]];
2690 const SMDS_MeshElement* e;
2692 for ( int iE = 0; iE < nbElems; ++iE )
2693 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2694 elemQueue->push_back( e );
2698 //=======================================================================
2700 * Split bi-quadratic elements into linear ones without creation of additional nodes
2701 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2702 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2703 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2704 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2705 * will be split in order to keep the mesh conformal.
2706 * \param elems - elements to split
2708 //=======================================================================
2710 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2712 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2713 vector<const SMDS_MeshElement* > splitElems;
2714 list< const SMDS_MeshElement* > elemQueue;
2715 list< const SMDS_MeshElement* >::iterator elemIt;
2717 SMESHDS_Mesh * mesh = GetMeshDS();
2718 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2719 int nbElems, nbNodes;
2721 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2722 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2725 elemQueue.push_back( *elemSetIt );
2726 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2728 const SMDS_MeshElement* elem = *elemIt;
2729 switch( elem->GetEntityType() )
2731 case SMDSEntity_TriQuad_Hexa: // HEX27
2733 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2734 nbElems = nbNodes = 8;
2735 elemType = & hexaType;
2737 // get nodes for new elements
2738 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2739 { 1,9,20,8, 17,22,26,21 },
2740 { 2,10,20,9, 18,23,26,22 },
2741 { 3,11,20,10, 19,24,26,23 },
2742 { 16,21,26,24, 4,12,25,15 },
2743 { 17,22,26,21, 5,13,25,12 },
2744 { 18,23,26,22, 6,14,25,13 },
2745 { 19,24,26,23, 7,15,25,14 }};
2746 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2748 // add boundary faces to elemQueue
2749 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2750 { 4,5,6,7, 12,13,14,15, 25 },
2751 { 0,1,5,4, 8,17,12,16, 21 },
2752 { 1,2,6,5, 9,18,13,17, 22 },
2753 { 2,3,7,6, 10,19,14,18, 23 },
2754 { 3,0,4,7, 11,16,15,19, 24 }};
2755 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2757 // add boundary segments to elemQueue
2758 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2759 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2760 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2761 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2764 case SMDSEntity_BiQuad_Triangle: // TRIA7
2766 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2769 elemType = & quadType;
2771 // get nodes for new elements
2772 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2773 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2775 // add boundary segments to elemQueue
2776 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2777 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2780 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2782 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2785 elemType = & quadType;
2787 // get nodes for new elements
2788 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2789 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2791 // add boundary segments to elemQueue
2792 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2793 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2796 case SMDSEntity_Quad_Edge:
2798 if ( elemIt == elemQueue.begin() )
2799 continue; // an elem is in theElems
2800 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2803 elemType = & segType;
2805 // get nodes for new elements
2806 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2807 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2811 } // switch( elem->GetEntityType() )
2813 // Create new elements
2815 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2819 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2820 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2821 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2822 //elemType->SetID( -1 );
2824 for ( int iE = 0; iE < nbElems; ++iE )
2825 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2828 ReplaceElemInGroups( elem, splitElems, mesh );
2831 for ( size_t i = 0; i < splitElems.size(); ++i )
2832 subMesh->AddElement( splitElems[i] );
2837 //=======================================================================
2838 //function : AddToSameGroups
2839 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2840 //=======================================================================
2842 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2843 const SMDS_MeshElement* elemInGroups,
2844 SMESHDS_Mesh * aMesh)
2846 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2847 if (!groups.empty()) {
2848 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2849 for ( ; grIt != groups.end(); grIt++ ) {
2850 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2851 if ( group && group->Contains( elemInGroups ))
2852 group->SMDSGroup().Add( elemToAdd );
2858 //=======================================================================
2859 //function : RemoveElemFromGroups
2860 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2861 //=======================================================================
2862 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2863 SMESHDS_Mesh * aMesh)
2865 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2866 if (!groups.empty())
2868 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2869 for (; GrIt != groups.end(); GrIt++)
2871 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2872 if (!grp || grp->IsEmpty()) continue;
2873 grp->SMDSGroup().Remove(removeelem);
2878 //================================================================================
2880 * \brief Replace elemToRm by elemToAdd in the all groups
2882 //================================================================================
2884 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2885 const SMDS_MeshElement* elemToAdd,
2886 SMESHDS_Mesh * aMesh)
2888 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2889 if (!groups.empty()) {
2890 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2891 for ( ; grIt != groups.end(); grIt++ ) {
2892 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2893 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2894 group->SMDSGroup().Add( elemToAdd );
2899 //================================================================================
2901 * \brief Replace elemToRm by elemToAdd in the all groups
2903 //================================================================================
2905 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2906 const vector<const SMDS_MeshElement*>& elemToAdd,
2907 SMESHDS_Mesh * aMesh)
2909 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2910 if (!groups.empty())
2912 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2913 for ( ; grIt != groups.end(); grIt++ ) {
2914 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2915 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2916 for ( int i = 0; i < elemToAdd.size(); ++i )
2917 group->SMDSGroup().Add( elemToAdd[ i ] );
2922 //=======================================================================
2923 //function : QuadToTri
2924 //purpose : Cut quadrangles into triangles.
2925 // theCrit is used to select a diagonal to cut
2926 //=======================================================================
2928 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2929 const bool the13Diag)
2931 myLastCreatedElems.Clear();
2932 myLastCreatedNodes.Clear();
2934 MESSAGE( "::QuadToTri()" );
2936 SMESHDS_Mesh * aMesh = GetMeshDS();
2938 Handle(Geom_Surface) surface;
2939 SMESH_MesherHelper helper( *GetMesh() );
2941 TIDSortedElemSet::iterator itElem;
2942 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2943 const SMDS_MeshElement* elem = *itElem;
2944 if ( !elem || elem->GetType() != SMDSAbs_Face )
2946 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2947 if(!isquad) continue;
2949 if(elem->NbNodes()==4) {
2950 // retrieve element nodes
2951 const SMDS_MeshNode* aNodes [4];
2952 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2954 while ( itN->more() )
2955 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2957 int aShapeId = FindShape( elem );
2958 const SMDS_MeshElement* newElem1 = 0;
2959 const SMDS_MeshElement* newElem2 = 0;
2961 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2962 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2965 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2966 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2968 myLastCreatedElems.Append(newElem1);
2969 myLastCreatedElems.Append(newElem2);
2970 // put a new triangle on the same shape and add to the same groups
2973 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2974 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2976 AddToSameGroups( newElem1, elem, aMesh );
2977 AddToSameGroups( newElem2, elem, aMesh );
2978 aMesh->RemoveElement( elem );
2981 // Quadratic quadrangle
2983 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2985 // get surface elem is on
2986 int aShapeId = FindShape( elem );
2987 if ( aShapeId != helper.GetSubShapeID() ) {
2991 shape = aMesh->IndexToShape( aShapeId );
2992 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2993 TopoDS_Face face = TopoDS::Face( shape );
2994 surface = BRep_Tool::Surface( face );
2995 if ( !surface.IsNull() )
2996 helper.SetSubShape( shape );
3000 const SMDS_MeshNode* aNodes [8];
3001 const SMDS_MeshNode* inFaceNode = 0;
3002 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3004 while ( itN->more() ) {
3005 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3006 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
3007 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
3009 inFaceNode = aNodes[ i-1 ];
3013 // find middle point for (0,1,2,3)
3014 // and create a node in this point;
3016 if ( surface.IsNull() ) {
3018 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
3022 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
3025 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
3027 p = surface->Value( uv.X(), uv.Y() ).XYZ();
3029 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
3030 myLastCreatedNodes.Append(newN);
3032 // create a new element
3033 const SMDS_MeshElement* newElem1 = 0;
3034 const SMDS_MeshElement* newElem2 = 0;
3036 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3037 aNodes[6], aNodes[7], newN );
3038 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3039 newN, aNodes[4], aNodes[5] );
3042 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3043 aNodes[7], aNodes[4], newN );
3044 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3045 newN, aNodes[5], aNodes[6] );
3047 myLastCreatedElems.Append(newElem1);
3048 myLastCreatedElems.Append(newElem2);
3049 // put a new triangle on the same shape and add to the same groups
3052 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3053 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3055 AddToSameGroups( newElem1, elem, aMesh );
3056 AddToSameGroups( newElem2, elem, aMesh );
3057 aMesh->RemoveElement( elem );
3064 //=======================================================================
3065 //function : getAngle
3067 //=======================================================================
3069 double getAngle(const SMDS_MeshElement * tr1,
3070 const SMDS_MeshElement * tr2,
3071 const SMDS_MeshNode * n1,
3072 const SMDS_MeshNode * n2)
3074 double angle = 2. * M_PI; // bad angle
3077 SMESH::Controls::TSequenceOfXYZ P1, P2;
3078 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3079 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3082 if(!tr1->IsQuadratic())
3083 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3085 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3086 if ( N1.SquareMagnitude() <= gp::Resolution() )
3088 if(!tr2->IsQuadratic())
3089 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3091 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3092 if ( N2.SquareMagnitude() <= gp::Resolution() )
3095 // find the first diagonal node n1 in the triangles:
3096 // take in account a diagonal link orientation
3097 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3098 for ( int t = 0; t < 2; t++ ) {
3099 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3100 int i = 0, iDiag = -1;
3101 while ( it->more()) {
3102 const SMDS_MeshElement *n = it->next();
3103 if ( n == n1 || n == n2 ) {
3107 if ( i - iDiag == 1 )
3108 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3117 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3120 angle = N1.Angle( N2 );
3125 // =================================================
3126 // class generating a unique ID for a pair of nodes
3127 // and able to return nodes by that ID
3128 // =================================================
3132 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3133 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3136 long GetLinkID (const SMDS_MeshNode * n1,
3137 const SMDS_MeshNode * n2) const
3139 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3142 bool GetNodes (const long theLinkID,
3143 const SMDS_MeshNode* & theNode1,
3144 const SMDS_MeshNode* & theNode2) const
3146 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3147 if ( !theNode1 ) return false;
3148 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3149 if ( !theNode2 ) return false;
3155 const SMESHDS_Mesh* myMesh;
3160 //=======================================================================
3161 //function : TriToQuad
3162 //purpose : Fuse neighbour triangles into quadrangles.
3163 // theCrit is used to select a neighbour to fuse with.
3164 // theMaxAngle is a max angle between element normals at which
3165 // fusion is still performed.
3166 //=======================================================================
3168 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3169 SMESH::Controls::NumericalFunctorPtr theCrit,
3170 const double theMaxAngle)
3172 myLastCreatedElems.Clear();
3173 myLastCreatedNodes.Clear();
3175 MESSAGE( "::TriToQuad()" );
3177 if ( !theCrit.get() )
3180 SMESHDS_Mesh * aMesh = GetMeshDS();
3182 // Prepare data for algo: build
3183 // 1. map of elements with their linkIDs
3184 // 2. map of linkIDs with their elements
3186 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3187 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3188 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3189 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3191 TIDSortedElemSet::iterator itElem;
3192 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3194 const SMDS_MeshElement* elem = *itElem;
3195 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3196 bool IsTria = ( elem->NbCornerNodes()==3 );
3197 if (!IsTria) continue;
3199 // retrieve element nodes
3200 const SMDS_MeshNode* aNodes [4];
3201 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3204 aNodes[ i++ ] = itN->next();
3205 aNodes[ 3 ] = aNodes[ 0 ];
3208 for ( i = 0; i < 3; i++ ) {
3209 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3210 // check if elements sharing a link can be fused
3211 itLE = mapLi_listEl.find( link );
3212 if ( itLE != mapLi_listEl.end() ) {
3213 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3215 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3216 //if ( FindShape( elem ) != FindShape( elem2 ))
3217 // continue; // do not fuse triangles laying on different shapes
3218 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3219 continue; // avoid making badly shaped quads
3220 (*itLE).second.push_back( elem );
3223 mapLi_listEl[ link ].push_back( elem );
3225 mapEl_setLi [ elem ].insert( link );
3228 // Clean the maps from the links shared by a sole element, ie
3229 // links to which only one element is bound in mapLi_listEl
3231 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3232 int nbElems = (*itLE).second.size();
3233 if ( nbElems < 2 ) {
3234 const SMDS_MeshElement* elem = (*itLE).second.front();
3235 SMESH_TLink link = (*itLE).first;
3236 mapEl_setLi[ elem ].erase( link );
3237 if ( mapEl_setLi[ elem ].empty() )
3238 mapEl_setLi.erase( elem );
3242 // Algo: fuse triangles into quadrangles
3244 while ( ! mapEl_setLi.empty() ) {
3245 // Look for the start element:
3246 // the element having the least nb of shared links
3247 const SMDS_MeshElement* startElem = 0;
3249 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3250 int nbLinks = (*itEL).second.size();
3251 if ( nbLinks < minNbLinks ) {
3252 startElem = (*itEL).first;
3253 minNbLinks = nbLinks;
3254 if ( minNbLinks == 1 )
3259 // search elements to fuse starting from startElem or links of elements
3260 // fused earlyer - startLinks
3261 list< SMESH_TLink > startLinks;
3262 while ( startElem || !startLinks.empty() ) {
3263 while ( !startElem && !startLinks.empty() ) {
3264 // Get an element to start, by a link
3265 SMESH_TLink linkId = startLinks.front();
3266 startLinks.pop_front();
3267 itLE = mapLi_listEl.find( linkId );
3268 if ( itLE != mapLi_listEl.end() ) {
3269 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3270 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3271 for ( ; itE != listElem.end() ; itE++ )
3272 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3274 mapLi_listEl.erase( itLE );
3279 // Get candidates to be fused
3280 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3281 const SMESH_TLink *link12, *link13;
3283 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3284 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3285 ASSERT( !setLi.empty() );
3286 set< SMESH_TLink >::iterator itLi;
3287 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3289 const SMESH_TLink & link = (*itLi);
3290 itLE = mapLi_listEl.find( link );
3291 if ( itLE == mapLi_listEl.end() )
3294 const SMDS_MeshElement* elem = (*itLE).second.front();
3296 elem = (*itLE).second.back();
3297 mapLi_listEl.erase( itLE );
3298 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3309 // add other links of elem to list of links to re-start from
3310 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3311 set< SMESH_TLink >::iterator it;
3312 for ( it = links.begin(); it != links.end(); it++ ) {
3313 const SMESH_TLink& link2 = (*it);
3314 if ( link2 != link )
3315 startLinks.push_back( link2 );
3319 // Get nodes of possible quadrangles
3320 const SMDS_MeshNode *n12 [4], *n13 [4];
3321 bool Ok12 = false, Ok13 = false;
3322 const SMDS_MeshNode *linkNode1, *linkNode2;
3324 linkNode1 = link12->first;
3325 linkNode2 = link12->second;
3326 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3330 linkNode1 = link13->first;
3331 linkNode2 = link13->second;
3332 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3336 // Choose a pair to fuse
3337 if ( Ok12 && Ok13 ) {
3338 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3339 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3340 double aBadRate12 = getBadRate( &quad12, theCrit );
3341 double aBadRate13 = getBadRate( &quad13, theCrit );
3342 if ( aBadRate13 < aBadRate12 )
3349 // and remove fused elems and remove links from the maps
3350 mapEl_setLi.erase( tr1 );
3353 mapEl_setLi.erase( tr2 );
3354 mapLi_listEl.erase( *link12 );
3355 if ( tr1->NbNodes() == 3 )
3357 const SMDS_MeshElement* newElem = 0;
3358 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3359 myLastCreatedElems.Append(newElem);
3360 AddToSameGroups( newElem, tr1, aMesh );
3361 int aShapeId = tr1->getshapeId();
3363 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3364 aMesh->RemoveElement( tr1 );
3365 aMesh->RemoveElement( tr2 );
3368 vector< const SMDS_MeshNode* > N1;
3369 vector< const SMDS_MeshNode* > N2;
3370 getNodesFromTwoTria(tr1,tr2,N1,N2);
3371 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3372 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3373 // i.e. first nodes from both arrays form a new diagonal
3374 const SMDS_MeshNode* aNodes[8];
3383 const SMDS_MeshElement* newElem = 0;
3384 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3385 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3386 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3388 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3389 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3390 myLastCreatedElems.Append(newElem);
3391 AddToSameGroups( newElem, tr1, aMesh );
3392 int aShapeId = tr1->getshapeId();
3394 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3395 aMesh->RemoveElement( tr1 );
3396 aMesh->RemoveElement( tr2 );
3397 // remove middle node (9)
3398 if ( N1[4]->NbInverseElements() == 0 )
3399 aMesh->RemoveNode( N1[4] );
3400 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3401 aMesh->RemoveNode( N1[6] );
3402 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3403 aMesh->RemoveNode( N2[6] );
3408 mapEl_setLi.erase( tr3 );
3409 mapLi_listEl.erase( *link13 );
3410 if ( tr1->NbNodes() == 3 ) {
3411 const SMDS_MeshElement* newElem = 0;
3412 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3413 myLastCreatedElems.Append(newElem);
3414 AddToSameGroups( newElem, tr1, aMesh );
3415 int aShapeId = tr1->getshapeId();
3417 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3418 aMesh->RemoveElement( tr1 );
3419 aMesh->RemoveElement( tr3 );
3422 vector< const SMDS_MeshNode* > N1;
3423 vector< const SMDS_MeshNode* > N2;
3424 getNodesFromTwoTria(tr1,tr3,N1,N2);
3425 // now we receive following N1 and N2 (using numeration as above image)
3426 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3427 // i.e. first nodes from both arrays form a new diagonal
3428 const SMDS_MeshNode* aNodes[8];
3437 const SMDS_MeshElement* newElem = 0;
3438 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3439 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3440 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3442 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3443 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3444 myLastCreatedElems.Append(newElem);
3445 AddToSameGroups( newElem, tr1, aMesh );
3446 int aShapeId = tr1->getshapeId();
3448 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3449 aMesh->RemoveElement( tr1 );
3450 aMesh->RemoveElement( tr3 );
3451 // remove middle node (9)
3452 if ( N1[4]->NbInverseElements() == 0 )
3453 aMesh->RemoveNode( N1[4] );
3454 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3455 aMesh->RemoveNode( N1[6] );
3456 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3457 aMesh->RemoveNode( N2[6] );
3461 // Next element to fuse: the rejected one
3463 startElem = Ok12 ? tr3 : tr2;
3465 } // if ( startElem )
3466 } // while ( startElem || !startLinks.empty() )
3467 } // while ( ! mapEl_setLi.empty() )
3473 /*#define DUMPSO(txt) \
3474 // cout << txt << endl;
3475 //=============================================================================
3479 //=============================================================================
3480 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3484 int tmp = idNodes[ i1 ];
3485 idNodes[ i1 ] = idNodes[ i2 ];
3486 idNodes[ i2 ] = tmp;
3487 gp_Pnt Ptmp = P[ i1 ];
3490 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3493 //=======================================================================
3494 //function : SortQuadNodes
3495 //purpose : Set 4 nodes of a quadrangle face in a good order.
3496 // Swap 1<->2 or 2<->3 nodes and correspondingly return
3498 //=======================================================================
3500 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3505 for ( i = 0; i < 4; i++ ) {
3506 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3508 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3511 gp_Vec V1(P[0], P[1]);
3512 gp_Vec V2(P[0], P[2]);
3513 gp_Vec V3(P[0], P[3]);
3515 gp_Vec Cross1 = V1 ^ V2;
3516 gp_Vec Cross2 = V2 ^ V3;
3519 if (Cross1.Dot(Cross2) < 0)
3524 if (Cross1.Dot(Cross2) < 0)
3528 swap ( i, i + 1, idNodes, P );
3530 // for ( int ii = 0; ii < 4; ii++ ) {
3531 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3532 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3538 //=======================================================================
3539 //function : SortHexaNodes
3540 //purpose : Set 8 nodes of a hexahedron in a good order.
3541 // Return success status
3542 //=======================================================================
3544 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3549 DUMPSO( "INPUT: ========================================");
3550 for ( i = 0; i < 8; i++ ) {
3551 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3552 if ( !n ) return false;
3553 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3554 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3556 DUMPSO( "========================================");
3559 set<int> faceNodes; // ids of bottom face nodes, to be found
3560 set<int> checkedId1; // ids of tried 2-nd nodes
3561 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3562 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
3563 int iMin, iLoop1 = 0;
3565 // Loop to try the 2-nd nodes
3567 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3569 // Find not checked 2-nd node
3570 for ( i = 1; i < 8; i++ )
3571 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3572 int id1 = idNodes[i];
3573 swap ( 1, i, idNodes, P );
3574 checkedId1.insert ( id1 );
3578 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3579 // ie that all but meybe one (id3 which is on the same face) nodes
3580 // lay on the same side from the triangle plane.
3582 bool manyInPlane = false; // more than 4 nodes lay in plane
3584 while ( ++iLoop2 < 6 ) {
3586 // get 1-2-3 plane coeffs
3587 Standard_Real A, B, C, D;
3588 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3589 if ( N.SquareMagnitude() > gp::Resolution() )
3591 gp_Pln pln ( P[0], N );
3592 pln.Coefficients( A, B, C, D );
3594 // find the node (iMin) closest to pln
3595 Standard_Real dist[ 8 ], minDist = DBL_MAX;
3597 for ( i = 3; i < 8; i++ ) {
3598 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3599 if ( fabs( dist[i] ) < minDist ) {
3600 minDist = fabs( dist[i] );
3603 if ( fabs( dist[i] ) <= tol )
3604 idInPln.insert( idNodes[i] );
3607 // there should not be more than 4 nodes in bottom plane
3608 if ( idInPln.size() > 1 )
3610 DUMPSO( "### idInPln.size() = " << idInPln.size());
3611 // idInPlane does not contain the first 3 nodes
3612 if ( manyInPlane || idInPln.size() == 5)
3613 return false; // all nodes in one plane
3616 // set the 1-st node to be not in plane
3617 for ( i = 3; i < 8; i++ ) {
3618 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3619 DUMPSO( "### Reset 0-th node");
3620 swap( 0, i, idNodes, P );
3625 // reset to re-check second nodes
3626 leastDist = DBL_MAX;
3630 break; // from iLoop2;
3633 // check that the other 4 nodes are on the same side
3634 bool sameSide = true;
3635 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3636 for ( i = 3; sameSide && i < 8; i++ ) {
3638 sameSide = ( isNeg == dist[i] <= 0.);
3641 // keep best solution
3642 if ( sameSide && minDist < leastDist ) {
3643 leastDist = minDist;
3645 faceNodes.insert( idNodes[ 1 ] );
3646 faceNodes.insert( idNodes[ 2 ] );
3647 faceNodes.insert( idNodes[ iMin ] );
3648 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3649 << " leastDist = " << leastDist);
3650 if ( leastDist <= DBL_MIN )
3655 // set next 3-d node to check
3656 int iNext = 2 + iLoop2;
3658 DUMPSO( "Try 2-nd");
3659 swap ( 2, iNext, idNodes, P );
3661 } // while ( iLoop2 < 6 )
3664 if ( faceNodes.empty() ) return false;
3666 // Put the faceNodes in proper places
3667 for ( i = 4; i < 8; i++ ) {
3668 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3669 // find a place to put
3671 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3673 DUMPSO( "Set faceNodes");
3674 swap ( iTo, i, idNodes, P );
3679 // Set nodes of the found bottom face in good order
3680 DUMPSO( " Found bottom face: ");
3681 i = SortQuadNodes( theMesh, idNodes );
3683 gp_Pnt Ptmp = P[ i ];
3688 // for ( int ii = 0; ii < 4; ii++ ) {
3689 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3690 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3693 // Gravity center of the top and bottom faces
3694 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3695 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3697 // Get direction from the bottom to the top face
3698 gp_Vec upDir ( aGCb, aGCt );
3699 Standard_Real upDirSize = upDir.Magnitude();
3700 if ( upDirSize <= gp::Resolution() ) return false;
3703 // Assure that the bottom face normal points up
3704 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3705 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3706 if ( Nb.Dot( upDir ) < 0 ) {
3707 DUMPSO( "Reverse bottom face");
3708 swap( 1, 3, idNodes, P );
3711 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3712 Standard_Real minDist = DBL_MAX;
3713 for ( i = 4; i < 8; i++ ) {
3714 // projection of P[i] to the plane defined by P[0] and upDir
3715 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3716 Standard_Real sqDist = P[0].SquareDistance( Pp );
3717 if ( sqDist < minDist ) {
3722 DUMPSO( "Set 4-th");
3723 swap ( 4, iMin, idNodes, P );
3725 // Set nodes of the top face in good order
3726 DUMPSO( "Sort top face");
3727 i = SortQuadNodes( theMesh, &idNodes[4] );
3730 gp_Pnt Ptmp = P[ i ];
3735 // Assure that direction of the top face normal is from the bottom face
3736 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3737 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3738 if ( Nt.Dot( upDir ) < 0 ) {
3739 DUMPSO( "Reverse top face");
3740 swap( 5, 7, idNodes, P );
3743 // DUMPSO( "OUTPUT: ========================================");
3744 // for ( i = 0; i < 8; i++ ) {
3745 // float *p = ugrid->GetPoint(idNodes[i]);
3746 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3752 //================================================================================
3754 * \brief Return nodes linked to the given one
3755 * \param theNode - the node
3756 * \param linkedNodes - the found nodes
3757 * \param type - the type of elements to check
3759 * Medium nodes are ignored
3761 //================================================================================
3763 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3764 TIDSortedElemSet & linkedNodes,
3765 SMDSAbs_ElementType type )
3767 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3768 while ( elemIt->more() )
3770 const SMDS_MeshElement* elem = elemIt->next();
3771 if(elem->GetType() == SMDSAbs_0DElement)
3774 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3775 if ( elem->GetType() == SMDSAbs_Volume )
3777 SMDS_VolumeTool vol( elem );
3778 while ( nodeIt->more() ) {
3779 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3780 if ( theNode != n && vol.IsLinked( theNode, n ))
3781 linkedNodes.insert( n );
3786 for ( int i = 0; nodeIt->more(); ++i ) {
3787 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3788 if ( n == theNode ) {
3789 int iBefore = i - 1;
3791 if ( elem->IsQuadratic() ) {
3792 int nb = elem->NbNodes() / 2;
3793 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3794 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3796 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3797 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3804 //=======================================================================
3805 //function : laplacianSmooth
3806 //purpose : pulls theNode toward the center of surrounding nodes directly
3807 // connected to that node along an element edge
3808 //=======================================================================
3810 void laplacianSmooth(const SMDS_MeshNode* theNode,
3811 const Handle(Geom_Surface)& theSurface,
3812 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3814 // find surrounding nodes
3816 TIDSortedElemSet nodeSet;
3817 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3819 // compute new coodrs
3821 double coord[] = { 0., 0., 0. };
3822 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3823 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3824 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3825 if ( theSurface.IsNull() ) { // smooth in 3D
3826 coord[0] += node->X();
3827 coord[1] += node->Y();
3828 coord[2] += node->Z();
3830 else { // smooth in 2D
3831 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3832 gp_XY* uv = theUVMap[ node ];
3833 coord[0] += uv->X();
3834 coord[1] += uv->Y();
3837 int nbNodes = nodeSet.size();
3840 coord[0] /= nbNodes;
3841 coord[1] /= nbNodes;
3843 if ( !theSurface.IsNull() ) {
3844 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3845 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3846 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3852 coord[2] /= nbNodes;
3856 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3859 //=======================================================================
3860 //function : centroidalSmooth
3861 //purpose : pulls theNode toward the element-area-weighted centroid of the
3862 // surrounding elements
3863 //=======================================================================
3865 void centroidalSmooth(const SMDS_MeshNode* theNode,
3866 const Handle(Geom_Surface)& theSurface,
3867 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3869 gp_XYZ aNewXYZ(0.,0.,0.);
3870 SMESH::Controls::Area anAreaFunc;
3871 double totalArea = 0.;
3876 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3877 while ( elemIt->more() )
3879 const SMDS_MeshElement* elem = elemIt->next();
3882 gp_XYZ elemCenter(0.,0.,0.);
3883 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3884 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3885 int nn = elem->NbNodes();
3886 if(elem->IsQuadratic()) nn = nn/2;
3888 //while ( itN->more() ) {
3890 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3892 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3893 aNodePoints.push_back( aP );
3894 if ( !theSurface.IsNull() ) { // smooth in 2D
3895 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3896 gp_XY* uv = theUVMap[ aNode ];
3897 aP.SetCoord( uv->X(), uv->Y(), 0. );
3901 double elemArea = anAreaFunc.GetValue( aNodePoints );
3902 totalArea += elemArea;
3904 aNewXYZ += elemCenter * elemArea;
3906 aNewXYZ /= totalArea;
3907 if ( !theSurface.IsNull() ) {
3908 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3909 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3914 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3917 //=======================================================================
3918 //function : getClosestUV
3919 //purpose : return UV of closest projection
3920 //=======================================================================
3922 static bool getClosestUV (Extrema_GenExtPS& projector,
3923 const gp_Pnt& point,
3926 projector.Perform( point );
3927 if ( projector.IsDone() ) {
3928 double u, v, minVal = DBL_MAX;
3929 for ( int i = projector.NbExt(); i > 0; i-- )
3930 if ( projector.SquareDistance( i ) < minVal ) {
3931 minVal = projector.SquareDistance( i );
3932 projector.Point( i ).Parameter( u, v );
3934 result.SetCoord( u, v );
3940 //=======================================================================
3942 //purpose : Smooth theElements during theNbIterations or until a worst
3943 // element has aspect ratio <= theTgtAspectRatio.
3944 // Aspect Ratio varies in range [1.0, inf].
3945 // If theElements is empty, the whole mesh is smoothed.
3946 // theFixedNodes contains additionally fixed nodes. Nodes built
3947 // on edges and boundary nodes are always fixed.
3948 //=======================================================================
3950 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3951 set<const SMDS_MeshNode*> & theFixedNodes,
3952 const SmoothMethod theSmoothMethod,
3953 const int theNbIterations,
3954 double theTgtAspectRatio,
3957 myLastCreatedElems.Clear();
3958 myLastCreatedNodes.Clear();
3960 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3962 if ( theTgtAspectRatio < 1.0 )
3963 theTgtAspectRatio = 1.0;
3965 const double disttol = 1.e-16;
3967 SMESH::Controls::AspectRatio aQualityFunc;
3969 SMESHDS_Mesh* aMesh = GetMeshDS();
3971 if ( theElems.empty() ) {
3972 // add all faces to theElems
3973 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3974 while ( fIt->more() ) {
3975 const SMDS_MeshElement* face = fIt->next();
3976 theElems.insert( theElems.end(), face );
3979 // get all face ids theElems are on
3980 set< int > faceIdSet;
3981 TIDSortedElemSet::iterator itElem;
3983 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3984 int fId = FindShape( *itElem );
3985 // check that corresponding submesh exists and a shape is face
3987 faceIdSet.find( fId ) == faceIdSet.end() &&
3988 aMesh->MeshElements( fId )) {
3989 TopoDS_Shape F = aMesh->IndexToShape( fId );
3990 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3991 faceIdSet.insert( fId );
3994 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3996 // ===============================================
3997 // smooth elements on each TopoDS_Face separately
3998 // ===============================================
4000 SMESH_MesherHelper helper( *GetMesh() );
4002 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
4003 for ( ; fId != faceIdSet.rend(); ++fId )
4005 // get face surface and submesh
4006 Handle(Geom_Surface) surface;
4007 SMESHDS_SubMesh* faceSubMesh = 0;
4009 double fToler2 = 0, f,l;
4010 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
4011 bool isUPeriodic = false, isVPeriodic = false;
4014 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
4015 surface = BRep_Tool::Surface( face );
4016 faceSubMesh = aMesh->MeshElements( *fId );
4017 fToler2 = BRep_Tool::Tolerance( face );
4018 fToler2 *= fToler2 * 10.;
4019 isUPeriodic = surface->IsUPeriodic();
4022 isVPeriodic = surface->IsVPeriodic();
4025 surface->Bounds( u1, u2, v1, v2 );
4026 helper.SetSubShape( face );
4028 // ---------------------------------------------------------
4029 // for elements on a face, find movable and fixed nodes and
4030 // compute UV for them
4031 // ---------------------------------------------------------
4032 bool checkBoundaryNodes = false;
4033 bool isQuadratic = false;
4034 set<const SMDS_MeshNode*> setMovableNodes;
4035 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4036 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4037 list< const SMDS_MeshElement* > elemsOnFace;
4039 Extrema_GenExtPS projector;
4040 GeomAdaptor_Surface surfAdaptor;
4041 if ( !surface.IsNull() ) {
4042 surfAdaptor.Load( surface );
4043 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4045 int nbElemOnFace = 0;
4046 itElem = theElems.begin();
4047 // loop on not yet smoothed elements: look for elems on a face
4048 while ( itElem != theElems.end() )
4050 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4051 break; // all elements found
4053 const SMDS_MeshElement* elem = *itElem;
4054 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4055 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4059 elemsOnFace.push_back( elem );
4060 theElems.erase( itElem++ );
4064 isQuadratic = elem->IsQuadratic();
4066 // get movable nodes of elem
4067 const SMDS_MeshNode* node;
4068 SMDS_TypeOfPosition posType;
4069 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4070 int nn = 0, nbn = elem->NbNodes();
4071 if(elem->IsQuadratic())
4073 while ( nn++ < nbn ) {
4074 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4075 const SMDS_PositionPtr& pos = node->GetPosition();
4076 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4077 if (posType != SMDS_TOP_EDGE &&
4078 posType != SMDS_TOP_VERTEX &&
4079 theFixedNodes.find( node ) == theFixedNodes.end())
4081 // check if all faces around the node are on faceSubMesh
4082 // because a node on edge may be bound to face
4083 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4085 if ( faceSubMesh ) {
4086 while ( eIt->more() && all ) {
4087 const SMDS_MeshElement* e = eIt->next();
4088 all = faceSubMesh->Contains( e );
4092 setMovableNodes.insert( node );
4094 checkBoundaryNodes = true;
4096 if ( posType == SMDS_TOP_3DSPACE )
4097 checkBoundaryNodes = true;
4100 if ( surface.IsNull() )
4103 // get nodes to check UV
4104 list< const SMDS_MeshNode* > uvCheckNodes;
4105 const SMDS_MeshNode* nodeInFace = 0;
4106 itN = elem->nodesIterator();
4107 nn = 0; nbn = elem->NbNodes();
4108 if(elem->IsQuadratic())
4110 while ( nn++ < nbn ) {
4111 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4112 if ( node->GetPosition()->GetDim() == 2 )
4114 if ( uvMap.find( node ) == uvMap.end() )
4115 uvCheckNodes.push_back( node );
4116 // add nodes of elems sharing node
4117 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4118 // while ( eIt->more() ) {
4119 // const SMDS_MeshElement* e = eIt->next();
4120 // if ( e != elem ) {
4121 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4122 // while ( nIt->more() ) {
4123 // const SMDS_MeshNode* n =
4124 // static_cast<const SMDS_MeshNode*>( nIt->next() );
4125 // if ( uvMap.find( n ) == uvMap.end() )
4126 // uvCheckNodes.push_back( n );
4132 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4133 for ( ; n != uvCheckNodes.end(); ++n ) {
4136 const SMDS_PositionPtr& pos = node->GetPosition();
4137 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4141 bool toCheck = true;
4142 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4144 // compute not existing UV
4145 bool project = ( posType == SMDS_TOP_3DSPACE );
4146 // double dist1 = DBL_MAX, dist2 = 0;
4147 // if ( posType != SMDS_TOP_3DSPACE ) {
4148 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4149 // project = dist1 > fToler2;
4151 if ( project ) { // compute new UV
4153 gp_Pnt pNode = SMESH_TNodeXYZ( node );
4154 if ( !getClosestUV( projector, pNode, newUV )) {
4155 MESSAGE("Node Projection Failed " << node);
4159 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4161 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4163 // if ( posType != SMDS_TOP_3DSPACE )
4164 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4165 // if ( dist2 < dist1 )
4169 // store UV in the map
4170 listUV.push_back( uv );
4171 uvMap.insert( make_pair( node, &listUV.back() ));
4173 } // loop on not yet smoothed elements
4175 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4176 checkBoundaryNodes = true;
4178 // fix nodes on mesh boundary
4180 if ( checkBoundaryNodes ) {
4181 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4182 map< SMESH_TLink, int >::iterator link_nb;
4183 // put all elements links to linkNbMap
4184 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4185 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4186 const SMDS_MeshElement* elem = (*elemIt);
4187 int nbn = elem->NbCornerNodes();
4188 // loop on elem links: insert them in linkNbMap
4189 for ( int iN = 0; iN < nbn; ++iN ) {
4190 const SMDS_MeshNode* n1 = elem->GetNode( iN );
4191 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4192 SMESH_TLink link( n1, n2 );
4193 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4197 // remove nodes that are in links encountered only once from setMovableNodes
4198 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4199 if ( link_nb->second == 1 ) {
4200 setMovableNodes.erase( link_nb->first.node1() );
4201 setMovableNodes.erase( link_nb->first.node2() );
4206 // -----------------------------------------------------
4207 // for nodes on seam edge, compute one more UV ( uvMap2 );
4208 // find movable nodes linked to nodes on seam and which
4209 // are to be smoothed using the second UV ( uvMap2 )
4210 // -----------------------------------------------------
4212 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4213 if ( !surface.IsNull() ) {
4214 TopExp_Explorer eExp( face, TopAbs_EDGE );
4215 for ( ; eExp.More(); eExp.Next() ) {
4216 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4217 if ( !BRep_Tool::IsClosed( edge, face ))
4219 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4220 if ( !sm ) continue;
4221 // find out which parameter varies for a node on seam
4224 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4225 if ( pcurve.IsNull() ) continue;
4226 uv1 = pcurve->Value( f );
4228 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4229 if ( pcurve.IsNull() ) continue;
4230 uv2 = pcurve->Value( f );
4231 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4233 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4234 std::swap( uv1, uv2 );
4235 // get nodes on seam and its vertices
4236 list< const SMDS_MeshNode* > seamNodes;
4237 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4238 while ( nSeamIt->more() ) {
4239 const SMDS_MeshNode* node = nSeamIt->next();
4240 if ( !isQuadratic || !IsMedium( node ))
4241 seamNodes.push_back( node );
4243 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4244 for ( ; vExp.More(); vExp.Next() ) {
4245 sm = aMesh->MeshElements( vExp.Current() );
4247 nSeamIt = sm->GetNodes();
4248 while ( nSeamIt->more() )
4249 seamNodes.push_back( nSeamIt->next() );
4252 // loop on nodes on seam
4253 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4254 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4255 const SMDS_MeshNode* nSeam = *noSeIt;
4256 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4257 if ( n_uv == uvMap.end() )
4260 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4261 // set the second UV
4262 listUV.push_back( *n_uv->second );
4263 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4264 if ( uvMap2.empty() )
4265 uvMap2 = uvMap; // copy the uvMap contents
4266 uvMap2[ nSeam ] = &listUV.back();
4268 // collect movable nodes linked to ones on seam in nodesNearSeam
4269 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4270 while ( eIt->more() ) {
4271 const SMDS_MeshElement* e = eIt->next();
4272 int nbUseMap1 = 0, nbUseMap2 = 0;
4273 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4274 int nn = 0, nbn = e->NbNodes();
4275 if(e->IsQuadratic()) nbn = nbn/2;
4276 while ( nn++ < nbn )
4278 const SMDS_MeshNode* n =
4279 static_cast<const SMDS_MeshNode*>( nIt->next() );
4281 setMovableNodes.find( n ) == setMovableNodes.end() )
4283 // add only nodes being closer to uv2 than to uv1
4284 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4285 // 0.5 * ( n->Y() + nSeam->Y() ),
4286 // 0.5 * ( n->Z() + nSeam->Z() ));
4288 // getClosestUV( projector, pMid, uv );
4289 double x = uvMap[ n ]->Coord( iPar );
4290 if ( Abs( uv1.Coord( iPar ) - x ) >
4291 Abs( uv2.Coord( iPar ) - x )) {
4292 nodesNearSeam.insert( n );
4298 // for centroidalSmooth all element nodes must
4299 // be on one side of a seam
4300 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4301 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4303 while ( nn++ < nbn ) {
4304 const SMDS_MeshNode* n =
4305 static_cast<const SMDS_MeshNode*>( nIt->next() );
4306 setMovableNodes.erase( n );
4310 } // loop on nodes on seam
4311 } // loop on edge of a face
4312 } // if ( !face.IsNull() )
4314 if ( setMovableNodes.empty() ) {
4315 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4316 continue; // goto next face
4324 double maxRatio = -1., maxDisplacement = -1.;
4325 set<const SMDS_MeshNode*>::iterator nodeToMove;
4326 for ( it = 0; it < theNbIterations; it++ ) {
4327 maxDisplacement = 0.;
4328 nodeToMove = setMovableNodes.begin();
4329 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4330 const SMDS_MeshNode* node = (*nodeToMove);
4331 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4334 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4335 if ( theSmoothMethod == LAPLACIAN )
4336 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4338 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4340 // node displacement
4341 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4342 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4343 if ( aDispl > maxDisplacement )
4344 maxDisplacement = aDispl;
4346 // no node movement => exit
4347 //if ( maxDisplacement < 1.e-16 ) {
4348 if ( maxDisplacement < disttol ) {
4349 MESSAGE("-- no node movement --");
4353 // check elements quality
4355 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4356 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4357 const SMDS_MeshElement* elem = (*elemIt);
4358 if ( !elem || elem->GetType() != SMDSAbs_Face )
4360 SMESH::Controls::TSequenceOfXYZ aPoints;
4361 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4362 double aValue = aQualityFunc.GetValue( aPoints );
4363 if ( aValue > maxRatio )
4367 if ( maxRatio <= theTgtAspectRatio ) {
4368 MESSAGE("-- quality achived --");
4371 if (it+1 == theNbIterations) {
4372 MESSAGE("-- Iteration limit exceeded --");
4374 } // smoothing iterations
4376 MESSAGE(" Face id: " << *fId <<
4377 " Nb iterstions: " << it <<
4378 " Displacement: " << maxDisplacement <<
4379 " Aspect Ratio " << maxRatio);
4381 // ---------------------------------------
4382 // new nodes positions are computed,
4383 // record movement in DS and set new UV
4384 // ---------------------------------------
4385 nodeToMove = setMovableNodes.begin();
4386 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4387 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4388 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4389 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4390 if ( node_uv != uvMap.end() ) {
4391 gp_XY* uv = node_uv->second;
4393 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4397 // move medium nodes of quadratic elements
4400 vector<const SMDS_MeshNode*> nodes;
4402 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4403 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4405 const SMDS_MeshElement* QF = *elemIt;
4406 if ( QF->IsQuadratic() )
4408 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4409 SMDS_MeshElement::iterator() );
4410 nodes.push_back( nodes[0] );
4412 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4414 if ( !surface.IsNull() )
4416 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4417 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4418 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4419 xyz = surface->Value( uv.X(), uv.Y() );
4422 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4424 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4425 // we have to move a medium node
4426 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4432 } // loop on face ids
4438 //=======================================================================
4439 //function : isReverse
4440 //purpose : Return true if normal of prevNodes is not co-directied with
4441 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4442 // iNotSame is where prevNodes and nextNodes are different.
4443 // If result is true then future volume orientation is OK
4444 //=======================================================================
4446 bool isReverse(const SMDS_MeshElement* face,
4447 const vector<const SMDS_MeshNode*>& prevNodes,
4448 const vector<const SMDS_MeshNode*>& nextNodes,
4452 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4453 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4454 gp_XYZ extrDir( pN - pP ), faceNorm;
4455 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4457 return faceNorm * extrDir < 0.0;
4460 //================================================================================
4462 * \brief Assure that theElemSets[0] holds elements, not nodes
4464 //================================================================================
4466 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4468 if ( !theElemSets[0].empty() &&
4469 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4471 std::swap( theElemSets[0], theElemSets[1] );
4473 else if ( !theElemSets[1].empty() &&
4474 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4476 std::swap( theElemSets[0], theElemSets[1] );
4481 //=======================================================================
4483 * \brief Create elements by sweeping an element
4484 * \param elem - element to sweep
4485 * \param newNodesItVec - nodes generated from each node of the element
4486 * \param newElems - generated elements
4487 * \param nbSteps - number of sweeping steps
4488 * \param srcElements - to append elem for each generated element
4490 //=======================================================================
4492 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4493 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4494 list<const SMDS_MeshElement*>& newElems,
4496 SMESH_SequenceOfElemPtr& srcElements)
4498 //MESSAGE("sweepElement " << nbSteps);
4499 SMESHDS_Mesh* aMesh = GetMeshDS();
4501 const int nbNodes = elem->NbNodes();
4502 const int nbCorners = elem->NbCornerNodes();
4503 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4504 polyhedron creation !!! */
4505 // Loop on elem nodes:
4506 // find new nodes and detect same nodes indices
4507 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4508 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4509 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4510 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4512 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4513 vector<int> sames(nbNodes);
4514 vector<bool> isSingleNode(nbNodes);
4516 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4517 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4518 const SMDS_MeshNode* node = nnIt->first;
4519 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4520 if ( listNewNodes.empty() )
4523 itNN [ iNode ] = listNewNodes.begin();
4524 prevNod[ iNode ] = node;
4525 nextNod[ iNode ] = listNewNodes.front();
4527 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4528 corner node of linear */
4529 if ( prevNod[ iNode ] != nextNod [ iNode ])
4530 nbDouble += !isSingleNode[iNode];
4532 if( iNode < nbCorners ) { // check corners only
4533 if ( prevNod[ iNode ] == nextNod [ iNode ])
4534 sames[nbSame++] = iNode;
4536 iNotSameNode = iNode;
4540 if ( nbSame == nbNodes || nbSame > 2) {
4541 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4545 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4547 // fix nodes order to have bottom normal external
4548 if ( baseType == SMDSEntity_Polygon )
4550 std::reverse( itNN.begin(), itNN.end() );
4551 std::reverse( prevNod.begin(), prevNod.end() );
4552 std::reverse( midlNod.begin(), midlNod.end() );
4553 std::reverse( nextNod.begin(), nextNod.end() );
4554 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4558 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4559 SMDS_MeshCell::applyInterlace( ind, itNN );
4560 SMDS_MeshCell::applyInterlace( ind, prevNod );
4561 SMDS_MeshCell::applyInterlace( ind, nextNod );
4562 SMDS_MeshCell::applyInterlace( ind, midlNod );
4563 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4566 sames[nbSame] = iNotSameNode;
4567 for ( int j = 0; j <= nbSame; ++j )
4568 for ( size_t i = 0; i < ind.size(); ++i )
4569 if ( ind[i] == sames[j] )
4574 iNotSameNode = sames[nbSame];
4578 else if ( elem->GetType() == SMDSAbs_Edge )
4580 // orient a new face same as adjacent one
4582 const SMDS_MeshElement* e;
4583 TIDSortedElemSet dummy;
4584 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4585 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4586 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4588 // there is an adjacent face, check order of nodes in it
4589 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4592 std::swap( itNN[0], itNN[1] );
4593 std::swap( prevNod[0], prevNod[1] );
4594 std::swap( nextNod[0], nextNod[1] );
4595 isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4597 sames[0] = 1 - sames[0];
4598 iNotSameNode = 1 - iNotSameNode;
4603 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4605 iSameNode = sames[ nbSame-1 ];
4606 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4607 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4608 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4611 if ( baseType == SMDSEntity_Polygon )
4613 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4614 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4616 else if ( baseType == SMDSEntity_Quad_Polygon )
4618 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4619 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4622 // make new elements
4623 for (int iStep = 0; iStep < nbSteps; iStep++ )
4626 for ( iNode = 0; iNode < nbNodes; iNode++ )
4628 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4629 nextNod[ iNode ] = *itNN[ iNode ]++;
4632 SMDS_MeshElement* aNewElem = 0;
4633 /*if(!elem->IsPoly())*/ {
4634 switch ( baseType ) {
4636 case SMDSEntity_Node: { // sweep NODE
4637 if ( nbSame == 0 ) {
4638 if ( isSingleNode[0] )
4639 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4641 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4647 case SMDSEntity_Edge: { // sweep EDGE
4648 if ( nbDouble == 0 )
4650 if ( nbSame == 0 ) // ---> quadrangle
4651 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4652 nextNod[ 1 ], nextNod[ 0 ] );
4653 else // ---> triangle
4654 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4655 nextNod[ iNotSameNode ] );
4657 else // ---> polygon
4659 vector<const SMDS_MeshNode*> poly_nodes;
4660 poly_nodes.push_back( prevNod[0] );
4661 poly_nodes.push_back( prevNod[1] );
4662 if ( prevNod[1] != nextNod[1] )
4664 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4665 poly_nodes.push_back( nextNod[1] );
4667 if ( prevNod[0] != nextNod[0] )
4669 poly_nodes.push_back( nextNod[0] );
4670 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4672 switch ( poly_nodes.size() ) {
4674 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4677 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4678 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4681 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4686 case SMDSEntity_Triangle: // TRIANGLE --->
4688 if ( nbDouble > 0 ) break;
4689 if ( nbSame == 0 ) // ---> pentahedron
4690 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4691 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4693 else if ( nbSame == 1 ) // ---> pyramid
4694 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4695 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4696 nextNod[ iSameNode ]);
4698 else // 2 same nodes: ---> tetrahedron
4699 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4700 nextNod[ iNotSameNode ]);
4703 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4707 if ( nbDouble+nbSame == 2 )
4709 if(nbSame==0) { // ---> quadratic quadrangle
4710 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4711 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4713 else { //(nbSame==1) // ---> quadratic triangle
4715 return; // medium node on axis
4717 else if(sames[0]==0)
4718 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4719 prevNod[2], midlNod[1], nextNod[2] );
4721 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4722 prevNod[2], nextNod[2], midlNod[0]);
4725 else if ( nbDouble == 3 )
4727 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4728 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4729 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4736 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4737 if ( nbDouble > 0 ) break;
4739 if ( nbSame == 0 ) // ---> hexahedron
4740 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4741 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4743 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4744 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4745 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4746 nextNod[ iSameNode ]);
4747 newElems.push_back( aNewElem );
4748 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4749 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4750 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4752 else if ( nbSame == 2 ) { // ---> pentahedron
4753 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4754 // iBeforeSame is same too
4755 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4756 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4757 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4759 // iAfterSame is same too
4760 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4761 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4762 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4766 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4767 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4768 if ( nbDouble+nbSame != 3 ) break;
4770 // ---> pentahedron with 15 nodes
4771 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4772 nextNod[0], nextNod[1], nextNod[2],
4773 prevNod[3], prevNod[4], prevNod[5],
4774 nextNod[3], nextNod[4], nextNod[5],
4775 midlNod[0], midlNod[1], midlNod[2]);
4777 else if(nbSame==1) {
4778 // ---> 2d order pyramid of 13 nodes
4779 int apex = iSameNode;
4780 int i0 = ( apex + 1 ) % nbCorners;
4781 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4785 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4786 nextNod[i0], nextNod[i1], prevNod[apex],
4787 prevNod[i01], midlNod[i0],
4788 nextNod[i01], midlNod[i1],
4789 prevNod[i1a], prevNod[i0a],
4790 nextNod[i0a], nextNod[i1a]);
4792 else if(nbSame==2) {
4793 // ---> 2d order tetrahedron of 10 nodes
4794 int n1 = iNotSameNode;
4795 int n2 = ( n1 + 1 ) % nbCorners;
4796 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4800 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4801 prevNod[n12], prevNod[n23], prevNod[n31],
4802 midlNod[n1], nextNod[n12], nextNod[n31]);
4806 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4808 if ( nbDouble != 4 ) break;
4809 // ---> hexahedron with 20 nodes
4810 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4811 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4812 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4813 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4814 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4816 else if(nbSame==1) {
4817 // ---> pyramid + pentahedron - can not be created since it is needed
4818 // additional middle node at the center of face
4819 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4822 else if( nbSame == 2 ) {
4823 if ( nbDouble != 2 ) break;
4824 // ---> 2d order Pentahedron with 15 nodes
4826 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4827 // iBeforeSame is same too
4834 // iAfterSame is same too
4844 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4845 prevNod[n4], prevNod[n5], nextNod[n5],
4846 prevNod[n12], midlNod[n2], nextNod[n12],
4847 prevNod[n45], midlNod[n5], nextNod[n45],
4848 prevNod[n14], prevNod[n25], nextNod[n25]);
4852 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4854 if( nbSame == 0 && nbDouble == 9 ) {
4855 // ---> tri-quadratic hexahedron with 27 nodes
4856 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4857 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4858 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4859 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4860 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4861 prevNod[8], // bottom center
4862 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4863 nextNod[8], // top center
4864 midlNod[8]);// elem center
4872 case SMDSEntity_Polygon: { // sweep POLYGON
4874 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4875 // ---> hexagonal prism
4876 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4877 prevNod[3], prevNod[4], prevNod[5],
4878 nextNod[0], nextNod[1], nextNod[2],
4879 nextNod[3], nextNod[4], nextNod[5]);
4883 case SMDSEntity_Ball:
4888 } // switch ( baseType )
4891 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4893 if ( baseType != SMDSEntity_Polygon )
4895 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4896 SMDS_MeshCell::applyInterlace( ind, prevNod );
4897 SMDS_MeshCell::applyInterlace( ind, nextNod );
4898 SMDS_MeshCell::applyInterlace( ind, midlNod );
4899 SMDS_MeshCell::applyInterlace( ind, itNN );
4900 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4901 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4903 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4904 vector<int> quantities (nbNodes + 2);
4905 polyedre_nodes.clear();
4909 for (int inode = 0; inode < nbNodes; inode++)
4910 polyedre_nodes.push_back( prevNod[inode] );
4911 quantities.push_back( nbNodes );
4914 polyedre_nodes.push_back( nextNod[0] );
4915 for (int inode = nbNodes; inode-1; --inode )
4916 polyedre_nodes.push_back( nextNod[inode-1] );
4917 quantities.push_back( nbNodes );
4925 const int iQuad = elem->IsQuadratic();
4926 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4928 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4929 int inextface = (iface+1+iQuad) % nbNodes;
4930 int imid = (iface+1) % nbNodes;
4931 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4932 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4933 polyedre_nodes.push_back( prevNod[iface] ); // 1
4934 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4936 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4937 polyedre_nodes.push_back( nextNod[iface] ); // 2
4939 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4940 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4942 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4943 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4945 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4946 if ( nbFaceNodes > 2 )
4947 quantities.push_back( nbFaceNodes );
4948 else // degenerated face
4949 polyedre_nodes.resize( prevNbNodes );
4951 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4953 } // try to create a polyherdal prism
4956 newElems.push_back( aNewElem );
4957 myLastCreatedElems.Append(aNewElem);
4958 srcElements.Append( elem );
4961 // set new prev nodes
4962 for ( iNode = 0; iNode < nbNodes; iNode++ )
4963 prevNod[ iNode ] = nextNod[ iNode ];
4968 //=======================================================================
4970 * \brief Create 1D and 2D elements around swept elements
4971 * \param mapNewNodes - source nodes and ones generated from them
4972 * \param newElemsMap - source elements and ones generated from them
4973 * \param elemNewNodesMap - nodes generated from each node of each element
4974 * \param elemSet - all swept elements
4975 * \param nbSteps - number of sweeping steps
4976 * \param srcElements - to append elem for each generated element
4978 //=======================================================================
4980 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4981 TTElemOfElemListMap & newElemsMap,
4982 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4983 TIDSortedElemSet& elemSet,
4985 SMESH_SequenceOfElemPtr& srcElements)
4987 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4988 SMESHDS_Mesh* aMesh = GetMeshDS();
4990 // Find nodes belonging to only one initial element - sweep them into edges.
4992 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4993 for ( ; nList != mapNewNodes.end(); nList++ )
4995 const SMDS_MeshNode* node =
4996 static_cast<const SMDS_MeshNode*>( nList->first );
4997 if ( newElemsMap.count( node ))
4998 continue; // node was extruded into edge
4999 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
5000 int nbInitElems = 0;
5001 const SMDS_MeshElement* el = 0;
5002 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
5003 while ( eIt->more() && nbInitElems < 2 ) {
5004 const SMDS_MeshElement* e = eIt->next();
5005 SMDSAbs_ElementType type = e->GetType();
5006 if ( type == SMDSAbs_Volume || type < highType ) continue;
5007 if ( type > highType ) {
5012 nbInitElems += elemSet.count(el);
5014 if ( nbInitElems == 1 ) {
5015 bool NotCreateEdge = el && el->IsMediumNode(node);
5016 if(!NotCreateEdge) {
5017 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5018 list<const SMDS_MeshElement*> newEdges;
5019 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5024 // Make a ceiling for each element ie an equal element of last new nodes.
5025 // Find free links of faces - make edges and sweep them into faces.
5027 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5029 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
5030 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5031 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5033 const SMDS_MeshElement* elem = itElem->first;
5034 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5036 if(itElem->second.size()==0) continue;
5038 const bool isQuadratic = elem->IsQuadratic();
5040 if ( elem->GetType() == SMDSAbs_Edge ) {
5041 // create a ceiling edge
5042 if ( !isQuadratic ) {
5043 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5044 vecNewNodes[ 1 ]->second.back())) {
5045 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5046 vecNewNodes[ 1 ]->second.back()));
5047 srcElements.Append( elem );
5051 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5052 vecNewNodes[ 1 ]->second.back(),
5053 vecNewNodes[ 2 ]->second.back())) {
5054 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5055 vecNewNodes[ 1 ]->second.back(),
5056 vecNewNodes[ 2 ]->second.back()));
5057 srcElements.Append( elem );
5061 if ( elem->GetType() != SMDSAbs_Face )
5064 bool hasFreeLinks = false;
5066 TIDSortedElemSet avoidSet;
5067 avoidSet.insert( elem );
5069 set<const SMDS_MeshNode*> aFaceLastNodes;
5070 int iNode, nbNodes = vecNewNodes.size();
5071 if ( !isQuadratic ) {
5072 // loop on the face nodes
5073 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5074 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5075 // look for free links of the face
5076 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5077 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5078 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5079 // check if a link n1-n2 is free
5080 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5081 hasFreeLinks = true;
5082 // make a new edge and a ceiling for a new edge
5083 const SMDS_MeshElement* edge;
5084 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5085 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5086 srcElements.Append( myLastCreatedElems.Last() );
5088 n1 = vecNewNodes[ iNode ]->second.back();
5089 n2 = vecNewNodes[ iNext ]->second.back();
5090 if ( !aMesh->FindEdge( n1, n2 )) {
5091 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5092 srcElements.Append( edge );
5097 else { // elem is quadratic face
5098 int nbn = nbNodes/2;
5099 for ( iNode = 0; iNode < nbn; iNode++ ) {
5100 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5101 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5102 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5103 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5104 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5105 // check if a link is free
5106 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5107 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5108 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5109 hasFreeLinks = true;
5110 // make an edge and a ceiling for a new edge
5112 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5113 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5114 srcElements.Append( elem );
5116 n1 = vecNewNodes[ iNode ]->second.back();
5117 n2 = vecNewNodes[ iNext ]->second.back();
5118 n3 = vecNewNodes[ iNode+nbn ]->second.back();
5119 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5120 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5121 srcElements.Append( elem );
5125 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5126 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5130 // sweep free links into faces
5132 if ( hasFreeLinks ) {
5133 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5134 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5136 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5137 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5138 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5139 initNodeSet.insert( vecNewNodes[ iNode ]->first );
5140 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5142 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
5143 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5144 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5146 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5147 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5148 std::advance( v, volNb );
5149 // find indices of free faces of a volume and their source edges
5150 list< int > freeInd;
5151 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5152 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5153 int iF, nbF = vTool.NbFaces();
5154 for ( iF = 0; iF < nbF; iF ++ ) {
5155 if (vTool.IsFreeFace( iF ) &&
5156 vTool.GetFaceNodes( iF, faceNodeSet ) &&
5157 initNodeSet != faceNodeSet) // except an initial face
5159 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5161 if ( faceNodeSet == initNodeSetNoCenter )
5163 freeInd.push_back( iF );
5164 // find source edge of a free face iF
5165 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5166 vector<const SMDS_MeshNode*>::iterator lastCommom;
5167 commonNodes.resize( nbNodes, 0 );
5168 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5169 initNodeSet.begin(), initNodeSet.end(),
5170 commonNodes.begin());
5171 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5172 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5174 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5176 if ( !srcEdges.back() )
5178 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5179 << iF << " of volume #" << vTool.ID() << endl;
5184 if ( freeInd.empty() )
5187 // create wall faces for all steps;
5188 // if such a face has been already created by sweep of edge,
5189 // assure that its orientation is OK
5190 for ( int iStep = 0; iStep < nbSteps; iStep++ )
5192 vTool.Set( *v, /*ignoreCentralNodes=*/false );
5193 vTool.SetExternalNormal();
5194 const int nextShift = vTool.IsForward() ? +1 : -1;
5195 list< int >::iterator ind = freeInd.begin();
5196 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5197 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5199 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5200 int nbn = vTool.NbFaceNodes( *ind );
5201 const SMDS_MeshElement * f = 0;
5202 if ( nbn == 3 ) ///// triangle
5204 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5206 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5208 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5210 nodes[ 1 + nextShift ] };
5212 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5214 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5218 else if ( nbn == 4 ) ///// quadrangle
5220 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5222 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5224 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5225 nodes[ 2 ], nodes[ 2+nextShift ] };
5227 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5229 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5230 newOrder[ 2 ], newOrder[ 3 ]));
5233 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5235 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5237 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5239 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5241 nodes[2 + 2*nextShift],
5242 nodes[3 - 2*nextShift],
5244 nodes[3 + 2*nextShift]};
5246 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5248 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5256 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5258 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5259 nodes[1], nodes[3], nodes[5], nodes[7] );
5261 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5263 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5264 nodes[4 - 2*nextShift],
5266 nodes[4 + 2*nextShift],
5268 nodes[5 - 2*nextShift],
5270 nodes[5 + 2*nextShift] };
5272 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5274 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5275 newOrder[ 2 ], newOrder[ 3 ],
5276 newOrder[ 4 ], newOrder[ 5 ],
5277 newOrder[ 6 ], newOrder[ 7 ]));
5280 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5282 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5283 SMDSAbs_Face, /*noMedium=*/false);
5285 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5287 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5288 nodes[4 - 2*nextShift],
5290 nodes[4 + 2*nextShift],
5292 nodes[5 - 2*nextShift],
5294 nodes[5 + 2*nextShift],
5297 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5299 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5300 newOrder[ 2 ], newOrder[ 3 ],
5301 newOrder[ 4 ], newOrder[ 5 ],
5302 newOrder[ 6 ], newOrder[ 7 ],
5306 else //////// polygon
5308 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5309 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5311 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5313 if ( !vTool.IsForward() )
5314 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5316 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5318 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5322 while ( srcElements.Length() < myLastCreatedElems.Length() )
5323 srcElements.Append( *srcEdge );
5325 } // loop on free faces
5327 // go to the next volume
5329 while ( iVol++ < nbVolumesByStep ) v++;
5332 } // loop on volumes of one step
5333 } // sweep free links into faces
5335 // Make a ceiling face with a normal external to a volume
5337 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5338 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5339 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5341 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5342 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5343 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5347 lastVol.SetExternalNormal();
5348 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5349 const int nbn = lastVol.NbFaceNodes( iF );
5350 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5351 if ( !hasFreeLinks ||
5352 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5354 const vector<int>& interlace =
5355 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5356 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5358 AddElement( nodeVec, anyFace.Init( elem ));
5360 while ( srcElements.Length() < myLastCreatedElems.Length() )
5361 srcElements.Append( elem );
5364 } // loop on swept elements
5367 //=======================================================================
5368 //function : RotationSweep
5370 //=======================================================================
5372 SMESH_MeshEditor::PGroupIDs
5373 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5374 const gp_Ax1& theAxis,
5375 const double theAngle,
5376 const int theNbSteps,
5377 const double theTol,
5378 const bool theMakeGroups,
5379 const bool theMakeWalls)
5381 myLastCreatedElems.Clear();
5382 myLastCreatedNodes.Clear();
5384 // source elements for each generated one
5385 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5387 MESSAGE( "RotationSweep()");
5389 aTrsf.SetRotation( theAxis, theAngle );
5391 aTrsf2.SetRotation( theAxis, theAngle/2. );
5393 gp_Lin aLine( theAxis );
5394 double aSqTol = theTol * theTol;
5396 SMESHDS_Mesh* aMesh = GetMeshDS();
5398 TNodeOfNodeListMap mapNewNodes;
5399 TElemOfVecOfNnlmiMap mapElemNewNodes;
5400 TTElemOfElemListMap newElemsMap;
5402 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5403 myMesh->NbFaces(ORDER_QUADRATIC) +
5404 myMesh->NbVolumes(ORDER_QUADRATIC) );
5405 // loop on theElemSets
5406 setElemsFirst( theElemSets );
5407 TIDSortedElemSet::iterator itElem;
5408 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5410 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5411 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5412 const SMDS_MeshElement* elem = *itElem;
5413 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5415 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5416 newNodesItVec.reserve( elem->NbNodes() );
5418 // loop on elem nodes
5419 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5420 while ( itN->more() )
5422 const SMDS_MeshNode* node = cast2Node( itN->next() );
5424 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5426 aXYZ.Coord( coord[0], coord[1], coord[2] );
5427 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5429 // check if a node has been already sweeped
5430 TNodeOfNodeListMapItr nIt =
5431 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5432 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5433 if ( listNewNodes.empty() )
5435 // check if we are to create medium nodes between corner ones
5436 bool needMediumNodes = false;
5437 if ( isQuadraticMesh )
5439 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5440 while (it->more() && !needMediumNodes )
5442 const SMDS_MeshElement* invElem = it->next();
5443 if ( invElem != elem && !theElems.count( invElem )) continue;
5444 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5445 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5446 needMediumNodes = true;
5451 const SMDS_MeshNode * newNode = node;
5452 for ( int i = 0; i < theNbSteps; i++ ) {
5454 if ( needMediumNodes ) // create a medium node
5456 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5457 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5458 myLastCreatedNodes.Append(newNode);
5459 srcNodes.Append( node );
5460 listNewNodes.push_back( newNode );
5461 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5464 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5466 // create a corner node
5467 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5468 myLastCreatedNodes.Append(newNode);
5469 srcNodes.Append( node );
5470 listNewNodes.push_back( newNode );
5473 listNewNodes.push_back( newNode );
5474 // if ( needMediumNodes )
5475 // listNewNodes.push_back( newNode );
5479 newNodesItVec.push_back( nIt );
5481 // make new elements
5482 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5487 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5489 PGroupIDs newGroupIDs;
5490 if ( theMakeGroups )
5491 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5496 //=======================================================================
5497 //function : ExtrusParam
5498 //purpose : standard construction
5499 //=======================================================================
5501 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5502 const int theNbSteps,
5504 const double theTolerance):
5506 myFlags( theFlags ),
5507 myTolerance( theTolerance ),
5508 myElemsToUse( NULL )
5510 mySteps = new TColStd_HSequenceOfReal;
5511 const double stepSize = theStep.Magnitude();
5512 for (int i=1; i<=theNbSteps; i++ )
5513 mySteps->Append( stepSize );
5515 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5516 ( theTolerance > 0 ))
5518 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5522 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5526 //=======================================================================
5527 //function : ExtrusParam
5528 //purpose : steps are given explicitly
5529 //=======================================================================
5531 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5532 Handle(TColStd_HSequenceOfReal) theSteps,
5534 const double theTolerance):
5536 mySteps( theSteps ),
5537 myFlags( theFlags ),
5538 myTolerance( theTolerance ),
5539 myElemsToUse( NULL )
5541 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5542 ( theTolerance > 0 ))
5544 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5548 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5552 //=======================================================================
5553 //function : ExtrusParam
5554 //purpose : for extrusion by normal
5555 //=======================================================================
5557 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5558 const int theNbSteps,
5562 mySteps( new TColStd_HSequenceOfReal ),
5563 myFlags( theFlags ),
5565 myElemsToUse( NULL )
5567 for (int i = 0; i < theNbSteps; i++ )
5568 mySteps->Append( theStepSize );
5572 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5576 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5580 //=======================================================================
5581 //function : ExtrusParam::SetElementsToUse
5582 //purpose : stores elements to use for extrusion by normal, depending on
5583 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5584 //=======================================================================
5586 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5588 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5591 //=======================================================================
5592 //function : ExtrusParam::beginStepIter
5593 //purpose : prepare iteration on steps
5594 //=======================================================================
5596 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5598 myWithMediumNodes = withMediumNodes;
5602 //=======================================================================
5603 //function : ExtrusParam::moreSteps
5604 //purpose : are there more steps?
5605 //=======================================================================
5607 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5609 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5611 //=======================================================================
5612 //function : ExtrusParam::nextStep
5613 //purpose : returns the next step
5614 //=======================================================================
5616 double SMESH_MeshEditor::ExtrusParam::nextStep()
5619 if ( !myCurSteps.empty() )
5621 res = myCurSteps.back();
5622 myCurSteps.pop_back();
5624 else if ( myNextStep <= mySteps->Length() )
5626 myCurSteps.push_back( mySteps->Value( myNextStep ));
5628 if ( myWithMediumNodes )
5630 myCurSteps.back() /= 2.;
5631 myCurSteps.push_back( myCurSteps.back() );
5638 //=======================================================================
5639 //function : ExtrusParam::makeNodesByDir
5640 //purpose : create nodes for standard extrusion
5641 //=======================================================================
5643 int SMESH_MeshEditor::ExtrusParam::
5644 makeNodesByDir( SMESHDS_Mesh* mesh,
5645 const SMDS_MeshNode* srcNode,
5646 std::list<const SMDS_MeshNode*> & newNodes,
5647 const bool makeMediumNodes)
5649 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5652 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5654 p += myDir.XYZ() * nextStep();
5655 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5656 newNodes.push_back( newNode );
5661 //=======================================================================
5662 //function : ExtrusParam::makeNodesByDirAndSew
5663 //purpose : create nodes for standard extrusion with sewing
5664 //=======================================================================
5666 int SMESH_MeshEditor::ExtrusParam::
5667 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5668 const SMDS_MeshNode* srcNode,
5669 std::list<const SMDS_MeshNode*> & newNodes,
5670 const bool makeMediumNodes)
5672 gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5675 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5677 P1 += myDir.XYZ() * nextStep();
5679 // try to search in sequence of existing nodes
5680 // if myNodes.Length()>0 we 'nave to use given sequence
5681 // else - use all nodes of mesh
5682 const SMDS_MeshNode * node = 0;
5683 if ( myNodes.Length() > 0 ) {
5685 for(i=1; i<=myNodes.Length(); i++) {
5686 gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5687 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5689 node = myNodes.Value(i);
5695 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5696 while(itn->more()) {
5697 SMESH_TNodeXYZ P2( itn->next() );
5698 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5707 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5709 newNodes.push_back( node );
5716 //=======================================================================
5717 //function : ExtrusParam::makeNodesByNormal2D
5718 //purpose : create nodes for extrusion using normals of faces
5719 //=======================================================================
5721 int SMESH_MeshEditor::ExtrusParam::
5722 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5723 const SMDS_MeshNode* srcNode,
5724 std::list<const SMDS_MeshNode*> & newNodes,
5725 const bool makeMediumNodes)
5727 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5729 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5731 // get normals to faces sharing srcNode
5732 vector< gp_XYZ > norms, baryCenters;
5733 gp_XYZ norm, avgNorm( 0,0,0 );
5734 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5735 while ( faceIt->more() )
5737 const SMDS_MeshElement* face = faceIt->next();
5738 if ( myElemsToUse && !myElemsToUse->count( face ))
5740 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5742 norms.push_back( norm );
5744 if ( !alongAvgNorm )
5748 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5749 bc += SMESH_TNodeXYZ( nIt->next() );
5750 baryCenters.push_back( bc / nbN );
5755 if ( norms.empty() ) return 0;
5757 double normSize = avgNorm.Modulus();
5758 if ( normSize < std::numeric_limits<double>::min() )
5761 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5764 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5767 avgNorm /= normSize;
5770 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5773 double stepSize = nextStep();
5775 if ( norms.size() > 1 )
5777 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5779 // translate plane of a face
5780 baryCenters[ iF ] += norms[ iF ] * stepSize;
5782 // find point of intersection of the face plane located at baryCenters[ iF ]
5783 // and avgNorm located at pNew
5784 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5785 double dot = ( norms[ iF ] * avgNorm );
5786 if ( dot < std::numeric_limits<double>::min() )
5787 dot = stepSize * 1e-3;
5788 double step = -( norms[ iF ] * pNew + d ) / dot;
5789 pNew += step * avgNorm;
5794 pNew += stepSize * avgNorm;
5798 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5799 newNodes.push_back( newNode );
5804 //=======================================================================
5805 //function : ExtrusParam::makeNodesByNormal1D
5806 //purpose : create nodes for extrusion using normals of edges
5807 //=======================================================================
5809 int SMESH_MeshEditor::ExtrusParam::
5810 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5811 const SMDS_MeshNode* srcNode,
5812 std::list<const SMDS_MeshNode*> & newNodes,
5813 const bool makeMediumNodes)
5815 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5819 //=======================================================================
5820 //function : ExtrusionSweep
5822 //=======================================================================
5824 SMESH_MeshEditor::PGroupIDs
5825 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5826 const gp_Vec& theStep,
5827 const int theNbSteps,
5828 TTElemOfElemListMap& newElemsMap,
5830 const double theTolerance)
5832 ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5833 return ExtrusionSweep( theElems, aParams, newElemsMap );
5837 //=======================================================================
5838 //function : ExtrusionSweep
5840 //=======================================================================
5842 SMESH_MeshEditor::PGroupIDs
5843 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5844 ExtrusParam& theParams,
5845 TTElemOfElemListMap& newElemsMap)
5847 myLastCreatedElems.Clear();
5848 myLastCreatedNodes.Clear();
5850 // source elements for each generated one
5851 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5853 SMESHDS_Mesh* aMesh = GetMeshDS();
5855 setElemsFirst( theElemSets );
5856 const int nbSteps = theParams.NbSteps();
5857 theParams.SetElementsToUse( theElemSets[0] );
5859 TNodeOfNodeListMap mapNewNodes;
5860 //TNodeOfNodeVecMap mapNewNodes;
5861 TElemOfVecOfNnlmiMap mapElemNewNodes;
5862 //TElemOfVecOfMapNodesMap mapElemNewNodes;
5864 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5865 myMesh->NbFaces(ORDER_QUADRATIC) +
5866 myMesh->NbVolumes(ORDER_QUADRATIC) );
5868 TIDSortedElemSet::iterator itElem;
5869 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5871 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5872 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5874 // check element type
5875 const SMDS_MeshElement* elem = *itElem;
5876 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5879 const size_t nbNodes = elem->NbNodes();
5880 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5881 newNodesItVec.reserve( nbNodes );
5883 // loop on elem nodes
5884 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5885 while ( itN->more() )
5887 // check if a node has been already sweeped
5888 const SMDS_MeshNode* node = cast2Node( itN->next() );
5889 TNodeOfNodeListMap::iterator nIt =
5890 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5891 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5892 if ( listNewNodes.empty() )
5896 // check if we are to create medium nodes between corner ones
5897 bool needMediumNodes = false;
5898 if ( isQuadraticMesh )
5900 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5901 while (it->more() && !needMediumNodes )
5903 const SMDS_MeshElement* invElem = it->next();
5904 if ( invElem != elem && !theElems.count( invElem )) continue;
5905 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5906 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5907 needMediumNodes = true;
5910 // create nodes for all steps
5911 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5913 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5914 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5916 myLastCreatedNodes.Append( *newNodesIt );
5917 srcNodes.Append( node );
5922 break; // newNodesItVec will be shorter than nbNodes
5925 newNodesItVec.push_back( nIt );
5927 // make new elements
5928 if ( newNodesItVec.size() == nbNodes )
5929 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5933 if ( theParams.ToMakeBoundary() ) {
5934 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5936 PGroupIDs newGroupIDs;
5937 if ( theParams.ToMakeGroups() )
5938 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5943 //=======================================================================
5944 //function : ExtrusionAlongTrack
5946 //=======================================================================
5947 SMESH_MeshEditor::Extrusion_Error
5948 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5949 SMESH_subMesh* theTrack,
5950 const SMDS_MeshNode* theN1,
5951 const bool theHasAngles,
5952 list<double>& theAngles,
5953 const bool theLinearVariation,
5954 const bool theHasRefPoint,
5955 const gp_Pnt& theRefPoint,
5956 const bool theMakeGroups)
5958 MESSAGE("ExtrusionAlongTrack");
5959 myLastCreatedElems.Clear();
5960 myLastCreatedNodes.Clear();
5963 std::list<double> aPrms;
5964 TIDSortedElemSet::iterator itElem;
5967 TopoDS_Edge aTrackEdge;
5968 TopoDS_Vertex aV1, aV2;
5970 SMDS_ElemIteratorPtr aItE;
5971 SMDS_NodeIteratorPtr aItN;
5972 SMDSAbs_ElementType aTypeE;
5974 TNodeOfNodeListMap mapNewNodes;
5977 aNbE = theElements[0].size() + theElements[1].size();
5980 return EXTR_NO_ELEMENTS;
5982 // 1.1 Track Pattern
5985 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5987 aItE = pSubMeshDS->GetElements();
5988 while ( aItE->more() ) {
5989 const SMDS_MeshElement* pE = aItE->next();
5990 aTypeE = pE->GetType();
5991 // Pattern must contain links only
5992 if ( aTypeE != SMDSAbs_Edge )
5993 return EXTR_PATH_NOT_EDGE;
5996 list<SMESH_MeshEditor_PathPoint> fullList;
5998 const TopoDS_Shape& aS = theTrack->GetSubShape();
5999 // Sub-shape for the Pattern must be an Edge or Wire
6000 if( aS.ShapeType() == TopAbs_EDGE ) {
6001 aTrackEdge = TopoDS::Edge( aS );
6002 // the Edge must not be degenerated
6003 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6004 return EXTR_BAD_PATH_SHAPE;
6005 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6006 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
6007 const SMDS_MeshNode* aN1 = aItN->next();
6008 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
6009 const SMDS_MeshNode* aN2 = aItN->next();
6010 // starting node must be aN1 or aN2
6011 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6012 return EXTR_BAD_STARTING_NODE;
6013 aItN = pSubMeshDS->GetNodes();
6014 while ( aItN->more() ) {
6015 const SMDS_MeshNode* pNode = aItN->next();
6016 const SMDS_EdgePosition* pEPos =
6017 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6018 double aT = pEPos->GetUParameter();
6019 aPrms.push_back( aT );
6021 //Extrusion_Error err =
6022 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6023 } else if( aS.ShapeType() == TopAbs_WIRE ) {
6024 list< SMESH_subMesh* > LSM;
6025 TopTools_SequenceOfShape Edges;
6026 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6027 while(itSM->more()) {
6028 SMESH_subMesh* SM = itSM->next();
6030 const TopoDS_Shape& aS = SM->GetSubShape();
6033 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6034 int startNid = theN1->GetID();
6035 TColStd_MapOfInteger UsedNums;
6037 int NbEdges = Edges.Length();
6039 for(; i<=NbEdges; i++) {
6041 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6042 for(; itLSM!=LSM.end(); itLSM++) {
6044 if(UsedNums.Contains(k)) continue;
6045 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6046 SMESH_subMesh* locTrack = *itLSM;
6047 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6048 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6049 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6050 const SMDS_MeshNode* aN1 = aItN->next();
6051 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6052 const SMDS_MeshNode* aN2 = aItN->next();
6053 // starting node must be aN1 or aN2
6054 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6055 // 2. Collect parameters on the track edge
6057 aItN = locMeshDS->GetNodes();
6058 while ( aItN->more() ) {
6059 const SMDS_MeshNode* pNode = aItN->next();
6060 const SMDS_EdgePosition* pEPos =
6061 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6062 double aT = pEPos->GetUParameter();
6063 aPrms.push_back( aT );
6065 list<SMESH_MeshEditor_PathPoint> LPP;
6066 //Extrusion_Error err =
6067 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6068 LLPPs.push_back(LPP);
6070 // update startN for search following egde
6071 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6072 else startNid = aN1->GetID();
6076 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6077 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6078 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6079 for(; itPP!=firstList.end(); itPP++) {
6080 fullList.push_back( *itPP );
6082 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6083 fullList.pop_back();
6085 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6086 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6087 itPP = currList.begin();
6088 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6089 gp_Dir D1 = PP1.Tangent();
6090 gp_Dir D2 = PP2.Tangent();
6091 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6092 (D1.Z()+D2.Z())/2 ) );
6093 PP1.SetTangent(Dnew);
6094 fullList.push_back(PP1);
6096 for(; itPP!=firstList.end(); itPP++) {
6097 fullList.push_back( *itPP );
6099 PP1 = fullList.back();
6100 fullList.pop_back();
6102 // if wire not closed
6103 fullList.push_back(PP1);
6107 return EXTR_BAD_PATH_SHAPE;
6110 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6111 theHasRefPoint, theRefPoint, theMakeGroups);
6115 //=======================================================================
6116 //function : ExtrusionAlongTrack
6118 //=======================================================================
6119 SMESH_MeshEditor::Extrusion_Error
6120 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6121 SMESH_Mesh* theTrack,
6122 const SMDS_MeshNode* theN1,
6123 const bool theHasAngles,
6124 list<double>& theAngles,
6125 const bool theLinearVariation,
6126 const bool theHasRefPoint,
6127 const gp_Pnt& theRefPoint,
6128 const bool theMakeGroups)
6130 myLastCreatedElems.Clear();
6131 myLastCreatedNodes.Clear();
6134 std::list<double> aPrms;
6135 TIDSortedElemSet::iterator itElem;
6138 TopoDS_Edge aTrackEdge;
6139 TopoDS_Vertex aV1, aV2;
6141 SMDS_ElemIteratorPtr aItE;
6142 SMDS_NodeIteratorPtr aItN;
6143 SMDSAbs_ElementType aTypeE;
6145 TNodeOfNodeListMap mapNewNodes;
6148 aNbE = theElements[0].size() + theElements[1].size();
6151 return EXTR_NO_ELEMENTS;
6153 // 1.1 Track Pattern
6156 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6158 aItE = pMeshDS->elementsIterator();
6159 while ( aItE->more() ) {
6160 const SMDS_MeshElement* pE = aItE->next();
6161 aTypeE = pE->GetType();
6162 // Pattern must contain links only
6163 if ( aTypeE != SMDSAbs_Edge )
6164 return EXTR_PATH_NOT_EDGE;
6167 list<SMESH_MeshEditor_PathPoint> fullList;
6169 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6171 if ( !theTrack->HasShapeToMesh() ) {
6172 //Mesh without shape
6173 const SMDS_MeshNode* currentNode = NULL;
6174 const SMDS_MeshNode* prevNode = theN1;
6175 std::vector<const SMDS_MeshNode*> aNodesList;
6176 aNodesList.push_back(theN1);
6177 int nbEdges = 0, conn=0;
6178 const SMDS_MeshElement* prevElem = NULL;
6179 const SMDS_MeshElement* currentElem = NULL;
6180 int totalNbEdges = theTrack->NbEdges();
6181 SMDS_ElemIteratorPtr nIt;
6184 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6185 return EXTR_BAD_STARTING_NODE;
6188 conn = nbEdgeConnectivity(theN1);
6190 return EXTR_PATH_NOT_EDGE;
6192 aItE = theN1->GetInverseElementIterator();
6193 prevElem = aItE->next();
6194 currentElem = prevElem;
6196 if(totalNbEdges == 1 ) {
6197 nIt = currentElem->nodesIterator();
6198 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6199 if(currentNode == prevNode)
6200 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6201 aNodesList.push_back(currentNode);
6203 nIt = currentElem->nodesIterator();
6204 while( nIt->more() ) {
6205 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6206 if(currentNode == prevNode)
6207 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6208 aNodesList.push_back(currentNode);
6210 //case of the closed mesh
6211 if(currentNode == theN1) {
6216 conn = nbEdgeConnectivity(currentNode);
6218 return EXTR_PATH_NOT_EDGE;
6219 }else if( conn == 1 && nbEdges > 0 ) {
6224 prevNode = currentNode;
6225 aItE = currentNode->GetInverseElementIterator();
6226 currentElem = aItE->next();
6227 if( currentElem == prevElem)
6228 currentElem = aItE->next();
6229 nIt = currentElem->nodesIterator();
6230 prevElem = currentElem;
6236 if(nbEdges != totalNbEdges)
6237 return EXTR_PATH_NOT_EDGE;
6239 TopTools_SequenceOfShape Edges;
6240 double x1,x2,y1,y2,z1,z2;
6241 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6242 int startNid = theN1->GetID();
6243 for(int i = 1; i < aNodesList.size(); i++) {
6244 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
6245 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
6246 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
6247 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
6248 list<SMESH_MeshEditor_PathPoint> LPP;
6250 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6251 LLPPs.push_back(LPP);
6252 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
6253 else startNid = aNodesList[i-1]->GetID();
6257 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6258 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6259 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6260 for(; itPP!=firstList.end(); itPP++) {
6261 fullList.push_back( *itPP );
6264 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6265 SMESH_MeshEditor_PathPoint PP2;
6266 fullList.pop_back();
6268 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6269 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6270 itPP = currList.begin();
6271 PP2 = currList.front();
6272 gp_Dir D1 = PP1.Tangent();
6273 gp_Dir D2 = PP2.Tangent();
6274 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6275 (D1.Z()+D2.Z())/2 ) );
6276 PP1.SetTangent(Dnew);
6277 fullList.push_back(PP1);
6279 for(; itPP!=currList.end(); itPP++) {
6280 fullList.push_back( *itPP );
6282 PP1 = fullList.back();
6283 fullList.pop_back();
6285 fullList.push_back(PP1);
6287 } // Sub-shape for the Pattern must be an Edge or Wire
6288 else if( aS.ShapeType() == TopAbs_EDGE ) {
6289 aTrackEdge = TopoDS::Edge( aS );
6290 // the Edge must not be degenerated
6291 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6292 return EXTR_BAD_PATH_SHAPE;
6293 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6294 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6295 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6296 // starting node must be aN1 or aN2
6297 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6298 return EXTR_BAD_STARTING_NODE;
6299 aItN = pMeshDS->nodesIterator();
6300 while ( aItN->more() ) {
6301 const SMDS_MeshNode* pNode = aItN->next();
6302 if( pNode==aN1 || pNode==aN2 ) continue;
6303 const SMDS_EdgePosition* pEPos =
6304 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6305 double aT = pEPos->GetUParameter();
6306 aPrms.push_back( aT );
6308 //Extrusion_Error err =
6309 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6311 else if( aS.ShapeType() == TopAbs_WIRE ) {
6312 list< SMESH_subMesh* > LSM;
6313 TopTools_SequenceOfShape Edges;
6314 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6315 for(; eExp.More(); eExp.Next()) {
6316 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6317 if( SMESH_Algo::isDegenerated(E) ) continue;
6318 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6324 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6325 TopoDS_Vertex aVprev;
6326 TColStd_MapOfInteger UsedNums;
6327 int NbEdges = Edges.Length();
6329 for(; i<=NbEdges; i++) {
6331 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6332 for(; itLSM!=LSM.end(); itLSM++) {
6334 if(UsedNums.Contains(k)) continue;
6335 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6336 SMESH_subMesh* locTrack = *itLSM;
6337 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6338 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6339 bool aN1isOK = false, aN2isOK = false;
6340 if ( aVprev.IsNull() ) {
6341 // if previous vertex is not yet defined, it means that we in the beginning of wire
6342 // and we have to find initial vertex corresponding to starting node theN1
6343 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6344 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6345 // starting node must be aN1 or aN2
6346 aN1isOK = ( aN1 && aN1 == theN1 );
6347 aN2isOK = ( aN2 && aN2 == theN1 );
6350 // we have specified ending vertex of the previous edge on the previous iteration
6351 // and we have just to check that it corresponds to any vertex in current segment
6352 aN1isOK = aVprev.IsSame( aV1 );
6353 aN2isOK = aVprev.IsSame( aV2 );
6355 if ( !aN1isOK && !aN2isOK ) continue;
6356 // 2. Collect parameters on the track edge
6358 aItN = locMeshDS->GetNodes();
6359 while ( aItN->more() ) {
6360 const SMDS_MeshNode* pNode = aItN->next();
6361 const SMDS_EdgePosition* pEPos =
6362 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6363 double aT = pEPos->GetUParameter();
6364 aPrms.push_back( aT );
6366 list<SMESH_MeshEditor_PathPoint> LPP;
6367 //Extrusion_Error err =
6368 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6369 LLPPs.push_back(LPP);
6371 // update startN for search following egde
6372 if ( aN1isOK ) aVprev = aV2;
6377 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6378 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6379 fullList.splice( fullList.end(), firstList );
6381 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6382 fullList.pop_back();
6384 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6385 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6386 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6387 gp_Dir D1 = PP1.Tangent();
6388 gp_Dir D2 = PP2.Tangent();
6389 gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
6390 PP1.SetTangent(Dnew);
6391 fullList.push_back(PP1);
6392 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6393 PP1 = fullList.back();
6394 fullList.pop_back();
6396 // if wire not closed
6397 fullList.push_back(PP1);
6401 return EXTR_BAD_PATH_SHAPE;
6404 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6405 theHasRefPoint, theRefPoint, theMakeGroups);
6409 //=======================================================================
6410 //function : MakeEdgePathPoints
6411 //purpose : auxilary for ExtrusionAlongTrack
6412 //=======================================================================
6413 SMESH_MeshEditor::Extrusion_Error
6414 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
6415 const TopoDS_Edge& aTrackEdge,
6417 list<SMESH_MeshEditor_PathPoint>& LPP)
6419 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6421 aTolVec2=aTolVec*aTolVec;
6423 TopoDS_Vertex aV1, aV2;
6424 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6425 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6426 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6427 // 2. Collect parameters on the track edge
6428 aPrms.push_front( aT1 );
6429 aPrms.push_back( aT2 );
6432 if( FirstIsStart ) {
6443 SMESH_MeshEditor_PathPoint aPP;
6444 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6445 std::list<double>::iterator aItD = aPrms.begin();
6446 for(; aItD != aPrms.end(); ++aItD) {
6450 aC3D->D1( aT, aP3D, aVec );
6451 aL2 = aVec.SquareMagnitude();
6452 if ( aL2 < aTolVec2 )
6453 return EXTR_CANT_GET_TANGENT;
6454 gp_Dir aTgt( aVec );
6456 aPP.SetTangent( aTgt );
6457 aPP.SetParameter( aT );
6464 //=======================================================================
6465 //function : MakeExtrElements
6466 //purpose : auxilary for ExtrusionAlongTrack
6467 //=======================================================================
6468 SMESH_MeshEditor::Extrusion_Error
6469 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet theElemSets[2],
6470 list<SMESH_MeshEditor_PathPoint>& fullList,
6471 const bool theHasAngles,
6472 list<double>& theAngles,
6473 const bool theLinearVariation,
6474 const bool theHasRefPoint,
6475 const gp_Pnt& theRefPoint,
6476 const bool theMakeGroups)
6478 const int aNbTP = fullList.size();
6480 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6481 LinearAngleVariation(aNbTP-1, theAngles);
6482 // fill vector of path points with angles
6483 vector<SMESH_MeshEditor_PathPoint> aPPs;
6484 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6485 list<double>::iterator itAngles = theAngles.begin();
6486 aPPs.push_back( *itPP++ );
6487 for( ; itPP != fullList.end(); itPP++) {
6488 aPPs.push_back( *itPP );
6489 if ( theHasAngles && itAngles != theAngles.end() )
6490 aPPs.back().SetAngle( *itAngles++ );
6493 TNodeOfNodeListMap mapNewNodes;
6494 TElemOfVecOfNnlmiMap mapElemNewNodes;
6495 TTElemOfElemListMap newElemsMap;
6496 TIDSortedElemSet::iterator itElem;
6497 // source elements for each generated one
6498 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6500 // 3. Center of rotation aV0
6501 gp_Pnt aV0 = theRefPoint;
6502 if ( !theHasRefPoint )
6504 gp_XYZ aGC( 0.,0.,0. );
6505 TIDSortedElemSet newNodes;
6507 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6509 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6510 itElem = theElements.begin();
6511 for ( ; itElem != theElements.end(); itElem++ ) {
6512 const SMDS_MeshElement* elem = *itElem;
6514 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6515 while ( itN->more() ) {
6516 const SMDS_MeshElement* node = itN->next();
6517 if ( newNodes.insert( node ).second )
6518 aGC += SMESH_TNodeXYZ( node );
6522 aGC /= newNodes.size();
6524 } // if (!theHasRefPoint) {
6526 // 4. Processing the elements
6527 SMESHDS_Mesh* aMesh = GetMeshDS();
6529 setElemsFirst( theElemSets );
6530 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6532 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6533 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
6534 // check element type
6535 const SMDS_MeshElement* elem = *itElem;
6538 // SMDSAbs_ElementType aTypeE = elem->GetType();
6539 // if ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge )
6542 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6543 newNodesItVec.reserve( elem->NbNodes() );
6545 // loop on elem nodes
6547 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6548 while ( itN->more() )
6551 // check if a node has been already processed
6552 const SMDS_MeshNode* node =
6553 static_cast<const SMDS_MeshNode*>( itN->next() );
6554 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
6555 if ( nIt == mapNewNodes.end() ) {
6556 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6557 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6560 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6561 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6562 gp_Ax1 anAx1, anAxT1T0;
6563 gp_Dir aDT1x, aDT0x, aDT1T0;
6568 aPN0 = SMESH_TNodeXYZ( node );
6570 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6572 aDT0x= aPP0.Tangent();
6573 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
6575 for ( int j = 1; j < aNbTP; ++j ) {
6576 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6578 aDT1x = aPP1.Tangent();
6579 aAngle1x = aPP1.Angle();
6581 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6583 gp_Vec aV01x( aP0x, aP1x );
6584 aTrsf.SetTranslation( aV01x );
6587 aV1x = aV0x.Transformed( aTrsf );
6588 aPN1 = aPN0.Transformed( aTrsf );
6590 // rotation 1 [ T1,T0 ]
6591 aAngleT1T0=-aDT1x.Angle( aDT0x );
6592 if (fabs(aAngleT1T0) > aTolAng) {
6594 anAxT1T0.SetLocation( aV1x );
6595 anAxT1T0.SetDirection( aDT1T0 );
6596 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6598 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6602 if ( theHasAngles ) {
6603 anAx1.SetLocation( aV1x );
6604 anAx1.SetDirection( aDT1x );
6605 aTrsfRot.SetRotation( anAx1, aAngle1x );
6607 aPN1 = aPN1.Transformed( aTrsfRot );
6611 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
6612 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6613 // create additional node
6614 double x = ( aPN1.X() + aPN0.X() )/2.;
6615 double y = ( aPN1.Y() + aPN0.Y() )/2.;
6616 double z = ( aPN1.Z() + aPN0.Z() )/2.;
6617 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6618 myLastCreatedNodes.Append(newNode);
6619 srcNodes.Append( node );
6620 listNewNodes.push_back( newNode );
6622 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6623 myLastCreatedNodes.Append(newNode);
6624 srcNodes.Append( node );
6625 listNewNodes.push_back( newNode );
6635 // if current elem is quadratic and current node is not medium
6636 // we have to check - may be it is needed to insert additional nodes
6637 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6638 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6639 if(listNewNodes.size()==aNbTP-1) {
6640 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6641 gp_XYZ P(node->X(), node->Y(), node->Z());
6642 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6644 for(i=0; i<aNbTP-1; i++) {
6645 const SMDS_MeshNode* N = *it;
6646 double x = ( N->X() + P.X() )/2.;
6647 double y = ( N->Y() + P.Y() )/2.;
6648 double z = ( N->Z() + P.Z() )/2.;
6649 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6650 srcNodes.Append( node );
6651 myLastCreatedNodes.Append(newN);
6654 P = gp_XYZ(N->X(),N->Y(),N->Z());
6656 listNewNodes.clear();
6657 for(i=0; i<2*(aNbTP-1); i++) {
6658 listNewNodes.push_back(aNodes[i]);
6664 newNodesItVec.push_back( nIt );
6666 // make new elements
6667 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6668 // newNodesItVec[0]->second.size(), myLastCreatedElems );
6669 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6673 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6675 if ( theMakeGroups )
6676 generateGroups( srcNodes, srcElems, "extruded");
6682 //=======================================================================
6683 //function : LinearAngleVariation
6684 //purpose : auxilary for ExtrusionAlongTrack
6685 //=======================================================================
6686 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6687 list<double>& Angles)
6689 int nbAngles = Angles.size();
6690 if( nbSteps > nbAngles ) {
6691 vector<double> theAngles(nbAngles);
6692 list<double>::iterator it = Angles.begin();
6694 for(; it!=Angles.end(); it++) {
6696 theAngles[i] = (*it);
6699 double rAn2St = double( nbAngles ) / double( nbSteps );
6700 double angPrev = 0, angle;
6701 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6702 double angCur = rAn2St * ( iSt+1 );
6703 double angCurFloor = floor( angCur );
6704 double angPrevFloor = floor( angPrev );
6705 if ( angPrevFloor == angCurFloor )
6706 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6708 int iP = int( angPrevFloor );
6709 double angPrevCeil = ceil(angPrev);
6710 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6712 int iC = int( angCurFloor );
6713 if ( iC < nbAngles )
6714 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6716 iP = int( angPrevCeil );
6718 angle += theAngles[ iC ];
6720 res.push_back(angle);
6725 for(; it!=res.end(); it++)
6726 Angles.push_back( *it );
6731 //================================================================================
6733 * \brief Move or copy theElements applying theTrsf to their nodes
6734 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6735 * \param theTrsf - transformation to apply
6736 * \param theCopy - if true, create translated copies of theElems
6737 * \param theMakeGroups - if true and theCopy, create translated groups
6738 * \param theTargetMesh - mesh to copy translated elements into
6739 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6741 //================================================================================
6743 SMESH_MeshEditor::PGroupIDs
6744 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6745 const gp_Trsf& theTrsf,
6747 const bool theMakeGroups,
6748 SMESH_Mesh* theTargetMesh)
6750 myLastCreatedElems.Clear();
6751 myLastCreatedNodes.Clear();
6753 bool needReverse = false;
6754 string groupPostfix;
6755 switch ( theTrsf.Form() ) {
6757 MESSAGE("gp_PntMirror");
6759 groupPostfix = "mirrored";
6762 MESSAGE("gp_Ax1Mirror");
6763 groupPostfix = "mirrored";
6766 MESSAGE("gp_Ax2Mirror");
6768 groupPostfix = "mirrored";
6771 MESSAGE("gp_Rotation");
6772 groupPostfix = "rotated";
6774 case gp_Translation:
6775 MESSAGE("gp_Translation");
6776 groupPostfix = "translated";
6779 MESSAGE("gp_Scale");
6780 groupPostfix = "scaled";
6782 case gp_CompoundTrsf: // different scale by axis
6783 MESSAGE("gp_CompoundTrsf");
6784 groupPostfix = "scaled";
6788 needReverse = false;
6789 groupPostfix = "transformed";
6792 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6793 SMESHDS_Mesh* aMesh = GetMeshDS();
6795 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6796 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6797 SMESH_MeshEditor::ElemFeatures elemType;
6799 // map old node to new one
6800 TNodeNodeMap nodeMap;
6802 // elements sharing moved nodes; those of them which have all
6803 // nodes mirrored but are not in theElems are to be reversed
6804 TIDSortedElemSet inverseElemSet;
6806 // source elements for each generated one
6807 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6809 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6810 TIDSortedElemSet orphanNode;
6812 if ( theElems.empty() ) // transform the whole mesh
6815 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6816 while ( eIt->more() ) theElems.insert( eIt->next() );
6818 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6819 while ( nIt->more() )
6821 const SMDS_MeshNode* node = nIt->next();
6822 if ( node->NbInverseElements() == 0)
6823 orphanNode.insert( node );
6827 // loop on elements to transform nodes : first orphan nodes then elems
6828 TIDSortedElemSet::iterator itElem;
6829 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6830 for (int i=0; i<2; i++)
6831 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6833 const SMDS_MeshElement* elem = *itElem;
6837 // loop on elem nodes
6839 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6840 while ( itN->more() )
6842 const SMDS_MeshNode* node = cast2Node( itN->next() );
6843 // check if a node has been already transformed
6844 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6845 nodeMap.insert( make_pair ( node, node ));
6846 if ( !n2n_isnew.second )
6849 node->GetXYZ( coord );
6850 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6851 if ( theTargetMesh ) {
6852 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6853 n2n_isnew.first->second = newNode;
6854 myLastCreatedNodes.Append(newNode);
6855 srcNodes.Append( node );
6857 else if ( theCopy ) {
6858 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6859 n2n_isnew.first->second = newNode;
6860 myLastCreatedNodes.Append(newNode);
6861 srcNodes.Append( node );
6864 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6865 // node position on shape becomes invalid
6866 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6867 ( SMDS_SpacePosition::originSpacePosition() );
6870 // keep inverse elements
6871 if ( !theCopy && !theTargetMesh && needReverse ) {
6872 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6873 while ( invElemIt->more() ) {
6874 const SMDS_MeshElement* iel = invElemIt->next();
6875 inverseElemSet.insert( iel );
6879 } // loop on elems in { &orphanNode, &theElems };
6881 // either create new elements or reverse mirrored ones
6882 if ( !theCopy && !needReverse && !theTargetMesh )
6885 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6887 // Replicate or reverse elements
6889 std::vector<int> iForw;
6890 vector<const SMDS_MeshNode*> nodes;
6891 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6893 const SMDS_MeshElement* elem = *itElem;
6894 if ( !elem ) continue;
6896 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6897 int nbNodes = elem->NbNodes();
6898 if ( geomType == SMDSGeom_NONE ) continue; // node
6900 nodes.resize( nbNodes );
6902 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6904 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6908 bool allTransformed = true;
6909 int nbFaces = aPolyedre->NbFaces();
6910 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6912 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6913 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6915 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6916 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6917 if ( nodeMapIt == nodeMap.end() )
6918 allTransformed = false; // not all nodes transformed
6920 nodes.push_back((*nodeMapIt).second);
6922 if ( needReverse && allTransformed )
6923 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6925 if ( !allTransformed )
6926 continue; // not all nodes transformed
6928 else // ----------------------- the rest element types
6930 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6931 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6932 const vector<int>& i = needReverse ? iRev : iForw;
6934 // find transformed nodes
6936 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6937 while ( itN->more() ) {
6938 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6939 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6940 if ( nodeMapIt == nodeMap.end() )
6941 break; // not all nodes transformed
6942 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6944 if ( iNode != nbNodes )
6945 continue; // not all nodes transformed
6949 // copy in this or a new mesh
6950 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6951 srcElems.Append( elem );
6954 // reverse element as it was reversed by transformation
6956 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6959 } // loop on elements
6961 if ( editor && editor != this )
6962 myLastCreatedElems = editor->myLastCreatedElems;
6964 PGroupIDs newGroupIDs;
6966 if ( ( theMakeGroups && theCopy ) ||
6967 ( theMakeGroups && theTargetMesh ) )
6968 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6973 //=======================================================================
6975 * \brief Create groups of elements made during transformation
6976 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6977 * \param elemGens - elements making corresponding myLastCreatedElems
6978 * \param postfix - to append to names of new groups
6979 * \param targetMesh - mesh to create groups in
6980 * \param topPresent - is there "top" elements that are created by sweeping
6982 //=======================================================================
6984 SMESH_MeshEditor::PGroupIDs
6985 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6986 const SMESH_SequenceOfElemPtr& elemGens,
6987 const std::string& postfix,
6988 SMESH_Mesh* targetMesh,
6989 const bool topPresent)
6991 PGroupIDs newGroupIDs( new list<int> );
6992 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6994 // Sort existing groups by types and collect their names
6996 // containers to store an old group and generated new ones;
6997 // 1st new group is for result elems of different type than a source one;
6998 // 2nd new group is for same type result elems ("top" group at extrusion)
7000 using boost::make_tuple;
7001 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
7002 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
7003 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7005 set< string > groupNames;
7007 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7008 if ( !groupIt->more() ) return newGroupIDs;
7010 int newGroupID = mesh->GetGroupIds().back()+1;
7011 while ( groupIt->more() )
7013 SMESH_Group * group = groupIt->next();
7014 if ( !group ) continue;
7015 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7016 if ( !groupDS || groupDS->IsEmpty() ) continue;
7017 groupNames.insert ( group->GetName() );
7018 groupDS->SetStoreName( group->GetName() );
7019 const SMDSAbs_ElementType type = groupDS->GetType();
7020 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7021 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7022 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7023 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7026 // Loop on nodes and elements to add them in new groups
7028 vector< const SMDS_MeshElement* > resultElems;
7029 for ( int isNodes = 0; isNodes < 2; ++isNodes )
7031 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
7032 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7033 if ( gens.Length() != elems.Length() )
7034 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7036 // loop on created elements
7037 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7039 const SMDS_MeshElement* sourceElem = gens( iElem );
7040 if ( !sourceElem ) {
7041 MESSAGE("generateGroups(): NULL source element");
7044 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7045 if ( groupsOldNew.empty() ) { // no groups of this type at all
7046 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7047 ++iElem; // skip all elements made by sourceElem
7050 // collect all elements made by the iElem-th sourceElem
7051 resultElems.clear();
7052 if ( const SMDS_MeshElement* resElem = elems( iElem ))
7053 if ( resElem != sourceElem )
7054 resultElems.push_back( resElem );
7055 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7056 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7057 if ( resElem != sourceElem )
7058 resultElems.push_back( resElem );
7060 const SMDS_MeshElement* topElem = 0;
7061 if ( isNodes ) // there must be a top element
7063 topElem = resultElems.back();
7064 resultElems.pop_back();
7068 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7069 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7070 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7072 topElem = *resElemIt;
7073 *resElemIt = 0; // erase *resElemIt
7077 // add resultElems to groups originted from ones the sourceElem belongs to
7078 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7079 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7081 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7082 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7084 // fill in a new group
7085 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7086 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7087 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7089 newGroup.Add( *resElemIt );
7091 // fill a "top" group
7094 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7095 newTopGroup.Add( topElem );
7099 } // loop on created elements
7100 }// loop on nodes and elements
7102 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7104 list<int> topGrouIds;
7105 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7107 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
7108 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7109 orderedOldNewGroups[i]->get<2>() };
7110 for ( int is2nd = 0; is2nd < 2; ++is2nd )
7112 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7113 if ( newGroupDS->IsEmpty() )
7115 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7120 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7123 const bool isTop = ( topPresent &&
7124 newGroupDS->GetType() == oldGroupDS->GetType() &&
7127 string name = oldGroupDS->GetStoreName();
7128 { // remove trailing whitespaces (issue 22599)
7129 size_t size = name.size();
7130 while ( size > 1 && isspace( name[ size-1 ]))
7132 if ( size != name.size() )
7134 name.resize( size );
7135 oldGroupDS->SetStoreName( name.c_str() );
7138 if ( !targetMesh ) {
7139 string suffix = ( isTop ? "top": postfix.c_str() );
7143 while ( !groupNames.insert( name ).second ) // name exists
7144 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7149 newGroupDS->SetStoreName( name.c_str() );
7151 // make a SMESH_Groups
7152 mesh->AddGroup( newGroupDS );
7154 topGrouIds.push_back( newGroupDS->GetID() );
7156 newGroupIDs->push_back( newGroupDS->GetID() );
7160 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7165 //================================================================================
7167 * * \brief Return list of group of nodes close to each other within theTolerance
7168 * * Search among theNodes or in the whole mesh if theNodes is empty using
7169 * * an Octree algorithm
7170 * \param [in,out] theNodes - the nodes to treat
7171 * \param [in] theTolerance - the tolerance
7172 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7173 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7174 * corner and medium nodes in separate groups
7176 //================================================================================
7178 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7179 const double theTolerance,
7180 TListOfListOfNodes & theGroupsOfNodes,
7181 bool theSeparateCornersAndMedium)
7183 myLastCreatedElems.Clear();
7184 myLastCreatedNodes.Clear();
7186 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7187 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7188 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7189 theSeparateCornersAndMedium = false;
7191 TIDSortedNodeSet& corners = theNodes;
7192 TIDSortedNodeSet medium;
7194 if ( theNodes.empty() ) // get all nodes in the mesh
7196 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7197 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7198 if ( theSeparateCornersAndMedium )
7199 while ( nIt->more() )
7201 const SMDS_MeshNode* n = nIt->next();
7202 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7203 nodeSet->insert( nodeSet->end(), n );
7206 while ( nIt->more() )
7207 theNodes.insert( theNodes.end(),nIt->next() );
7209 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7211 TIDSortedNodeSet::iterator nIt = corners.begin();
7212 while ( nIt != corners.end() )
7213 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7215 medium.insert( medium.end(), *nIt );
7216 corners.erase( nIt++ );
7224 if ( !corners.empty() )
7225 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7226 if ( !medium.empty() )
7227 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7230 //=======================================================================
7231 //function : SimplifyFace
7232 //purpose : split a chain of nodes into several closed chains
7233 //=======================================================================
7235 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7236 vector<const SMDS_MeshNode *>& poly_nodes,
7237 vector<int>& quantities) const
7239 int nbNodes = faceNodes.size();
7244 set<const SMDS_MeshNode*> nodeSet;
7246 // get simple seq of nodes
7247 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7250 simpleNodes[iSimple++] = faceNodes[0];
7251 for (int iCur = 1; iCur < nbNodes; iCur++) {
7252 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7253 simpleNodes[iSimple++] = faceNodes[iCur];
7254 nodeSet.insert( faceNodes[iCur] );
7257 int nbUnique = nodeSet.size();
7258 int nbSimple = iSimple;
7259 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7269 bool foundLoop = (nbSimple > nbUnique);
7272 set<const SMDS_MeshNode*> loopSet;
7273 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7274 const SMDS_MeshNode* n = simpleNodes[iSimple];
7275 if (!loopSet.insert( n ).second) {
7279 int iC = 0, curLast = iSimple;
7280 for (; iC < curLast; iC++) {
7281 if (simpleNodes[iC] == n) break;
7283 int loopLen = curLast - iC;
7285 // create sub-element
7287 quantities.push_back(loopLen);
7288 for (; iC < curLast; iC++) {
7289 poly_nodes.push_back(simpleNodes[iC]);
7292 // shift the rest nodes (place from the first loop position)
7293 for (iC = curLast + 1; iC < nbSimple; iC++) {
7294 simpleNodes[iC - loopLen] = simpleNodes[iC];
7296 nbSimple -= loopLen;
7299 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7300 } // while (foundLoop)
7304 quantities.push_back(iSimple);
7305 for (int i = 0; i < iSimple; i++)
7306 poly_nodes.push_back(simpleNodes[i]);
7312 //=======================================================================
7313 //function : MergeNodes
7314 //purpose : In each group, the cdr of nodes are substituted by the first one
7316 //=======================================================================
7318 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7320 MESSAGE("MergeNodes");
7321 myLastCreatedElems.Clear();
7322 myLastCreatedNodes.Clear();
7324 SMESHDS_Mesh* aMesh = GetMeshDS();
7326 TNodeNodeMap nodeNodeMap; // node to replace - new node
7327 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7328 list< int > rmElemIds, rmNodeIds;
7330 // Fill nodeNodeMap and elems
7332 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7333 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7335 list<const SMDS_MeshNode*>& nodes = *grIt;
7336 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7337 const SMDS_MeshNode* nToKeep = *nIt;
7338 for ( ++nIt; nIt != nodes.end(); nIt++ )
7340 const SMDS_MeshNode* nToRemove = *nIt;
7341 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7342 if ( nToRemove != nToKeep )
7344 rmNodeIds.push_back( nToRemove->GetID() );
7345 AddToSameGroups( nToKeep, nToRemove, aMesh );
7346 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7347 // after MergeNodes() w/o creating node in place of merged ones.
7348 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7349 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7350 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7351 sm->SetIsAlwaysComputed( true );
7353 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7354 while ( invElemIt->more() ) {
7355 const SMDS_MeshElement* elem = invElemIt->next();
7360 // Change element nodes or remove an element
7362 set<const SMDS_MeshNode*> nodeSet;
7363 vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7365 ElemFeatures elemType;
7367 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7368 for ( ; eIt != elems.end(); eIt++ )
7370 const SMDS_MeshElement* elem = *eIt;
7371 int nbNodes = elem->NbNodes();
7372 int aShapeId = FindShape( elem );
7375 curNodes.resize( nbNodes );
7376 uniqueNodes.resize( nbNodes );
7377 iRepl.resize( nbNodes );
7378 int iUnique = 0, iCur = 0, nbRepl = 0;
7380 // get new seq of nodes
7381 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7382 while ( itN->more() )
7384 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7386 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7387 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7389 { ////////// BUG 0020185: begin
7390 bool stopRecur = false;
7391 set<const SMDS_MeshNode*> nodesRecur;
7392 nodesRecur.insert(n);
7393 while (!stopRecur) {
7394 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7395 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7396 n = (*nnIt_i).second;
7397 if (!nodesRecur.insert(n).second) {
7398 // error: recursive dependancy
7405 } ////////// BUG 0020185: end
7407 curNodes[ iCur ] = n;
7408 bool isUnique = nodeSet.insert( n ).second;
7410 uniqueNodes[ iUnique++ ] = n;
7412 iRepl[ nbRepl++ ] = iCur;
7416 // Analyse element topology after replacement
7419 int nbUniqueNodes = nodeSet.size();
7420 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7422 if (elem->IsPoly()) // Polygons and Polyhedral volumes
7424 if (elem->GetType() == SMDSAbs_Face) // Polygon
7426 elemType.Init( elem );
7427 const bool isQuad = elemType.myIsQuad;
7429 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7430 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7432 // a polygon can divide into several elements
7433 vector<const SMDS_MeshNode *> polygons_nodes;
7434 vector<int> quantities;
7435 int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7438 vector<const SMDS_MeshNode *> face_nodes;
7440 for (int iface = 0; iface < nbNew; iface++)
7442 int nbNewNodes = quantities[iface];
7443 face_nodes.assign( polygons_nodes.begin() + inode,
7444 polygons_nodes.begin() + inode + nbNewNodes );
7445 inode += nbNewNodes;
7446 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7448 bool isValid = ( nbNewNodes % 2 == 0 );
7449 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7450 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7451 elemType.SetQuad( isValid );
7452 if ( isValid ) // put medium nodes after corners
7453 SMDS_MeshCell::applyInterlaceRev
7454 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7455 nbNewNodes ), face_nodes );
7457 elemType.SetPoly(( nbNewNodes / ( elemType.myIsQuad + 1 ) > 4 ));
7459 SMDS_MeshElement* newElem = AddElement( face_nodes, elemType );
7461 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7464 rmElemIds.push_back(elem->GetID());
7468 else if (elem->GetType() == SMDSAbs_Volume) // Polyhedral volume
7470 if (nbUniqueNodes < 4) {
7471 rmElemIds.push_back(elem->GetID());
7474 // each face has to be analyzed in order to check volume validity
7475 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
7478 int nbFaces = aPolyedre->NbFaces();
7480 vector<const SMDS_MeshNode *> poly_nodes;
7481 vector<int> quantities;
7483 for (int iface = 1; iface <= nbFaces; iface++) {
7484 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7485 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7487 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7488 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7489 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7490 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7491 faceNode = (*nnIt).second;
7493 faceNodes[inode - 1] = faceNode;
7496 SimplifyFace(faceNodes, poly_nodes, quantities);
7499 if (quantities.size() > 3) {
7500 // to be done: remove coincident faces
7503 if (quantities.size() > 3)
7505 const SMDS_MeshElement* newElem =
7506 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7507 myLastCreatedElems.Append(newElem);
7508 if ( aShapeId && newElem )
7509 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7510 rmElemIds.push_back(elem->GetID());
7514 rmElemIds.push_back(elem->GetID());
7525 // TODO not all the possible cases are solved. Find something more generic?
7526 switch ( nbNodes ) {
7527 case 2: ///////////////////////////////////// EDGE
7528 isOk = false; break;
7529 case 3: ///////////////////////////////////// TRIANGLE
7530 isOk = false; break;
7532 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7534 else { //////////////////////////////////// QUADRANGLE
7535 if ( nbUniqueNodes < 3 )
7537 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7538 isOk = false; // opposite nodes stick
7539 //MESSAGE("isOk " << isOk);
7542 case 6: ///////////////////////////////////// PENTAHEDRON
7543 if ( nbUniqueNodes == 4 ) {
7544 // ---------------------------------> tetrahedron
7546 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7547 // all top nodes stick: reverse a bottom
7548 uniqueNodes[ 0 ] = curNodes [ 1 ];
7549 uniqueNodes[ 1 ] = curNodes [ 0 ];
7551 else if (nbRepl == 3 &&
7552 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7553 // all bottom nodes stick: set a top before
7554 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7555 uniqueNodes[ 0 ] = curNodes [ 3 ];
7556 uniqueNodes[ 1 ] = curNodes [ 4 ];
7557 uniqueNodes[ 2 ] = curNodes [ 5 ];
7559 else if (nbRepl == 4 &&
7560 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7561 // a lateral face turns into a line: reverse a bottom
7562 uniqueNodes[ 0 ] = curNodes [ 1 ];
7563 uniqueNodes[ 1 ] = curNodes [ 0 ];
7568 else if ( nbUniqueNodes == 5 ) {
7569 // PENTAHEDRON --------------------> 2 tetrahedrons
7570 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7571 // a bottom node sticks with a linked top one
7573 SMDS_MeshElement* newElem =
7574 aMesh->AddVolume(curNodes[ 3 ],
7577 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7578 myLastCreatedElems.Append(newElem);
7580 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7581 // 2. : reverse a bottom
7582 uniqueNodes[ 0 ] = curNodes [ 1 ];
7583 uniqueNodes[ 1 ] = curNodes [ 0 ];
7593 if(elem->IsQuadratic()) { // Quadratic quadrangle
7605 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7608 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7610 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7611 uniqueNodes[0] = curNodes[0];
7612 uniqueNodes[1] = curNodes[2];
7613 uniqueNodes[2] = curNodes[3];
7614 uniqueNodes[3] = curNodes[5];
7615 uniqueNodes[4] = curNodes[6];
7616 uniqueNodes[5] = curNodes[7];
7619 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7620 uniqueNodes[0] = curNodes[0];
7621 uniqueNodes[1] = curNodes[1];
7622 uniqueNodes[2] = curNodes[2];
7623 uniqueNodes[3] = curNodes[4];
7624 uniqueNodes[4] = curNodes[5];
7625 uniqueNodes[5] = curNodes[6];
7628 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7629 uniqueNodes[0] = curNodes[1];
7630 uniqueNodes[1] = curNodes[2];
7631 uniqueNodes[2] = curNodes[3];
7632 uniqueNodes[3] = curNodes[5];
7633 uniqueNodes[4] = curNodes[6];
7634 uniqueNodes[5] = curNodes[0];
7637 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7638 uniqueNodes[0] = curNodes[0];
7639 uniqueNodes[1] = curNodes[1];
7640 uniqueNodes[2] = curNodes[3];
7641 uniqueNodes[3] = curNodes[4];
7642 uniqueNodes[4] = curNodes[6];
7643 uniqueNodes[5] = curNodes[7];
7646 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7647 uniqueNodes[0] = curNodes[0];
7648 uniqueNodes[1] = curNodes[2];
7649 uniqueNodes[2] = curNodes[3];
7650 uniqueNodes[3] = curNodes[1];
7651 uniqueNodes[4] = curNodes[6];
7652 uniqueNodes[5] = curNodes[7];
7655 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7656 uniqueNodes[0] = curNodes[0];
7657 uniqueNodes[1] = curNodes[1];
7658 uniqueNodes[2] = curNodes[2];
7659 uniqueNodes[3] = curNodes[4];
7660 uniqueNodes[4] = curNodes[5];
7661 uniqueNodes[5] = curNodes[7];
7664 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7665 uniqueNodes[0] = curNodes[0];
7666 uniqueNodes[1] = curNodes[1];
7667 uniqueNodes[2] = curNodes[3];
7668 uniqueNodes[3] = curNodes[4];
7669 uniqueNodes[4] = curNodes[2];
7670 uniqueNodes[5] = curNodes[7];
7673 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7674 uniqueNodes[0] = curNodes[0];
7675 uniqueNodes[1] = curNodes[1];
7676 uniqueNodes[2] = curNodes[2];
7677 uniqueNodes[3] = curNodes[4];
7678 uniqueNodes[4] = curNodes[5];
7679 uniqueNodes[5] = curNodes[3];
7684 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7687 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7691 //////////////////////////////////// HEXAHEDRON
7693 SMDS_VolumeTool hexa (elem);
7694 hexa.SetExternalNormal();
7695 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7696 //////////////////////// HEX ---> 1 tetrahedron
7697 for ( int iFace = 0; iFace < 6; iFace++ ) {
7698 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7699 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7700 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7701 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7702 // one face turns into a point ...
7703 int iOppFace = hexa.GetOppFaceIndex( iFace );
7704 ind = hexa.GetFaceNodesIndices( iOppFace );
7706 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7707 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7710 if ( nbStick == 1 ) {
7711 // ... and the opposite one - into a triangle.
7713 ind = hexa.GetFaceNodesIndices( iFace );
7714 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7721 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7722 //////////////////////// HEX ---> 1 prism
7723 int nbTria = 0, iTria[3];
7724 const int *ind; // indices of face nodes
7725 // look for triangular faces
7726 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7727 ind = hexa.GetFaceNodesIndices( iFace );
7728 TIDSortedNodeSet faceNodes;
7729 for ( iCur = 0; iCur < 4; iCur++ )
7730 faceNodes.insert( curNodes[ind[iCur]] );
7731 if ( faceNodes.size() == 3 )
7732 iTria[ nbTria++ ] = iFace;
7734 // check if triangles are opposite
7735 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7738 // set nodes of the bottom triangle
7739 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7741 for ( iCur = 0; iCur < 4; iCur++ )
7742 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7743 indB.push_back( ind[iCur] );
7744 if ( !hexa.IsForward() )
7745 std::swap( indB[0], indB[2] );
7746 for ( iCur = 0; iCur < 3; iCur++ )
7747 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7748 // set nodes of the top triangle
7749 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7750 for ( iCur = 0; iCur < 3; ++iCur )
7751 for ( int j = 0; j < 4; ++j )
7752 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7754 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7760 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7761 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7762 for ( int iFace = 0; iFace < 6; iFace++ ) {
7763 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7764 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7765 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7766 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7767 // one face turns into a point ...
7768 int iOppFace = hexa.GetOppFaceIndex( iFace );
7769 ind = hexa.GetFaceNodesIndices( iOppFace );
7771 iUnique = 2; // reverse a tetrahedron 1 bottom
7772 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7773 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7775 else if ( iUnique >= 0 )
7776 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7778 if ( nbStick == 0 ) {
7779 // ... and the opposite one is a quadrangle
7781 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7782 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7785 SMDS_MeshElement* newElem =
7786 aMesh->AddVolume(curNodes[ind[ 0 ]],
7789 curNodes[indTop[ 0 ]]);
7790 myLastCreatedElems.Append(newElem);
7792 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7799 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7800 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7801 // find indices of quad and tri faces
7802 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7803 for ( iFace = 0; iFace < 6; iFace++ ) {
7804 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7806 for ( iCur = 0; iCur < 4; iCur++ )
7807 nodeSet.insert( curNodes[ind[ iCur ]] );
7808 nbUniqueNodes = nodeSet.size();
7809 if ( nbUniqueNodes == 3 )
7810 iTriFace[ nbTri++ ] = iFace;
7811 else if ( nbUniqueNodes == 4 )
7812 iQuadFace[ nbQuad++ ] = iFace;
7814 if (nbQuad == 2 && nbTri == 4 &&
7815 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7816 // 2 opposite quadrangles stuck with a diagonal;
7817 // sample groups of merged indices: (0-4)(2-6)
7818 // --------------------------------------------> 2 tetrahedrons
7819 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7820 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7821 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7822 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7823 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7824 // stuck with 0-2 diagonal
7832 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7833 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7834 // stuck with 1-3 diagonal
7846 uniqueNodes[ 0 ] = curNodes [ i0 ];
7847 uniqueNodes[ 1 ] = curNodes [ i1d ];
7848 uniqueNodes[ 2 ] = curNodes [ i3d ];
7849 uniqueNodes[ 3 ] = curNodes [ i0t ];
7852 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7856 myLastCreatedElems.Append(newElem);
7858 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7861 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7862 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7863 // --------------------------------------------> prism
7864 // find 2 opposite triangles
7866 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7867 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7868 // find indices of kept and replaced nodes
7869 // and fill unique nodes of 2 opposite triangles
7870 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7871 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7872 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7873 // fill unique nodes
7876 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7877 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7878 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7880 // iCur of a linked node of the opposite face (make normals co-directed):
7881 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7882 // check that correspondent corners of triangles are linked
7883 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7886 uniqueNodes[ iUnique ] = n;
7887 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7896 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7899 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7906 } // switch ( nbNodes )
7908 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7910 if ( isOk ) // the non-poly elem remains valid after sticking nodes
7912 elemType.Init( elem ).SetID( elem->GetID() );
7914 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7915 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7917 uniqueNodes.resize(nbUniqueNodes);
7918 SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7919 if ( sm && newElem )
7920 sm->AddElement( newElem );
7921 if ( elem != newElem )
7922 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
7959 void Set(const SMDS_MeshElement* e) const
7964 mutable const SMDS_MeshElement* myElem;
7967 //=======================================================================
7968 //function : FindEqualElements
7969 //purpose : Return list of group of elements built on the same nodes.
7970 // Search among theElements or in the whole mesh if theElements is empty
7971 //=======================================================================
7973 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7974 TListOfListOfElementsID & theGroupsOfElementsID)
7976 myLastCreatedElems.Clear();
7977 myLastCreatedNodes.Clear();
7979 typedef map< SortableElement, int > TMapOfNodeSet;
7980 typedef list<int> TGroupOfElems;
7982 if ( theElements.empty() )
7983 { // get all elements in the mesh
7984 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7985 while ( eIt->more() )
7986 theElements.insert( theElements.end(), eIt->next() );
7989 vector< TGroupOfElems > arrayOfGroups;
7990 TGroupOfElems groupOfElems;
7991 TMapOfNodeSet mapOfNodeSet;
7993 TIDSortedElemSet::iterator elemIt = theElements.begin();
7994 for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7996 const SMDS_MeshElement* curElem = *elemIt;
7997 SortableElement SE(curElem);
7999 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8000 if ( !pp.second ) { // one more coincident elem
8001 TMapOfNodeSet::iterator& itSE = pp.first;
8002 int ind = (*itSE).second;
8003 arrayOfGroups[ind].push_back( curElem->GetID() );
8006 arrayOfGroups.push_back( groupOfElems );
8007 arrayOfGroups.back().push_back( curElem->GetID() );
8012 groupOfElems.clear();
8013 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8014 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8016 if ( groupIt->size() > 1 ) {
8017 //groupOfElems.sort(); -- theElements is sorted already
8018 theGroupsOfElementsID.push_back( groupOfElems );
8019 theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8024 //=======================================================================
8025 //function : MergeElements
8026 //purpose : In each given group, substitute all elements by the first one.
8027 //=======================================================================
8029 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8031 myLastCreatedElems.Clear();
8032 myLastCreatedNodes.Clear();
8034 typedef list<int> TListOfIDs;
8035 TListOfIDs rmElemIds; // IDs of elems to remove
8037 SMESHDS_Mesh* aMesh = GetMeshDS();
8039 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8040 while ( groupsIt != theGroupsOfElementsID.end() ) {
8041 TListOfIDs& aGroupOfElemID = *groupsIt;
8042 aGroupOfElemID.sort();
8043 int elemIDToKeep = aGroupOfElemID.front();
8044 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8045 aGroupOfElemID.pop_front();
8046 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8047 while ( idIt != aGroupOfElemID.end() ) {
8048 int elemIDToRemove = *idIt;
8049 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8050 // add the kept element in groups of removed one (PAL15188)
8051 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8052 rmElemIds.push_back( elemIDToRemove );
8058 Remove( rmElemIds, false );
8061 //=======================================================================
8062 //function : MergeEqualElements
8063 //purpose : Remove all but one of elements built on the same nodes.
8064 //=======================================================================
8066 void SMESH_MeshEditor::MergeEqualElements()
8068 TIDSortedElemSet aMeshElements; /* empty input ==
8069 to merge equal elements in the whole mesh */
8070 TListOfListOfElementsID aGroupsOfElementsID;
8071 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8072 MergeElements(aGroupsOfElementsID);
8075 //=======================================================================
8076 //function : findAdjacentFace
8078 //=======================================================================
8080 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8081 const SMDS_MeshNode* n2,
8082 const SMDS_MeshElement* elem)
8084 TIDSortedElemSet elemSet, avoidSet;
8086 avoidSet.insert ( elem );
8087 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8090 //=======================================================================
8091 //function : findSegment
8092 //purpose : Return a mesh segment by two nodes one of which can be medium
8093 //=======================================================================
8095 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8096 const SMDS_MeshNode* n2)
8098 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8099 while ( it->more() )
8101 const SMDS_MeshElement* seg = it->next();
8102 if ( seg->GetNodeIndex( n2 ) >= 0 )
8108 //=======================================================================
8109 //function : FindFreeBorder
8111 //=======================================================================
8113 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8115 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8116 const SMDS_MeshNode* theSecondNode,
8117 const SMDS_MeshNode* theLastNode,
8118 list< const SMDS_MeshNode* > & theNodes,
8119 list< const SMDS_MeshElement* >& theFaces)
8121 if ( !theFirstNode || !theSecondNode )
8123 // find border face between theFirstNode and theSecondNode
8124 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8128 theFaces.push_back( curElem );
8129 theNodes.push_back( theFirstNode );
8130 theNodes.push_back( theSecondNode );
8132 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8133 TIDSortedElemSet foundElems;
8134 bool needTheLast = ( theLastNode != 0 );
8136 while ( nStart != theLastNode ) {
8137 if ( nStart == theFirstNode )
8138 return !needTheLast;
8140 // find all free border faces sharing form nStart
8142 list< const SMDS_MeshElement* > curElemList;
8143 list< const SMDS_MeshNode* > nStartList;
8144 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8145 while ( invElemIt->more() ) {
8146 const SMDS_MeshElement* e = invElemIt->next();
8147 if ( e == curElem || foundElems.insert( e ).second ) {
8149 int iNode = 0, nbNodes = e->NbNodes();
8150 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8152 if ( e->IsQuadratic() ) {
8153 const SMDS_VtkFace* F =
8154 dynamic_cast<const SMDS_VtkFace*>(e);
8155 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8156 // use special nodes iterator
8157 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8158 while( anIter->more() ) {
8159 nodes[ iNode++ ] = cast2Node(anIter->next());
8163 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8164 while ( nIt->more() )
8165 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8167 nodes[ iNode ] = nodes[ 0 ];
8169 for ( iNode = 0; iNode < nbNodes; iNode++ )
8170 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8171 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8172 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8174 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8175 curElemList.push_back( e );
8179 // analyse the found
8181 int nbNewBorders = curElemList.size();
8182 if ( nbNewBorders == 0 ) {
8183 // no free border furthermore
8184 return !needTheLast;
8186 else if ( nbNewBorders == 1 ) {
8187 // one more element found
8189 nStart = nStartList.front();
8190 curElem = curElemList.front();
8191 theFaces.push_back( curElem );
8192 theNodes.push_back( nStart );
8195 // several continuations found
8196 list< const SMDS_MeshElement* >::iterator curElemIt;
8197 list< const SMDS_MeshNode* >::iterator nStartIt;
8198 // check if one of them reached the last node
8199 if ( needTheLast ) {
8200 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8201 curElemIt!= curElemList.end();
8202 curElemIt++, nStartIt++ )
8203 if ( *nStartIt == theLastNode ) {
8204 theFaces.push_back( *curElemIt );
8205 theNodes.push_back( *nStartIt );
8209 // find the best free border by the continuations
8210 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8211 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8212 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8213 curElemIt!= curElemList.end();
8214 curElemIt++, nStartIt++ )
8216 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8217 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8218 // find one more free border
8219 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8223 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8224 // choice: clear a worse one
8225 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8226 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8227 contNodes[ iWorse ].clear();
8228 contFaces[ iWorse ].clear();
8231 if ( contNodes[0].empty() && contNodes[1].empty() )
8234 // append the best free border
8235 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8236 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8237 theNodes.pop_back(); // remove nIgnore
8238 theNodes.pop_back(); // remove nStart
8239 theFaces.pop_back(); // remove curElem
8240 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8241 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8242 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8243 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8246 } // several continuations found
8247 } // while ( nStart != theLastNode )
8252 //=======================================================================
8253 //function : CheckFreeBorderNodes
8254 //purpose : Return true if the tree nodes are on a free border
8255 //=======================================================================
8257 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8258 const SMDS_MeshNode* theNode2,
8259 const SMDS_MeshNode* theNode3)
8261 list< const SMDS_MeshNode* > nodes;
8262 list< const SMDS_MeshElement* > faces;
8263 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8266 //=======================================================================
8267 //function : SewFreeBorder
8269 //=======================================================================
8271 SMESH_MeshEditor::Sew_Error
8272 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8273 const SMDS_MeshNode* theBordSecondNode,
8274 const SMDS_MeshNode* theBordLastNode,
8275 const SMDS_MeshNode* theSideFirstNode,
8276 const SMDS_MeshNode* theSideSecondNode,
8277 const SMDS_MeshNode* theSideThirdNode,
8278 const bool theSideIsFreeBorder,
8279 const bool toCreatePolygons,
8280 const bool toCreatePolyedrs)
8282 myLastCreatedElems.Clear();
8283 myLastCreatedNodes.Clear();
8285 MESSAGE("::SewFreeBorder()");
8286 Sew_Error aResult = SEW_OK;
8288 // ====================================
8289 // find side nodes and elements
8290 // ====================================
8292 list< const SMDS_MeshNode* > nSide[ 2 ];
8293 list< const SMDS_MeshElement* > eSide[ 2 ];
8294 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8295 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8299 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8300 nSide[0], eSide[0])) {
8301 MESSAGE(" Free Border 1 not found " );
8302 aResult = SEW_BORDER1_NOT_FOUND;
8304 if (theSideIsFreeBorder) {
8307 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8308 nSide[1], eSide[1])) {
8309 MESSAGE(" Free Border 2 not found " );
8310 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8313 if ( aResult != SEW_OK )
8316 if (!theSideIsFreeBorder) {
8320 // -------------------------------------------------------------------------
8322 // 1. If nodes to merge are not coincident, move nodes of the free border
8323 // from the coord sys defined by the direction from the first to last
8324 // nodes of the border to the correspondent sys of the side 2
8325 // 2. On the side 2, find the links most co-directed with the correspondent
8326 // links of the free border
8327 // -------------------------------------------------------------------------
8329 // 1. Since sewing may break if there are volumes to split on the side 2,
8330 // we wont move nodes but just compute new coordinates for them
8331 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8332 TNodeXYZMap nBordXYZ;
8333 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8334 list< const SMDS_MeshNode* >::iterator nBordIt;
8336 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8337 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8338 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8339 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8340 double tol2 = 1.e-8;
8341 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8342 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8343 // Need node movement.
8345 // find X and Z axes to create trsf
8346 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8348 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8350 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8353 gp_Ax3 toBordAx( Pb1, Zb, X );
8354 gp_Ax3 fromSideAx( Ps1, Zs, X );
8355 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8357 gp_Trsf toBordSys, fromSide2Sys;
8358 toBordSys.SetTransformation( toBordAx );
8359 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8360 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8363 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8364 const SMDS_MeshNode* n = *nBordIt;
8365 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8366 toBordSys.Transforms( xyz );
8367 fromSide2Sys.Transforms( xyz );
8368 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8372 // just insert nodes XYZ in the nBordXYZ map
8373 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8374 const SMDS_MeshNode* n = *nBordIt;
8375 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8379 // 2. On the side 2, find the links most co-directed with the correspondent
8380 // links of the free border
8382 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8383 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8384 sideNodes.push_back( theSideFirstNode );
8386 bool hasVolumes = false;
8387 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8388 set<long> foundSideLinkIDs, checkedLinkIDs;
8389 SMDS_VolumeTool volume;
8390 //const SMDS_MeshNode* faceNodes[ 4 ];
8392 const SMDS_MeshNode* sideNode;
8393 const SMDS_MeshElement* sideElem;
8394 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8395 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8396 nBordIt = bordNodes.begin();
8398 // border node position and border link direction to compare with
8399 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8400 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8401 // choose next side node by link direction or by closeness to
8402 // the current border node:
8403 bool searchByDir = ( *nBordIt != theBordLastNode );
8405 // find the next node on the Side 2
8407 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8409 checkedLinkIDs.clear();
8410 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8412 // loop on inverse elements of current node (prevSideNode) on the Side 2
8413 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8414 while ( invElemIt->more() )
8416 const SMDS_MeshElement* elem = invElemIt->next();
8417 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8418 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8419 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8420 bool isVolume = volume.Set( elem );
8421 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8422 if ( isVolume ) // --volume
8424 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8425 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8426 if(elem->IsQuadratic()) {
8427 const SMDS_VtkFace* F =
8428 dynamic_cast<const SMDS_VtkFace*>(elem);
8429 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8430 // use special nodes iterator
8431 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8432 while( anIter->more() ) {
8433 nodes[ iNode ] = cast2Node(anIter->next());
8434 if ( nodes[ iNode++ ] == prevSideNode )
8435 iPrevNode = iNode - 1;
8439 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8440 while ( nIt->more() ) {
8441 nodes[ iNode ] = cast2Node( nIt->next() );
8442 if ( nodes[ iNode++ ] == prevSideNode )
8443 iPrevNode = iNode - 1;
8446 // there are 2 links to check
8451 // loop on links, to be precise, on the second node of links
8452 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8453 const SMDS_MeshNode* n = nodes[ iNode ];
8455 if ( !volume.IsLinked( n, prevSideNode ))
8459 if ( iNode ) // a node before prevSideNode
8460 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8461 else // a node after prevSideNode
8462 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8464 // check if this link was already used
8465 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8466 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8467 if (!isJustChecked &&
8468 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8470 // test a link geometrically
8471 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8472 bool linkIsBetter = false;
8473 double dot = 0.0, dist = 0.0;
8474 if ( searchByDir ) { // choose most co-directed link
8475 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8476 linkIsBetter = ( dot > maxDot );
8478 else { // choose link with the node closest to bordPos
8479 dist = ( nextXYZ - bordPos ).SquareModulus();
8480 linkIsBetter = ( dist < minDist );
8482 if ( linkIsBetter ) {
8491 } // loop on inverse elements of prevSideNode
8494 MESSAGE(" Cant find path by links of the Side 2 ");
8495 return SEW_BAD_SIDE_NODES;
8497 sideNodes.push_back( sideNode );
8498 sideElems.push_back( sideElem );
8499 foundSideLinkIDs.insert ( linkID );
8500 prevSideNode = sideNode;
8502 if ( *nBordIt == theBordLastNode )
8503 searchByDir = false;
8505 // find the next border link to compare with
8506 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8507 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8508 // move to next border node if sideNode is before forward border node (bordPos)
8509 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8510 prevBordNode = *nBordIt;
8512 bordPos = nBordXYZ[ *nBordIt ];
8513 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8514 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8518 while ( sideNode != theSideSecondNode );
8520 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8521 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8522 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8524 } // end nodes search on the side 2
8526 // ============================
8527 // sew the border to the side 2
8528 // ============================
8530 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8531 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8533 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8534 if ( toMergeConformal && toCreatePolygons )
8536 // do not merge quadrangles if polygons are OK (IPAL0052824)
8537 eIt[0] = eSide[0].begin();
8538 eIt[1] = eSide[1].begin();
8539 bool allQuads[2] = { true, true };
8540 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8541 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8542 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8544 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8547 TListOfListOfNodes nodeGroupsToMerge;
8548 if (( toMergeConformal ) ||
8549 ( theSideIsFreeBorder && !theSideThirdNode )) {
8551 // all nodes are to be merged
8553 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8554 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8555 nIt[0]++, nIt[1]++ )
8557 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8558 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8559 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8564 // insert new nodes into the border and the side to get equal nb of segments
8566 // get normalized parameters of nodes on the borders
8567 vector< double > param[ 2 ];
8568 param[0].resize( maxNbNodes );
8569 param[1].resize( maxNbNodes );
8571 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8572 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8573 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8574 const SMDS_MeshNode* nPrev = *nIt;
8575 double bordLength = 0;
8576 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8577 const SMDS_MeshNode* nCur = *nIt;
8578 gp_XYZ segment (nCur->X() - nPrev->X(),
8579 nCur->Y() - nPrev->Y(),
8580 nCur->Z() - nPrev->Z());
8581 double segmentLen = segment.Modulus();
8582 bordLength += segmentLen;
8583 param[ iBord ][ iNode ] = bordLength;
8586 // normalize within [0,1]
8587 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8588 param[ iBord ][ iNode ] /= bordLength;
8592 // loop on border segments
8593 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8594 int i[ 2 ] = { 0, 0 };
8595 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8596 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8598 TElemOfNodeListMap insertMap;
8599 TElemOfNodeListMap::iterator insertMapIt;
8601 // key: elem to insert nodes into
8602 // value: 2 nodes to insert between + nodes to be inserted
8604 bool next[ 2 ] = { false, false };
8606 // find min adjacent segment length after sewing
8607 double nextParam = 10., prevParam = 0;
8608 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8609 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8610 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8611 if ( i[ iBord ] > 0 )
8612 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8614 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8615 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8616 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8618 // choose to insert or to merge nodes
8619 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8620 if ( Abs( du ) <= minSegLen * 0.2 ) {
8623 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8624 const SMDS_MeshNode* n0 = *nIt[0];
8625 const SMDS_MeshNode* n1 = *nIt[1];
8626 nodeGroupsToMerge.back().push_back( n1 );
8627 nodeGroupsToMerge.back().push_back( n0 );
8628 // position of node of the border changes due to merge
8629 param[ 0 ][ i[0] ] += du;
8630 // move n1 for the sake of elem shape evaluation during insertion.
8631 // n1 will be removed by MergeNodes() anyway
8632 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8633 next[0] = next[1] = true;
8638 int intoBord = ( du < 0 ) ? 0 : 1;
8639 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8640 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8641 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8642 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8643 if ( intoBord == 1 ) {
8644 // move node of the border to be on a link of elem of the side
8645 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8646 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8647 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8648 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8649 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8651 insertMapIt = insertMap.find( elem );
8652 bool notFound = ( insertMapIt == insertMap.end() );
8653 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8655 // insert into another link of the same element:
8656 // 1. perform insertion into the other link of the elem
8657 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8658 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8659 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8660 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8661 // 2. perform insertion into the link of adjacent faces
8662 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8663 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8665 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8666 InsertNodesIntoLink( seg, n12, n22, nodeList );
8668 if (toCreatePolyedrs) {
8669 // perform insertion into the links of adjacent volumes
8670 UpdateVolumes(n12, n22, nodeList);
8672 // 3. find an element appeared on n1 and n2 after the insertion
8673 insertMap.erase( elem );
8674 elem = findAdjacentFace( n1, n2, 0 );
8676 if ( notFound || otherLink ) {
8677 // add element and nodes of the side into the insertMap
8678 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8679 (*insertMapIt).second.push_back( n1 );
8680 (*insertMapIt).second.push_back( n2 );
8682 // add node to be inserted into elem
8683 (*insertMapIt).second.push_back( nIns );
8684 next[ 1 - intoBord ] = true;
8687 // go to the next segment
8688 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8689 if ( next[ iBord ] ) {
8690 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8692 nPrev[ iBord ] = *nIt[ iBord ];
8693 nIt[ iBord ]++; i[ iBord ]++;
8697 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8699 // perform insertion of nodes into elements
8701 for (insertMapIt = insertMap.begin();
8702 insertMapIt != insertMap.end();
8705 const SMDS_MeshElement* elem = (*insertMapIt).first;
8706 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8707 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8708 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8710 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8712 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8713 InsertNodesIntoLink( seg, n1, n2, nodeList );
8716 if ( !theSideIsFreeBorder ) {
8717 // look for and insert nodes into the faces adjacent to elem
8718 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8719 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8722 if (toCreatePolyedrs) {
8723 // perform insertion into the links of adjacent volumes
8724 UpdateVolumes(n1, n2, nodeList);
8727 } // end: insert new nodes
8729 MergeNodes ( nodeGroupsToMerge );
8732 // Remove coincident segments
8735 TIDSortedElemSet segments;
8736 SMESH_SequenceOfElemPtr newFaces;
8737 for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8739 if ( !myLastCreatedElems(i) ) continue;
8740 if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8741 segments.insert( segments.end(), myLastCreatedElems(i) );
8743 newFaces.Append( myLastCreatedElems(i) );
8746 TListOfListOfElementsID equalGroups;
8747 FindEqualElements( segments, equalGroups );
8748 if ( !equalGroups.empty() )
8750 // remove from segments those that will be removed
8751 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8752 for ( ; itGroups != equalGroups.end(); ++itGroups )
8754 list< int >& group = *itGroups;
8755 list< int >::iterator id = group.begin();
8756 for ( ++id; id != group.end(); ++id )
8757 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8758 segments.erase( seg );
8760 // remove equal segments
8761 MergeElements( equalGroups );
8763 // restore myLastCreatedElems
8764 myLastCreatedElems = newFaces;
8765 TIDSortedElemSet::iterator seg = segments.begin();
8766 for ( ; seg != segments.end(); ++seg )
8767 myLastCreatedElems.Append( *seg );
8773 //=======================================================================
8774 //function : InsertNodesIntoLink
8775 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8776 // and theBetweenNode2 and split theElement
8777 //=======================================================================
8779 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8780 const SMDS_MeshNode* theBetweenNode1,
8781 const SMDS_MeshNode* theBetweenNode2,
8782 list<const SMDS_MeshNode*>& theNodesToInsert,
8783 const bool toCreatePoly)
8785 if ( !theElement ) return;
8787 SMESHDS_Mesh *aMesh = GetMeshDS();
8788 vector<const SMDS_MeshElement*> newElems;
8790 if ( theElement->GetType() == SMDSAbs_Edge )
8792 theNodesToInsert.push_front( theBetweenNode1 );
8793 theNodesToInsert.push_back ( theBetweenNode2 );
8794 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8795 const SMDS_MeshNode* n1 = *n;
8796 for ( ++n; n != theNodesToInsert.end(); ++n )
8798 const SMDS_MeshNode* n2 = *n;
8799 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8800 AddToSameGroups( seg, theElement, aMesh );
8802 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8805 theNodesToInsert.pop_front();
8806 theNodesToInsert.pop_back();
8808 if ( theElement->IsQuadratic() ) // add a not split part
8810 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8811 theElement->end_nodes() );
8812 int iOther = 0, nbN = nodes.size();
8813 for ( ; iOther < nbN; ++iOther )
8814 if ( nodes[iOther] != theBetweenNode1 &&
8815 nodes[iOther] != theBetweenNode2 )
8819 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8820 AddToSameGroups( seg, theElement, aMesh );
8822 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8824 else if ( iOther == 2 )
8826 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8827 AddToSameGroups( seg, theElement, aMesh );
8829 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8832 // treat new elements
8833 for ( size_t i = 0; i < newElems.size(); ++i )
8836 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8837 myLastCreatedElems.Append( newElems[i] );
8839 ReplaceElemInGroups( theElement, newElems, aMesh );
8840 aMesh->RemoveElement( theElement );
8843 } // if ( theElement->GetType() == SMDSAbs_Edge )
8845 const SMDS_MeshElement* theFace = theElement;
8846 if ( theFace->GetType() != SMDSAbs_Face ) return;
8848 // find indices of 2 link nodes and of the rest nodes
8849 int iNode = 0, il1, il2, i3, i4;
8850 il1 = il2 = i3 = i4 = -1;
8851 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8853 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8854 while ( nodeIt->more() ) {
8855 const SMDS_MeshNode* n = nodeIt->next();
8856 if ( n == theBetweenNode1 )
8858 else if ( n == theBetweenNode2 )
8864 nodes[ iNode++ ] = n;
8866 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8869 // arrange link nodes to go one after another regarding the face orientation
8870 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8871 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8876 aNodesToInsert.reverse();
8878 // check that not link nodes of a quadrangles are in good order
8879 int nbFaceNodes = theFace->NbNodes();
8880 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8886 if (toCreatePoly || theFace->IsPoly()) {
8889 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8891 // add nodes of face up to first node of link
8894 if ( theFace->IsQuadratic() ) {
8895 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8896 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8897 // use special nodes iterator
8898 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8899 while( anIter->more() && !isFLN ) {
8900 const SMDS_MeshNode* n = cast2Node(anIter->next());
8901 poly_nodes[iNode++] = n;
8902 if (n == nodes[il1]) {
8906 // add nodes to insert
8907 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8908 for (; nIt != aNodesToInsert.end(); nIt++) {
8909 poly_nodes[iNode++] = *nIt;
8911 // add nodes of face starting from last node of link
8912 while ( anIter->more() ) {
8913 poly_nodes[iNode++] = cast2Node(anIter->next());
8917 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8918 while ( nodeIt->more() && !isFLN ) {
8919 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8920 poly_nodes[iNode++] = n;
8921 if (n == nodes[il1]) {
8925 // add nodes to insert
8926 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8927 for (; nIt != aNodesToInsert.end(); nIt++) {
8928 poly_nodes[iNode++] = *nIt;
8930 // add nodes of face starting from last node of link
8931 while ( nodeIt->more() ) {
8932 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8933 poly_nodes[iNode++] = n;
8938 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8941 else if ( !theFace->IsQuadratic() )
8943 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8944 int nbLinkNodes = 2 + aNodesToInsert.size();
8945 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8946 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8947 linkNodes[ 0 ] = nodes[ il1 ];
8948 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8949 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8950 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8951 linkNodes[ iNode++ ] = *nIt;
8953 // decide how to split a quadrangle: compare possible variants
8954 // and choose which of splits to be a quadrangle
8955 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8956 if ( nbFaceNodes == 3 ) {
8957 iBestQuad = nbSplits;
8960 else if ( nbFaceNodes == 4 ) {
8961 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8962 double aBestRate = DBL_MAX;
8963 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8965 double aBadRate = 0;
8966 // evaluate elements quality
8967 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8968 if ( iSplit == iQuad ) {
8969 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8973 aBadRate += getBadRate( &quad, aCrit );
8976 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8978 nodes[ iSplit < iQuad ? i4 : i3 ]);
8979 aBadRate += getBadRate( &tria, aCrit );
8983 if ( aBadRate < aBestRate ) {
8985 aBestRate = aBadRate;
8990 // create new elements
8992 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8993 SMDS_MeshElement* newElem = 0;
8994 if ( iSplit == iBestQuad )
8995 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9000 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9002 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
9005 const SMDS_MeshNode* newNodes[ 4 ];
9006 newNodes[ 0 ] = linkNodes[ i1 ];
9007 newNodes[ 1 ] = linkNodes[ i2 ];
9008 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9009 newNodes[ 3 ] = nodes[ i4 ];
9010 if (iSplit == iBestQuad)
9011 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
9013 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
9015 } // end if(!theFace->IsQuadratic())
9017 else { // theFace is quadratic
9018 // we have to split theFace on simple triangles and one simple quadrangle
9020 int nbshift = tmp*2;
9021 // shift nodes in nodes[] by nbshift
9023 for(i=0; i<nbshift; i++) {
9024 const SMDS_MeshNode* n = nodes[0];
9025 for(j=0; j<nbFaceNodes-1; j++) {
9026 nodes[j] = nodes[j+1];
9028 nodes[nbFaceNodes-1] = n;
9030 il1 = il1 - nbshift;
9031 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9032 // n0 n1 n2 n0 n1 n2
9033 // +-----+-----+ +-----+-----+
9042 // create new elements
9044 if ( nbFaceNodes == 6 ) { // quadratic triangle
9045 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9046 if ( theFace->IsMediumNode(nodes[il1]) ) {
9047 // create quadrangle
9048 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9054 // create quadrangle
9055 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9061 else { // nbFaceNodes==8 - quadratic quadrangle
9062 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9063 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9064 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9065 if ( theFace->IsMediumNode( nodes[ il1 ])) {
9066 // create quadrangle
9067 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9073 // create quadrangle
9074 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9080 // create needed triangles using n1,n2,n3 and inserted nodes
9081 int nbn = 2 + aNodesToInsert.size();
9082 vector<const SMDS_MeshNode*> aNodes(nbn);
9083 aNodes[0 ] = nodes[n1];
9084 aNodes[nbn-1] = nodes[n2];
9085 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9086 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9087 aNodes[iNode++] = *nIt;
9089 for ( i = 1; i < nbn; i++ )
9090 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9093 // remove the old face
9094 for ( size_t i = 0; i < newElems.size(); ++i )
9097 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9098 myLastCreatedElems.Append( newElems[i] );
9100 ReplaceElemInGroups( theFace, newElems, aMesh );
9101 aMesh->RemoveElement(theFace);
9103 } // InsertNodesIntoLink()
9105 //=======================================================================
9106 //function : UpdateVolumes
9108 //=======================================================================
9110 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9111 const SMDS_MeshNode* theBetweenNode2,
9112 list<const SMDS_MeshNode*>& theNodesToInsert)
9114 myLastCreatedElems.Clear();
9115 myLastCreatedNodes.Clear();
9117 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9118 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9119 const SMDS_MeshElement* elem = invElemIt->next();
9121 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9122 SMDS_VolumeTool aVolume (elem);
9123 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9126 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9127 int iface, nbFaces = aVolume.NbFaces();
9128 vector<const SMDS_MeshNode *> poly_nodes;
9129 vector<int> quantities (nbFaces);
9131 for (iface = 0; iface < nbFaces; iface++) {
9132 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9133 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9134 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9136 for (int inode = 0; inode < nbFaceNodes; inode++) {
9137 poly_nodes.push_back(faceNodes[inode]);
9139 if (nbInserted == 0) {
9140 if (faceNodes[inode] == theBetweenNode1) {
9141 if (faceNodes[inode + 1] == theBetweenNode2) {
9142 nbInserted = theNodesToInsert.size();
9144 // add nodes to insert
9145 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9146 for (; nIt != theNodesToInsert.end(); nIt++) {
9147 poly_nodes.push_back(*nIt);
9151 else if (faceNodes[inode] == theBetweenNode2) {
9152 if (faceNodes[inode + 1] == theBetweenNode1) {
9153 nbInserted = theNodesToInsert.size();
9155 // add nodes to insert in reversed order
9156 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9158 for (; nIt != theNodesToInsert.begin(); nIt--) {
9159 poly_nodes.push_back(*nIt);
9161 poly_nodes.push_back(*nIt);
9168 quantities[iface] = nbFaceNodes + nbInserted;
9171 // Replace the volume
9172 SMESHDS_Mesh *aMesh = GetMeshDS();
9174 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9176 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9177 myLastCreatedElems.Append( newElem );
9178 ReplaceElemInGroups( elem, newElem, aMesh );
9180 aMesh->RemoveElement( elem );
9186 //================================================================================
9188 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9190 //================================================================================
9192 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9193 vector<const SMDS_MeshNode *> & nodes,
9194 vector<int> & nbNodeInFaces )
9197 nbNodeInFaces.clear();
9198 SMDS_VolumeTool vTool ( elem );
9199 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9201 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9202 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9203 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9208 //=======================================================================
9210 * \brief Convert elements contained in a sub-mesh to quadratic
9211 * \return int - nb of checked elements
9213 //=======================================================================
9215 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9216 SMESH_MesherHelper& theHelper,
9217 const bool theForce3d)
9220 if( !theSm ) return nbElem;
9222 vector<int> nbNodeInFaces;
9223 vector<const SMDS_MeshNode *> nodes;
9224 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9225 while(ElemItr->more())
9228 const SMDS_MeshElement* elem = ElemItr->next();
9229 if( !elem ) continue;
9231 // analyse a necessity of conversion
9232 const SMDSAbs_ElementType aType = elem->GetType();
9233 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9235 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9236 bool hasCentralNodes = false;
9237 if ( elem->IsQuadratic() )
9240 switch ( aGeomType ) {
9241 case SMDSEntity_Quad_Triangle:
9242 case SMDSEntity_Quad_Quadrangle:
9243 case SMDSEntity_Quad_Hexa:
9244 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9246 case SMDSEntity_BiQuad_Triangle:
9247 case SMDSEntity_BiQuad_Quadrangle:
9248 case SMDSEntity_TriQuad_Hexa:
9249 alreadyOK = theHelper.GetIsBiQuadratic();
9250 hasCentralNodes = true;
9255 // take into account already present modium nodes
9257 case SMDSAbs_Volume:
9258 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9260 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9262 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9268 // get elem data needed to re-create it
9270 const int id = elem->GetID();
9271 const int nbNodes = elem->NbCornerNodes();
9272 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9273 if ( aGeomType == SMDSEntity_Polyhedra )
9274 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9275 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9276 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9278 // remove a linear element
9279 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9281 // remove central nodes of biquadratic elements (biquad->quad convertion)
9282 if ( hasCentralNodes )
9283 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9284 if ( nodes[i]->NbInverseElements() == 0 )
9285 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9287 const SMDS_MeshElement* NewElem = 0;
9293 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9301 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9304 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9307 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9311 case SMDSAbs_Volume :
9315 case SMDSEntity_Tetra:
9316 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9318 case SMDSEntity_Pyramid:
9319 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9321 case SMDSEntity_Penta:
9322 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9324 case SMDSEntity_Hexa:
9325 case SMDSEntity_Quad_Hexa:
9326 case SMDSEntity_TriQuad_Hexa:
9327 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9328 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9330 case SMDSEntity_Hexagonal_Prism:
9332 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9339 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9340 if( NewElem && NewElem->getshapeId() < 1 )
9341 theSm->AddElement( NewElem );
9345 //=======================================================================
9346 //function : ConvertToQuadratic
9348 //=======================================================================
9350 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9352 SMESHDS_Mesh* meshDS = GetMeshDS();
9354 SMESH_MesherHelper aHelper(*myMesh);
9356 aHelper.SetIsQuadratic( true );
9357 aHelper.SetIsBiQuadratic( theToBiQuad );
9358 aHelper.SetElementsOnShape(true);
9359 aHelper.ToFixNodeParameters( true );
9361 // convert elements assigned to sub-meshes
9362 int nbCheckedElems = 0;
9363 if ( myMesh->HasShapeToMesh() )
9365 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9367 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9368 while ( smIt->more() ) {
9369 SMESH_subMesh* sm = smIt->next();
9370 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9371 aHelper.SetSubShape( sm->GetSubShape() );
9372 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9378 // convert elements NOT assigned to sub-meshes
9379 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9380 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9382 aHelper.SetElementsOnShape(false);
9383 SMESHDS_SubMesh *smDS = 0;
9386 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9387 while( aEdgeItr->more() )
9389 const SMDS_MeshEdge* edge = aEdgeItr->next();
9390 if ( !edge->IsQuadratic() )
9392 int id = edge->GetID();
9393 const SMDS_MeshNode* n1 = edge->GetNode(0);
9394 const SMDS_MeshNode* n2 = edge->GetNode(1);
9396 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9398 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9399 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9403 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9408 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9409 while( aFaceItr->more() )
9411 const SMDS_MeshFace* face = aFaceItr->next();
9412 if ( !face ) continue;
9414 const SMDSAbs_EntityType type = face->GetEntityType();
9418 case SMDSEntity_Quad_Triangle:
9419 case SMDSEntity_Quad_Quadrangle:
9420 alreadyOK = !theToBiQuad;
9421 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9423 case SMDSEntity_BiQuad_Triangle:
9424 case SMDSEntity_BiQuad_Quadrangle:
9425 alreadyOK = theToBiQuad;
9426 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9428 default: alreadyOK = false;
9433 const int id = face->GetID();
9434 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9436 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9438 SMDS_MeshFace * NewFace = 0;
9441 case SMDSEntity_Triangle:
9442 case SMDSEntity_Quad_Triangle:
9443 case SMDSEntity_BiQuad_Triangle:
9444 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9445 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9446 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9449 case SMDSEntity_Quadrangle:
9450 case SMDSEntity_Quad_Quadrangle:
9451 case SMDSEntity_BiQuad_Quadrangle:
9452 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9453 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9454 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9458 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9460 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9464 vector<int> nbNodeInFaces;
9465 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9466 while(aVolumeItr->more())
9468 const SMDS_MeshVolume* volume = aVolumeItr->next();
9469 if ( !volume ) continue;
9471 const SMDSAbs_EntityType type = volume->GetEntityType();
9472 if ( volume->IsQuadratic() )
9477 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9478 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9479 default: alreadyOK = true;
9483 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9487 const int id = volume->GetID();
9488 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9489 if ( type == SMDSEntity_Polyhedra )
9490 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9491 else if ( type == SMDSEntity_Hexagonal_Prism )
9492 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9494 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9496 SMDS_MeshVolume * NewVolume = 0;
9499 case SMDSEntity_Tetra:
9500 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9502 case SMDSEntity_Hexa:
9503 case SMDSEntity_Quad_Hexa:
9504 case SMDSEntity_TriQuad_Hexa:
9505 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9506 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9507 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9508 if ( nodes[i]->NbInverseElements() == 0 )
9509 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9511 case SMDSEntity_Pyramid:
9512 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9513 nodes[3], nodes[4], id, theForce3d);
9515 case SMDSEntity_Penta:
9516 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9517 nodes[3], nodes[4], nodes[5], id, theForce3d);
9519 case SMDSEntity_Hexagonal_Prism:
9521 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9523 ReplaceElemInGroups(volume, NewVolume, meshDS);
9528 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9529 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9530 // aHelper.FixQuadraticElements(myError);
9531 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9535 //================================================================================
9537 * \brief Makes given elements quadratic
9538 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9539 * \param theElements - elements to make quadratic
9541 //================================================================================
9543 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9544 TIDSortedElemSet& theElements,
9545 const bool theToBiQuad)
9547 if ( theElements.empty() ) return;
9549 // we believe that all theElements are of the same type
9550 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9552 // get all nodes shared by theElements
9553 TIDSortedNodeSet allNodes;
9554 TIDSortedElemSet::iterator eIt = theElements.begin();
9555 for ( ; eIt != theElements.end(); ++eIt )
9556 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9558 // complete theElements with elements of lower dim whose all nodes are in allNodes
9560 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9561 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9562 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9563 for ( ; nIt != allNodes.end(); ++nIt )
9565 const SMDS_MeshNode* n = *nIt;
9566 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9567 while ( invIt->more() )
9569 const SMDS_MeshElement* e = invIt->next();
9570 const SMDSAbs_ElementType type = e->GetType();
9571 if ( e->IsQuadratic() )
9573 quadAdjacentElems[ type ].insert( e );
9576 switch ( e->GetEntityType() ) {
9577 case SMDSEntity_Quad_Triangle:
9578 case SMDSEntity_Quad_Quadrangle:
9579 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9580 case SMDSEntity_BiQuad_Triangle:
9581 case SMDSEntity_BiQuad_Quadrangle:
9582 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9583 default: alreadyOK = true;
9588 if ( type >= elemType )
9589 continue; // same type or more complex linear element
9591 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9592 continue; // e is already checked
9596 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9597 while ( nodeIt->more() && allIn )
9598 allIn = allNodes.count( nodeIt->next() );
9600 theElements.insert(e );
9604 SMESH_MesherHelper helper(*myMesh);
9605 helper.SetIsQuadratic( true );
9606 helper.SetIsBiQuadratic( theToBiQuad );
9608 // add links of quadratic adjacent elements to the helper
9610 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9611 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9612 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9614 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9616 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9617 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9618 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9620 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9622 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9623 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9624 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9626 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9629 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9631 SMESHDS_Mesh* meshDS = GetMeshDS();
9632 SMESHDS_SubMesh* smDS = 0;
9633 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9635 const SMDS_MeshElement* elem = *eIt;
9638 int nbCentralNodes = 0;
9639 switch ( elem->GetEntityType() ) {
9640 // linear convertible
9641 case SMDSEntity_Edge:
9642 case SMDSEntity_Triangle:
9643 case SMDSEntity_Quadrangle:
9644 case SMDSEntity_Tetra:
9645 case SMDSEntity_Pyramid:
9646 case SMDSEntity_Hexa:
9647 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9648 // quadratic that can become bi-quadratic
9649 case SMDSEntity_Quad_Triangle:
9650 case SMDSEntity_Quad_Quadrangle:
9651 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9653 case SMDSEntity_BiQuad_Triangle:
9654 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9655 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9657 default: alreadyOK = true;
9659 if ( alreadyOK ) continue;
9661 const SMDSAbs_ElementType type = elem->GetType();
9662 const int id = elem->GetID();
9663 const int nbNodes = elem->NbCornerNodes();
9664 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9666 helper.SetSubShape( elem->getshapeId() );
9668 if ( !smDS || !smDS->Contains( elem ))
9669 smDS = meshDS->MeshElements( elem->getshapeId() );
9670 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9672 SMDS_MeshElement * newElem = 0;
9675 case 4: // cases for most frequently used element types go first (for optimization)
9676 if ( type == SMDSAbs_Volume )
9677 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9679 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9682 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9683 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9686 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9689 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9692 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9693 nodes[4], id, theForce3d);
9696 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9697 nodes[4], nodes[5], id, theForce3d);
9701 ReplaceElemInGroups( elem, newElem, meshDS);
9702 if( newElem && smDS )
9703 smDS->AddElement( newElem );
9705 // remove central nodes
9706 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9707 if ( nodes[i]->NbInverseElements() == 0 )
9708 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9710 } // loop on theElements
9713 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9714 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9715 // helper.FixQuadraticElements( myError );
9716 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9720 //=======================================================================
9722 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9723 * \return int - nb of checked elements
9725 //=======================================================================
9727 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9728 SMDS_ElemIteratorPtr theItr,
9729 const int theShapeID)
9732 SMESHDS_Mesh* meshDS = GetMeshDS();
9733 ElemFeatures elemType;
9734 vector<const SMDS_MeshNode *> nodes;
9736 while( theItr->more() )
9738 const SMDS_MeshElement* elem = theItr->next();
9740 if( elem && elem->IsQuadratic())
9743 int nbCornerNodes = elem->NbCornerNodes();
9744 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9746 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9748 //remove a quadratic element
9749 if ( !theSm || !theSm->Contains( elem ))
9750 theSm = meshDS->MeshElements( elem->getshapeId() );
9751 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9753 // remove medium nodes
9754 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9755 if ( nodes[i]->NbInverseElements() == 0 )
9756 meshDS->RemoveFreeNode( nodes[i], theSm );
9758 // add a linear element
9759 nodes.resize( nbCornerNodes );
9760 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9761 ReplaceElemInGroups(elem, newElem, meshDS);
9762 if( theSm && newElem )
9763 theSm->AddElement( newElem );
9769 //=======================================================================
9770 //function : ConvertFromQuadratic
9772 //=======================================================================
9774 bool SMESH_MeshEditor::ConvertFromQuadratic()
9776 int nbCheckedElems = 0;
9777 if ( myMesh->HasShapeToMesh() )
9779 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9781 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9782 while ( smIt->more() ) {
9783 SMESH_subMesh* sm = smIt->next();
9784 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9785 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9791 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9792 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9794 SMESHDS_SubMesh *aSM = 0;
9795 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9803 //================================================================================
9805 * \brief Return true if all medium nodes of the element are in the node set
9807 //================================================================================
9809 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9811 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9812 if ( !nodeSet.count( elem->GetNode(i) ))
9818 //================================================================================
9820 * \brief Makes given elements linear
9822 //================================================================================
9824 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9826 if ( theElements.empty() ) return;
9828 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9829 set<int> mediumNodeIDs;
9830 TIDSortedElemSet::iterator eIt = theElements.begin();
9831 for ( ; eIt != theElements.end(); ++eIt )
9833 const SMDS_MeshElement* e = *eIt;
9834 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9835 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9838 // replace given elements by linear ones
9839 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9840 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9842 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9843 // except those elements sharing medium nodes of quadratic element whose medium nodes
9844 // are not all in mediumNodeIDs
9846 // get remaining medium nodes
9847 TIDSortedNodeSet mediumNodes;
9848 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9849 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9850 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9851 mediumNodes.insert( mediumNodes.end(), n );
9853 // find more quadratic elements to convert
9854 TIDSortedElemSet moreElemsToConvert;
9855 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9856 for ( ; nIt != mediumNodes.end(); ++nIt )
9858 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9859 while ( invIt->more() )
9861 const SMDS_MeshElement* e = invIt->next();
9862 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9864 // find a more complex element including e and
9865 // whose medium nodes are not in mediumNodes
9866 bool complexFound = false;
9867 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9869 SMDS_ElemIteratorPtr invIt2 =
9870 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9871 while ( invIt2->more() )
9873 const SMDS_MeshElement* eComplex = invIt2->next();
9874 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9876 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9877 if ( nbCommonNodes == e->NbNodes())
9879 complexFound = true;
9880 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9886 if ( !complexFound )
9887 moreElemsToConvert.insert( e );
9891 elemIt = elemSetIterator( moreElemsToConvert );
9892 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9895 //=======================================================================
9896 //function : SewSideElements
9898 //=======================================================================
9900 SMESH_MeshEditor::Sew_Error
9901 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9902 TIDSortedElemSet& theSide2,
9903 const SMDS_MeshNode* theFirstNode1,
9904 const SMDS_MeshNode* theFirstNode2,
9905 const SMDS_MeshNode* theSecondNode1,
9906 const SMDS_MeshNode* theSecondNode2)
9908 myLastCreatedElems.Clear();
9909 myLastCreatedNodes.Clear();
9911 MESSAGE ("::::SewSideElements()");
9912 if ( theSide1.size() != theSide2.size() )
9913 return SEW_DIFF_NB_OF_ELEMENTS;
9915 Sew_Error aResult = SEW_OK;
9917 // 1. Build set of faces representing each side
9918 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9919 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9921 // =======================================================================
9922 // 1. Build set of faces representing each side:
9923 // =======================================================================
9924 // a. build set of nodes belonging to faces
9925 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9926 // c. create temporary faces representing side of volumes if correspondent
9927 // face does not exist
9929 SMESHDS_Mesh* aMesh = GetMeshDS();
9930 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9931 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9932 TIDSortedElemSet faceSet1, faceSet2;
9933 set<const SMDS_MeshElement*> volSet1, volSet2;
9934 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9935 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9936 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9937 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9938 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9939 int iSide, iFace, iNode;
9941 list<const SMDS_MeshElement* > tempFaceList;
9942 for ( iSide = 0; iSide < 2; iSide++ ) {
9943 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9944 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9945 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9946 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9947 set<const SMDS_MeshElement*>::iterator vIt;
9948 TIDSortedElemSet::iterator eIt;
9949 set<const SMDS_MeshNode*>::iterator nIt;
9951 // check that given nodes belong to given elements
9952 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9953 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9954 int firstIndex = -1, secondIndex = -1;
9955 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9956 const SMDS_MeshElement* elem = *eIt;
9957 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9958 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9959 if ( firstIndex > -1 && secondIndex > -1 ) break;
9961 if ( firstIndex < 0 || secondIndex < 0 ) {
9962 // we can simply return until temporary faces created
9963 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9966 // -----------------------------------------------------------
9967 // 1a. Collect nodes of existing faces
9968 // and build set of face nodes in order to detect missing
9969 // faces corresponding to sides of volumes
9970 // -----------------------------------------------------------
9972 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9974 // loop on the given element of a side
9975 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9976 //const SMDS_MeshElement* elem = *eIt;
9977 const SMDS_MeshElement* elem = *eIt;
9978 if ( elem->GetType() == SMDSAbs_Face ) {
9979 faceSet->insert( elem );
9980 set <const SMDS_MeshNode*> faceNodeSet;
9981 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9982 while ( nodeIt->more() ) {
9983 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9984 nodeSet->insert( n );
9985 faceNodeSet.insert( n );
9987 setOfFaceNodeSet.insert( faceNodeSet );
9989 else if ( elem->GetType() == SMDSAbs_Volume )
9990 volSet->insert( elem );
9992 // ------------------------------------------------------------------------------
9993 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9994 // ------------------------------------------------------------------------------
9996 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9997 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9998 while ( fIt->more() ) { // loop on faces sharing a node
9999 const SMDS_MeshElement* f = fIt->next();
10000 if ( faceSet->find( f ) == faceSet->end() ) {
10001 // check if all nodes are in nodeSet and
10002 // complete setOfFaceNodeSet if they are
10003 set <const SMDS_MeshNode*> faceNodeSet;
10004 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10005 bool allInSet = true;
10006 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10007 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10008 if ( nodeSet->find( n ) == nodeSet->end() )
10011 faceNodeSet.insert( n );
10014 faceSet->insert( f );
10015 setOfFaceNodeSet.insert( faceNodeSet );
10021 // -------------------------------------------------------------------------
10022 // 1c. Create temporary faces representing sides of volumes if correspondent
10023 // face does not exist
10024 // -------------------------------------------------------------------------
10026 if ( !volSet->empty() ) {
10027 //int nodeSetSize = nodeSet->size();
10029 // loop on given volumes
10030 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10031 SMDS_VolumeTool vol (*vIt);
10032 // loop on volume faces: find free faces
10033 // --------------------------------------
10034 list<const SMDS_MeshElement* > freeFaceList;
10035 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10036 if ( !vol.IsFreeFace( iFace ))
10038 // check if there is already a face with same nodes in a face set
10039 const SMDS_MeshElement* aFreeFace = 0;
10040 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10041 int nbNodes = vol.NbFaceNodes( iFace );
10042 set <const SMDS_MeshNode*> faceNodeSet;
10043 vol.GetFaceNodes( iFace, faceNodeSet );
10044 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10046 // no such a face is given but it still can exist, check it
10047 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10048 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10050 if ( !aFreeFace ) {
10051 // create a temporary face
10052 if ( nbNodes == 3 ) {
10053 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10054 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10056 else if ( nbNodes == 4 ) {
10057 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10058 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10061 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10062 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10063 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10066 tempFaceList.push_back( aFreeFace );
10070 freeFaceList.push_back( aFreeFace );
10072 } // loop on faces of a volume
10074 // choose one of several free faces of a volume
10075 // --------------------------------------------
10076 if ( freeFaceList.size() > 1 ) {
10077 // choose a face having max nb of nodes shared by other elems of a side
10078 int maxNbNodes = -1;
10079 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10080 while ( fIt != freeFaceList.end() ) { // loop on free faces
10081 int nbSharedNodes = 0;
10082 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10083 while ( nodeIt->more() ) { // loop on free face nodes
10084 const SMDS_MeshNode* n =
10085 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10086 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10087 while ( invElemIt->more() ) {
10088 const SMDS_MeshElement* e = invElemIt->next();
10089 nbSharedNodes += faceSet->count( e );
10090 nbSharedNodes += elemSet->count( e );
10093 if ( nbSharedNodes > maxNbNodes ) {
10094 maxNbNodes = nbSharedNodes;
10095 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10097 else if ( nbSharedNodes == maxNbNodes ) {
10101 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10104 if ( freeFaceList.size() > 1 )
10106 // could not choose one face, use another way
10107 // choose a face most close to the bary center of the opposite side
10108 gp_XYZ aBC( 0., 0., 0. );
10109 set <const SMDS_MeshNode*> addedNodes;
10110 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10111 eIt = elemSet2->begin();
10112 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10113 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10114 while ( nodeIt->more() ) { // loop on free face nodes
10115 const SMDS_MeshNode* n =
10116 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10117 if ( addedNodes.insert( n ).second )
10118 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10121 aBC /= addedNodes.size();
10122 double minDist = DBL_MAX;
10123 fIt = freeFaceList.begin();
10124 while ( fIt != freeFaceList.end() ) { // loop on free faces
10126 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10127 while ( nodeIt->more() ) { // loop on free face nodes
10128 const SMDS_MeshNode* n =
10129 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10130 gp_XYZ p( n->X(),n->Y(),n->Z() );
10131 dist += ( aBC - p ).SquareModulus();
10133 if ( dist < minDist ) {
10135 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10138 fIt = freeFaceList.erase( fIt++ );
10141 } // choose one of several free faces of a volume
10143 if ( freeFaceList.size() == 1 ) {
10144 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10145 faceSet->insert( aFreeFace );
10146 // complete a node set with nodes of a found free face
10147 // for ( iNode = 0; iNode < ; iNode++ )
10148 // nodeSet->insert( fNodes[ iNode ] );
10151 } // loop on volumes of a side
10153 // // complete a set of faces if new nodes in a nodeSet appeared
10154 // // ----------------------------------------------------------
10155 // if ( nodeSetSize != nodeSet->size() ) {
10156 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10157 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10158 // while ( fIt->more() ) { // loop on faces sharing a node
10159 // const SMDS_MeshElement* f = fIt->next();
10160 // if ( faceSet->find( f ) == faceSet->end() ) {
10161 // // check if all nodes are in nodeSet and
10162 // // complete setOfFaceNodeSet if they are
10163 // set <const SMDS_MeshNode*> faceNodeSet;
10164 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10165 // bool allInSet = true;
10166 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10167 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10168 // if ( nodeSet->find( n ) == nodeSet->end() )
10169 // allInSet = false;
10171 // faceNodeSet.insert( n );
10173 // if ( allInSet ) {
10174 // faceSet->insert( f );
10175 // setOfFaceNodeSet.insert( faceNodeSet );
10181 } // Create temporary faces, if there are volumes given
10184 if ( faceSet1.size() != faceSet2.size() ) {
10185 // delete temporary faces: they are in reverseElements of actual nodes
10186 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10187 // while ( tmpFaceIt->more() )
10188 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10189 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10190 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10191 // aMesh->RemoveElement(*tmpFaceIt);
10192 MESSAGE("Diff nb of faces");
10193 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10196 // ============================================================
10197 // 2. Find nodes to merge:
10198 // bind a node to remove to a node to put instead
10199 // ============================================================
10201 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10202 if ( theFirstNode1 != theFirstNode2 )
10203 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10204 if ( theSecondNode1 != theSecondNode2 )
10205 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10207 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10208 set< long > linkIdSet; // links to process
10209 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10211 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10212 list< NLink > linkList[2];
10213 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10214 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10215 // loop on links in linkList; find faces by links and append links
10216 // of the found faces to linkList
10217 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10218 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10220 NLink link[] = { *linkIt[0], *linkIt[1] };
10221 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10222 if ( !linkIdSet.count( linkID ) )
10225 // by links, find faces in the face sets,
10226 // and find indices of link nodes in the found faces;
10227 // in a face set, there is only one or no face sharing a link
10228 // ---------------------------------------------------------------
10230 const SMDS_MeshElement* face[] = { 0, 0 };
10231 vector<const SMDS_MeshNode*> fnodes[2];
10232 int iLinkNode[2][2];
10233 TIDSortedElemSet avoidSet;
10234 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10235 const SMDS_MeshNode* n1 = link[iSide].first;
10236 const SMDS_MeshNode* n2 = link[iSide].second;
10237 //cout << "Side " << iSide << " ";
10238 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10239 // find a face by two link nodes
10240 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10241 *faceSetPtr[ iSide ], avoidSet,
10242 &iLinkNode[iSide][0],
10243 &iLinkNode[iSide][1] );
10244 if ( face[ iSide ])
10246 //cout << " F " << face[ iSide]->GetID() <<endl;
10247 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10248 // put face nodes to fnodes
10249 if ( face[ iSide ]->IsQuadratic() )
10251 // use interlaced nodes iterator
10252 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10253 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10254 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10255 while ( nIter->more() )
10256 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10260 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10261 face[ iSide ]->end_nodes() );
10263 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10267 // check similarity of elements of the sides
10268 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10269 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10270 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10271 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10274 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10276 break; // do not return because it's necessary to remove tmp faces
10279 // set nodes to merge
10280 // -------------------
10282 if ( face[0] && face[1] ) {
10283 const int nbNodes = face[0]->NbNodes();
10284 if ( nbNodes != face[1]->NbNodes() ) {
10285 MESSAGE("Diff nb of face nodes");
10286 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10287 break; // do not return because it s necessary to remove tmp faces
10289 bool reverse[] = { false, false }; // order of nodes in the link
10290 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10291 // analyse link orientation in faces
10292 int i1 = iLinkNode[ iSide ][ 0 ];
10293 int i2 = iLinkNode[ iSide ][ 1 ];
10294 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10296 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10297 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10298 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10300 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10301 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10304 // add other links of the faces to linkList
10305 // -----------------------------------------
10307 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10308 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10309 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10310 if ( !iter_isnew.second ) { // already in a set: no need to process
10311 linkIdSet.erase( iter_isnew.first );
10313 else // new in set == encountered for the first time: add
10315 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10316 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10317 linkList[0].push_back ( NLink( n1, n2 ));
10318 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10323 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10326 } // loop on link lists
10328 if ( aResult == SEW_OK &&
10329 ( //linkIt[0] != linkList[0].end() ||
10330 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10331 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10332 " " << (faceSetPtr[1]->empty()));
10333 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10336 // ====================================================================
10337 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10338 // ====================================================================
10340 // delete temporary faces
10341 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10342 // while ( tmpFaceIt->more() )
10343 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10344 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10345 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10346 aMesh->RemoveElement(*tmpFaceIt);
10348 if ( aResult != SEW_OK)
10351 list< int > nodeIDsToRemove;
10352 vector< const SMDS_MeshNode*> nodes;
10353 ElemFeatures elemType;
10355 // loop on nodes replacement map
10356 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10357 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10358 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10360 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10361 nodeIDsToRemove.push_back( nToRemove->GetID() );
10362 // loop on elements sharing nToRemove
10363 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10364 while ( invElemIt->more() ) {
10365 const SMDS_MeshElement* e = invElemIt->next();
10366 // get a new suite of nodes: make replacement
10367 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10368 nodes.resize( nbNodes );
10369 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10370 while ( nIt->more() ) {
10371 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10372 nnIt = nReplaceMap.find( n );
10373 if ( nnIt != nReplaceMap.end() ) {
10375 n = (*nnIt).second;
10379 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10380 // elemIDsToRemove.push_back( e->GetID() );
10384 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10385 aMesh->RemoveElement( e );
10387 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10389 AddToSameGroups( newElem, e, aMesh );
10390 if ( int aShapeId = e->getshapeId() )
10391 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10397 Remove( nodeIDsToRemove, true );
10402 //================================================================================
10404 * \brief Find corresponding nodes in two sets of faces
10405 * \param theSide1 - first face set
10406 * \param theSide2 - second first face
10407 * \param theFirstNode1 - a boundary node of set 1
10408 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10409 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10410 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10411 * \param nReplaceMap - output map of corresponding nodes
10412 * \return bool - is a success or not
10414 //================================================================================
10417 //#define DEBUG_MATCHING_NODES
10420 SMESH_MeshEditor::Sew_Error
10421 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10422 set<const SMDS_MeshElement*>& theSide2,
10423 const SMDS_MeshNode* theFirstNode1,
10424 const SMDS_MeshNode* theFirstNode2,
10425 const SMDS_MeshNode* theSecondNode1,
10426 const SMDS_MeshNode* theSecondNode2,
10427 TNodeNodeMap & nReplaceMap)
10429 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10431 nReplaceMap.clear();
10432 if ( theFirstNode1 != theFirstNode2 )
10433 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10434 if ( theSecondNode1 != theSecondNode2 )
10435 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10437 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10438 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10440 list< NLink > linkList[2];
10441 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10442 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10444 // loop on links in linkList; find faces by links and append links
10445 // of the found faces to linkList
10446 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10447 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10448 NLink link[] = { *linkIt[0], *linkIt[1] };
10449 if ( linkSet.find( link[0] ) == linkSet.end() )
10452 // by links, find faces in the face sets,
10453 // and find indices of link nodes in the found faces;
10454 // in a face set, there is only one or no face sharing a link
10455 // ---------------------------------------------------------------
10457 const SMDS_MeshElement* face[] = { 0, 0 };
10458 list<const SMDS_MeshNode*> notLinkNodes[2];
10459 //bool reverse[] = { false, false }; // order of notLinkNodes
10461 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10463 const SMDS_MeshNode* n1 = link[iSide].first;
10464 const SMDS_MeshNode* n2 = link[iSide].second;
10465 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10466 set< const SMDS_MeshElement* > facesOfNode1;
10467 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10469 // during a loop of the first node, we find all faces around n1,
10470 // during a loop of the second node, we find one face sharing both n1 and n2
10471 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10472 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10473 while ( fIt->more() ) { // loop on faces sharing a node
10474 const SMDS_MeshElement* f = fIt->next();
10475 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10476 ! facesOfNode1.insert( f ).second ) // f encounters twice
10478 if ( face[ iSide ] ) {
10479 MESSAGE( "2 faces per link " );
10480 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10483 faceSet->erase( f );
10485 // get not link nodes
10486 int nbN = f->NbNodes();
10487 if ( f->IsQuadratic() )
10489 nbNodes[ iSide ] = nbN;
10490 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10491 int i1 = f->GetNodeIndex( n1 );
10492 int i2 = f->GetNodeIndex( n2 );
10493 int iEnd = nbN, iBeg = -1, iDelta = 1;
10494 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10496 std::swap( iEnd, iBeg ); iDelta = -1;
10501 if ( i == iEnd ) i = iBeg + iDelta;
10502 if ( i == i1 ) break;
10503 nodes.push_back ( f->GetNode( i ) );
10509 // check similarity of elements of the sides
10510 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10511 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10512 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10513 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10516 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10520 // set nodes to merge
10521 // -------------------
10523 if ( face[0] && face[1] ) {
10524 if ( nbNodes[0] != nbNodes[1] ) {
10525 MESSAGE("Diff nb of face nodes");
10526 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10528 #ifdef DEBUG_MATCHING_NODES
10529 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10530 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10531 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10533 int nbN = nbNodes[0];
10535 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10536 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10537 for ( int i = 0 ; i < nbN - 2; ++i ) {
10538 #ifdef DEBUG_MATCHING_NODES
10539 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10541 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10545 // add other links of the face 1 to linkList
10546 // -----------------------------------------
10548 const SMDS_MeshElement* f0 = face[0];
10549 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10550 for ( int i = 0; i < nbN; i++ )
10552 const SMDS_MeshNode* n2 = f0->GetNode( i );
10553 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10554 linkSet.insert( SMESH_TLink( n1, n2 ));
10555 if ( !iter_isnew.second ) { // already in a set: no need to process
10556 linkSet.erase( iter_isnew.first );
10558 else // new in set == encountered for the first time: add
10560 #ifdef DEBUG_MATCHING_NODES
10561 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10562 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10564 linkList[0].push_back ( NLink( n1, n2 ));
10565 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10570 } // loop on link lists
10575 //================================================================================
10577 * \brief Create elements equal (on same nodes) to given ones
10578 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10579 * elements of the uppest dimension are duplicated.
10581 //================================================================================
10583 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10585 ClearLastCreated();
10586 SMESHDS_Mesh* mesh = GetMeshDS();
10588 // get an element type and an iterator over elements
10590 SMDSAbs_ElementType type;
10591 SMDS_ElemIteratorPtr elemIt;
10592 vector< const SMDS_MeshElement* > allElems;
10593 if ( theElements.empty() )
10595 if ( mesh->NbNodes() == 0 )
10597 // get most complex type
10598 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10599 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10600 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10602 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10603 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10608 // put all elements in the vector <allElems>
10609 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10610 elemIt = mesh->elementsIterator( type );
10611 while ( elemIt->more() )
10612 allElems.push_back( elemIt->next());
10613 elemIt = elemSetIterator( allElems );
10617 type = (*theElements.begin())->GetType();
10618 elemIt = elemSetIterator( theElements );
10621 // duplicate elements
10623 ElemFeatures elemType;
10625 vector< const SMDS_MeshNode* > nodes;
10626 while ( elemIt->more() )
10628 const SMDS_MeshElement* elem = elemIt->next();
10629 if ( elem->GetType() != type )
10632 elemType.Init( elem, /*basicOnly=*/false );
10633 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10635 AddElement( nodes, elemType );
10639 //================================================================================
10641 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10642 \param theElems - the list of elements (edges or faces) to be replicated
10643 The nodes for duplication could be found from these elements
10644 \param theNodesNot - list of nodes to NOT replicate
10645 \param theAffectedElems - the list of elements (cells and edges) to which the
10646 replicated nodes should be associated to.
10647 \return TRUE if operation has been completed successfully, FALSE otherwise
10649 //================================================================================
10651 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10652 const TIDSortedElemSet& theNodesNot,
10653 const TIDSortedElemSet& theAffectedElems )
10655 myLastCreatedElems.Clear();
10656 myLastCreatedNodes.Clear();
10658 if ( theElems.size() == 0 )
10661 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10666 TNodeNodeMap anOldNodeToNewNode;
10667 // duplicate elements and nodes
10668 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10669 // replce nodes by duplications
10670 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10674 //================================================================================
10676 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10677 \param theMeshDS - mesh instance
10678 \param theElems - the elements replicated or modified (nodes should be changed)
10679 \param theNodesNot - nodes to NOT replicate
10680 \param theNodeNodeMap - relation of old node to new created node
10681 \param theIsDoubleElem - flag os to replicate element or modify
10682 \return TRUE if operation has been completed successfully, FALSE otherwise
10684 //================================================================================
10686 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10687 const TIDSortedElemSet& theElems,
10688 const TIDSortedElemSet& theNodesNot,
10689 TNodeNodeMap& theNodeNodeMap,
10690 const bool theIsDoubleElem )
10692 MESSAGE("doubleNodes");
10693 // iterate through element and duplicate them (by nodes duplication)
10695 std::vector<const SMDS_MeshNode*> newNodes;
10696 ElemFeatures elemType;
10698 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10699 for ( ; elemItr != theElems.end(); ++elemItr )
10701 const SMDS_MeshElement* anElem = *elemItr;
10705 // duplicate nodes to duplicate element
10706 bool isDuplicate = false;
10707 newNodes.resize( anElem->NbNodes() );
10708 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10710 while ( anIter->more() )
10712 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10713 const SMDS_MeshNode* aNewNode = aCurrNode;
10714 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10715 if ( n2n != theNodeNodeMap.end() )
10717 aNewNode = n2n->second;
10719 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10722 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10723 copyPosition( aCurrNode, aNewNode );
10724 theNodeNodeMap[ aCurrNode ] = aNewNode;
10725 myLastCreatedNodes.Append( aNewNode );
10727 isDuplicate |= (aCurrNode != aNewNode);
10728 newNodes[ ind++ ] = aNewNode;
10730 if ( !isDuplicate )
10733 if ( theIsDoubleElem )
10734 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10736 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10743 //================================================================================
10745 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10746 \param theNodes - identifiers of nodes to be doubled
10747 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10748 nodes. If list of element identifiers is empty then nodes are doubled but
10749 they not assigned to elements
10750 \return TRUE if operation has been completed successfully, FALSE otherwise
10752 //================================================================================
10754 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10755 const std::list< int >& theListOfModifiedElems )
10757 MESSAGE("DoubleNodes");
10758 myLastCreatedElems.Clear();
10759 myLastCreatedNodes.Clear();
10761 if ( theListOfNodes.size() == 0 )
10764 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10768 // iterate through nodes and duplicate them
10770 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10772 std::list< int >::const_iterator aNodeIter;
10773 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10775 int aCurr = *aNodeIter;
10776 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10782 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10785 copyPosition( aNode, aNewNode );
10786 anOldNodeToNewNode[ aNode ] = aNewNode;
10787 myLastCreatedNodes.Append( aNewNode );
10791 // Create map of new nodes for modified elements
10793 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10795 std::list< int >::const_iterator anElemIter;
10796 for ( anElemIter = theListOfModifiedElems.begin();
10797 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10799 int aCurr = *anElemIter;
10800 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10804 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10806 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10808 while ( anIter->more() )
10810 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10811 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10813 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10814 aNodeArr[ ind++ ] = aNewNode;
10817 aNodeArr[ ind++ ] = aCurrNode;
10819 anElemToNodes[ anElem ] = aNodeArr;
10822 // Change nodes of elements
10824 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10825 anElemToNodesIter = anElemToNodes.begin();
10826 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10828 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10829 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10832 MESSAGE("ChangeElementNodes");
10833 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10842 //================================================================================
10844 \brief Check if element located inside shape
10845 \return TRUE if IN or ON shape, FALSE otherwise
10847 //================================================================================
10849 template<class Classifier>
10850 bool isInside(const SMDS_MeshElement* theElem,
10851 Classifier& theClassifier,
10852 const double theTol)
10854 gp_XYZ centerXYZ (0, 0, 0);
10855 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10856 while (aNodeItr->more())
10857 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10859 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10860 theClassifier.Perform(aPnt, theTol);
10861 TopAbs_State aState = theClassifier.State();
10862 return (aState == TopAbs_IN || aState == TopAbs_ON );
10865 //================================================================================
10867 * \brief Classifier of the 3D point on the TopoDS_Face
10868 * with interaface suitable for isInside()
10870 //================================================================================
10872 struct _FaceClassifier
10874 Extrema_ExtPS _extremum;
10875 BRepAdaptor_Surface _surface;
10876 TopAbs_State _state;
10878 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10880 _extremum.Initialize( _surface,
10881 _surface.FirstUParameter(), _surface.LastUParameter(),
10882 _surface.FirstVParameter(), _surface.LastVParameter(),
10883 _surface.Tolerance(), _surface.Tolerance() );
10885 void Perform(const gp_Pnt& aPnt, double theTol)
10888 _state = TopAbs_OUT;
10889 _extremum.Perform(aPnt);
10890 if ( _extremum.IsDone() )
10891 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10892 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10894 TopAbs_State State() const
10901 //================================================================================
10903 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10904 This method is the first step of DoubleNodeElemGroupsInRegion.
10905 \param theElems - list of groups of elements (edges or faces) to be replicated
10906 \param theNodesNot - list of groups of nodes not to replicated
10907 \param theShape - shape to detect affected elements (element which geometric center
10908 located on or inside shape). If the shape is null, detection is done on faces orientations
10909 (select elements with a gravity center on the side given by faces normals).
10910 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10911 The replicated nodes should be associated to affected elements.
10912 \return groups of affected elements
10913 \sa DoubleNodeElemGroupsInRegion()
10915 //================================================================================
10917 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10918 const TIDSortedElemSet& theNodesNot,
10919 const TopoDS_Shape& theShape,
10920 TIDSortedElemSet& theAffectedElems)
10922 if ( theShape.IsNull() )
10924 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10925 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10926 std::set<const SMDS_MeshElement*> edgesToCheck;
10927 alreadyCheckedNodes.clear();
10928 alreadyCheckedElems.clear();
10929 edgesToCheck.clear();
10931 // --- iterates on elements to be replicated and get elements by back references from their nodes
10933 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10935 for ( ielem=1; elemItr != theElems.end(); ++elemItr )
10937 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10938 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10941 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10942 MESSAGE("element " << ielem++ << " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10943 std::set<const SMDS_MeshNode*> nodesElem;
10945 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10946 while ( nodeItr->more() )
10948 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10949 nodesElem.insert(aNode);
10951 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10952 for (; nodit != nodesElem.end(); nodit++)
10954 MESSAGE(" noeud ");
10955 const SMDS_MeshNode* aNode = *nodit;
10956 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10958 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10960 alreadyCheckedNodes.insert(aNode);
10961 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10962 while ( backElemItr->more() )
10964 MESSAGE(" backelem ");
10965 const SMDS_MeshElement* curElem = backElemItr->next();
10966 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10968 if (theElems.find(curElem) != theElems.end())
10970 alreadyCheckedElems.insert(curElem);
10971 double x=0, y=0, z=0;
10973 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10974 while ( nodeItr2->more() )
10976 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10977 x += anotherNode->X();
10978 y += anotherNode->Y();
10979 z += anotherNode->Z();
10983 p.SetCoord( x/nb -aNode->X(),
10985 z/nb -aNode->Z() );
10986 MESSAGE(" check " << p.X() << " " << p.Y() << " " << p.Z());
10989 MESSAGE(" --- inserted")
10990 theAffectedElems.insert( curElem );
10992 else if (curElem->GetType() == SMDSAbs_Edge)
10993 edgesToCheck.insert(curElem);
10997 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10998 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10999 for( ; eit != edgesToCheck.end(); eit++)
11001 bool onside = true;
11002 const SMDS_MeshElement* anEdge = *eit;
11003 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
11004 while ( nodeItr->more() )
11006 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11007 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
11015 MESSAGE(" --- edge onside inserted")
11016 theAffectedElems.insert(anEdge);
11022 const double aTol = Precision::Confusion();
11023 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11024 auto_ptr<_FaceClassifier> aFaceClassifier;
11025 if ( theShape.ShapeType() == TopAbs_SOLID )
11027 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11028 bsc3d->PerformInfinitePoint(aTol);
11030 else if (theShape.ShapeType() == TopAbs_FACE )
11032 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11035 // iterates on indicated elements and get elements by back references from their nodes
11036 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11038 for ( ielem = 1; elemItr != theElems.end(); ++elemItr )
11040 MESSAGE("element " << ielem++);
11041 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11044 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11045 while ( nodeItr->more() )
11047 MESSAGE(" noeud ");
11048 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11049 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11051 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11052 while ( backElemItr->more() )
11054 MESSAGE(" backelem ");
11055 const SMDS_MeshElement* curElem = backElemItr->next();
11056 if ( curElem && theElems.find(curElem) == theElems.end() &&
11058 isInside( curElem, *bsc3d, aTol ) :
11059 isInside( curElem, *aFaceClassifier, aTol )))
11060 theAffectedElems.insert( curElem );
11068 //================================================================================
11070 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11071 \param theElems - group of of elements (edges or faces) to be replicated
11072 \param theNodesNot - group of nodes not to replicate
11073 \param theShape - shape to detect affected elements (element which geometric center
11074 located on or inside shape).
11075 The replicated nodes should be associated to affected elements.
11076 \return TRUE if operation has been completed successfully, FALSE otherwise
11078 //================================================================================
11080 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11081 const TIDSortedElemSet& theNodesNot,
11082 const TopoDS_Shape& theShape )
11084 if ( theShape.IsNull() )
11087 const double aTol = Precision::Confusion();
11088 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11089 auto_ptr<_FaceClassifier> aFaceClassifier;
11090 if ( theShape.ShapeType() == TopAbs_SOLID )
11092 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11093 bsc3d->PerformInfinitePoint(aTol);
11095 else if (theShape.ShapeType() == TopAbs_FACE )
11097 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11100 // iterates on indicated elements and get elements by back references from their nodes
11101 TIDSortedElemSet anAffected;
11102 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11103 for ( ; elemItr != theElems.end(); ++elemItr )
11105 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11109 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11110 while ( nodeItr->more() )
11112 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11113 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11115 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11116 while ( backElemItr->more() )
11118 const SMDS_MeshElement* curElem = backElemItr->next();
11119 if ( curElem && theElems.find(curElem) == theElems.end() &&
11121 isInside( curElem, *bsc3d, aTol ) :
11122 isInside( curElem, *aFaceClassifier, aTol )))
11123 anAffected.insert( curElem );
11127 return DoubleNodes( theElems, theNodesNot, anAffected );
11131 * \brief compute an oriented angle between two planes defined by four points.
11132 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11133 * @param p0 base of the rotation axe
11134 * @param p1 extremity of the rotation axe
11135 * @param g1 belongs to the first plane
11136 * @param g2 belongs to the second plane
11138 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11140 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11141 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11142 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11143 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11144 gp_Vec vref(p0, p1);
11147 gp_Vec n1 = vref.Crossed(v1);
11148 gp_Vec n2 = vref.Crossed(v2);
11150 return n2.AngleWithRef(n1, vref);
11152 catch ( Standard_Failure ) {
11154 return Max( v1.Magnitude(), v2.Magnitude() );
11158 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11159 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11160 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11161 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11162 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11163 * 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.
11164 * 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.
11165 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11166 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11167 * \param theElems - list of groups of volumes, where a group of volume is a set of
11168 * SMDS_MeshElements sorted by Id.
11169 * \param createJointElems - if TRUE, create the elements
11170 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11171 * the boundary between \a theDomains and the rest mesh
11172 * \return TRUE if operation has been completed successfully, FALSE otherwise
11174 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11175 bool createJointElems,
11176 bool onAllBoundaries)
11178 MESSAGE("----------------------------------------------");
11179 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11180 MESSAGE("----------------------------------------------");
11182 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11183 meshDS->BuildDownWardConnectivity(true);
11185 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11187 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11188 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11189 // build the list of nodes shared by 2 or more domains, with their domain indexes
11191 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11192 std::map<int,int>celldom; // cell vtkId --> domain
11193 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11194 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11195 faceDomains.clear();
11197 cellDomains.clear();
11198 nodeDomains.clear();
11199 std::map<int,int> emptyMap;
11200 std::set<int> emptySet;
11203 MESSAGE(".. Number of domains :"<<theElems.size());
11205 TIDSortedElemSet theRestDomElems;
11206 const int iRestDom = -1;
11207 const int idom0 = onAllBoundaries ? iRestDom : 0;
11208 const int nbDomains = theElems.size();
11210 // Check if the domains do not share an element
11211 for (int idom = 0; idom < nbDomains-1; idom++)
11213 // MESSAGE("... Check of domain #" << idom);
11214 const TIDSortedElemSet& domain = theElems[idom];
11215 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11216 for (; elemItr != domain.end(); ++elemItr)
11218 const SMDS_MeshElement* anElem = *elemItr;
11219 int idombisdeb = idom + 1 ;
11220 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
11222 const TIDSortedElemSet& domainbis = theElems[idombis];
11223 if ( domainbis.count(anElem) )
11225 MESSAGE(".... Domain #" << idom);
11226 MESSAGE(".... Domain #" << idombis);
11227 throw SALOME_Exception("The domains are not disjoint.");
11234 for (int idom = 0; idom < nbDomains; idom++)
11237 // --- build a map (face to duplicate --> volume to modify)
11238 // with all the faces shared by 2 domains (group of elements)
11239 // and corresponding volume of this domain, for each shared face.
11240 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11242 MESSAGE("... Neighbors of domain #" << idom);
11243 const TIDSortedElemSet& domain = theElems[idom];
11244 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11245 for (; elemItr != domain.end(); ++elemItr)
11247 const SMDS_MeshElement* anElem = *elemItr;
11250 int vtkId = anElem->getVtkId();
11251 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11252 int neighborsVtkIds[NBMAXNEIGHBORS];
11253 int downIds[NBMAXNEIGHBORS];
11254 unsigned char downTypes[NBMAXNEIGHBORS];
11255 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11256 for (int n = 0; n < nbNeighbors; n++)
11258 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11259 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11260 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11263 for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11265 // MESSAGE("Domain " << idombis);
11266 const TIDSortedElemSet& domainbis = theElems[idombis];
11267 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11269 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11271 DownIdType face(downIds[n], downTypes[n]);
11272 if (!faceDomains[face].count(idom))
11274 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11275 celldom[vtkId] = idom;
11276 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11280 theRestDomElems.insert( elem );
11281 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11282 celldom[neighborsVtkIds[n]] = iRestDom;
11290 //MESSAGE("Number of shared faces " << faceDomains.size());
11291 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11293 // --- explore the shared faces domain by domain,
11294 // explore the nodes of the face and see if they belong to a cell in the domain,
11295 // which has only a node or an edge on the border (not a shared face)
11297 for (int idomain = idom0; idomain < nbDomains; idomain++)
11299 //MESSAGE("Domain " << idomain);
11300 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11301 itface = faceDomains.begin();
11302 for (; itface != faceDomains.end(); ++itface)
11304 const std::map<int, int>& domvol = itface->second;
11305 if (!domvol.count(idomain))
11307 DownIdType face = itface->first;
11308 //MESSAGE(" --- face " << face.cellId);
11309 std::set<int> oldNodes;
11311 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11312 std::set<int>::iterator itn = oldNodes.begin();
11313 for (; itn != oldNodes.end(); ++itn)
11316 //MESSAGE(" node " << oldId);
11317 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11318 for (int i=0; i<l.ncells; i++)
11320 int vtkId = l.cells[i];
11321 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11322 if (!domain.count(anElem))
11324 int vtkType = grid->GetCellType(vtkId);
11325 int downId = grid->CellIdToDownId(vtkId);
11328 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11329 continue; // not OK at this stage of the algorithm:
11330 //no cells created after BuildDownWardConnectivity
11332 DownIdType aCell(downId, vtkType);
11333 cellDomains[aCell][idomain] = vtkId;
11334 celldom[vtkId] = idomain;
11335 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11341 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11342 // for each shared face, get the nodes
11343 // for each node, for each domain of the face, create a clone of the node
11345 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11346 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11347 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11349 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11350 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11351 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11353 MESSAGE(".. Duplication of the nodes");
11354 for (int idomain = idom0; idomain < nbDomains; idomain++)
11356 itface = faceDomains.begin();
11357 for (; itface != faceDomains.end(); ++itface)
11359 const std::map<int, int>& domvol = itface->second;
11360 if (!domvol.count(idomain))
11362 DownIdType face = itface->first;
11363 //MESSAGE(" --- face " << face.cellId);
11364 std::set<int> oldNodes;
11366 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11367 std::set<int>::iterator itn = oldNodes.begin();
11368 for (; itn != oldNodes.end(); ++itn)
11371 if (nodeDomains[oldId].empty())
11373 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11374 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11376 std::map<int, int>::const_iterator itdom = domvol.begin();
11377 for (; itdom != domvol.end(); ++itdom)
11379 int idom = itdom->first;
11380 //MESSAGE(" domain " << idom);
11381 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11383 if (nodeDomains[oldId].size() >= 2) // a multiple node
11385 vector<int> orderedDoms;
11386 //MESSAGE("multiple node " << oldId);
11387 if (mutipleNodes.count(oldId))
11388 orderedDoms = mutipleNodes[oldId];
11391 map<int,int>::iterator it = nodeDomains[oldId].begin();
11392 for (; it != nodeDomains[oldId].end(); ++it)
11393 orderedDoms.push_back(it->first);
11395 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11396 //stringstream txt;
11397 //for (int i=0; i<orderedDoms.size(); i++)
11398 // txt << orderedDoms[i] << " ";
11399 //MESSAGE("orderedDoms " << txt.str());
11400 mutipleNodes[oldId] = orderedDoms;
11402 double *coords = grid->GetPoint(oldId);
11403 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11404 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11405 int newId = newNode->getVtkId();
11406 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11407 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11414 MESSAGE(".. Creation of elements");
11415 for (int idomain = idom0; idomain < nbDomains; idomain++)
11417 itface = faceDomains.begin();
11418 for (; itface != faceDomains.end(); ++itface)
11420 std::map<int, int> domvol = itface->second;
11421 if (!domvol.count(idomain))
11423 DownIdType face = itface->first;
11424 //MESSAGE(" --- face " << face.cellId);
11425 std::set<int> oldNodes;
11427 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11428 int nbMultipleNodes = 0;
11429 std::set<int>::iterator itn = oldNodes.begin();
11430 for (; itn != oldNodes.end(); ++itn)
11433 if (mutipleNodes.count(oldId))
11436 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11438 //MESSAGE("multiple Nodes detected on a shared face");
11439 int downId = itface->first.cellId;
11440 unsigned char cellType = itface->first.cellType;
11441 // --- shared edge or shared face ?
11442 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11445 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11446 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11447 if (mutipleNodes.count(nodes[i]))
11448 if (!mutipleNodesToFace.count(nodes[i]))
11449 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11451 else // shared face (between two volumes)
11453 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11454 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11455 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11456 for (int ie =0; ie < nbEdges; ie++)
11459 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11460 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11462 vector<int> vn0 = mutipleNodes[nodes[0]];
11463 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11465 for (int i0 = 0; i0 < vn0.size(); i0++)
11466 for (int i1 = 0; i1 < vn1.size(); i1++)
11467 if (vn0[i0] == vn1[i1])
11468 doms.push_back(vn0[i0]);
11469 if (doms.size() >2)
11471 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11472 double *coords = grid->GetPoint(nodes[0]);
11473 gp_Pnt p0(coords[0], coords[1], coords[2]);
11474 coords = grid->GetPoint(nodes[nbNodes - 1]);
11475 gp_Pnt p1(coords[0], coords[1], coords[2]);
11477 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11478 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11479 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11480 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11481 for (int id=0; id < doms.size(); id++)
11483 int idom = doms[id];
11484 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11485 for (int ivol=0; ivol<nbvol; ivol++)
11487 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11488 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11489 if (domain.count(elem))
11491 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11492 domvol[idom] = svol;
11493 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11495 vtkIdType npts = 0;
11496 vtkIdType* pts = 0;
11497 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11498 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11501 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11502 angleDom[idom] = 0;
11506 gp_Pnt g(values[0], values[1], values[2]);
11507 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11508 //MESSAGE(" angle=" << angleDom[idom]);
11514 map<double, int> sortedDom; // sort domains by angle
11515 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11516 sortedDom[ia->second] = ia->first;
11517 vector<int> vnodes;
11519 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11521 vdom.push_back(ib->second);
11522 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11524 for (int ino = 0; ino < nbNodes; ino++)
11525 vnodes.push_back(nodes[ino]);
11526 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11535 // --- iterate on shared faces (volumes to modify, face to extrude)
11536 // get node id's of the face (id SMDS = id VTK)
11537 // create flat element with old and new nodes if requested
11539 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11540 // (domain1 X domain2) = domain1 + MAXINT*domain2
11542 std::map<int, std::map<long,int> > nodeQuadDomains;
11543 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11545 MESSAGE(".. Creation of elements: simple junction");
11546 if (createJointElems)
11549 string joints2DName = "joints2D";
11550 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11551 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11552 string joints3DName = "joints3D";
11553 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11554 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11556 itface = faceDomains.begin();
11557 for (; itface != faceDomains.end(); ++itface)
11559 DownIdType face = itface->first;
11560 std::set<int> oldNodes;
11561 std::set<int>::iterator itn;
11563 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11565 std::map<int, int> domvol = itface->second;
11566 std::map<int, int>::iterator itdom = domvol.begin();
11567 int dom1 = itdom->first;
11568 int vtkVolId = itdom->second;
11570 int dom2 = itdom->first;
11571 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11573 stringstream grpname;
11576 grpname << dom1 << "_" << dom2;
11578 grpname << dom2 << "_" << dom1;
11579 string namegrp = grpname.str();
11580 if (!mapOfJunctionGroups.count(namegrp))
11581 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11582 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11584 sgrp->Add(vol->GetID());
11585 if (vol->GetType() == SMDSAbs_Volume)
11586 joints3DGrp->Add(vol->GetID());
11587 else if (vol->GetType() == SMDSAbs_Face)
11588 joints2DGrp->Add(vol->GetID());
11592 // --- create volumes on multiple domain intersection if requested
11593 // iterate on mutipleNodesToFace
11594 // iterate on edgesMultiDomains
11596 MESSAGE(".. Creation of elements: multiple junction");
11597 if (createJointElems)
11599 // --- iterate on mutipleNodesToFace
11601 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11602 for (; itn != mutipleNodesToFace.end(); ++itn)
11604 int node = itn->first;
11605 vector<int> orderDom = itn->second;
11606 vector<vtkIdType> orderedNodes;
11607 for (int idom = 0; idom <orderDom.size(); idom++)
11608 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11609 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11611 stringstream grpname;
11613 grpname << 0 << "_" << 0;
11615 string namegrp = grpname.str();
11616 if (!mapOfJunctionGroups.count(namegrp))
11617 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11618 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11620 sgrp->Add(face->GetID());
11623 // --- iterate on edgesMultiDomains
11625 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11626 for (; ite != edgesMultiDomains.end(); ++ite)
11628 vector<int> nodes = ite->first;
11629 vector<int> orderDom = ite->second;
11630 vector<vtkIdType> orderedNodes;
11631 if (nodes.size() == 2)
11633 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11634 for (int ino=0; ino < nodes.size(); ino++)
11635 if (orderDom.size() == 3)
11636 for (int idom = 0; idom <orderDom.size(); idom++)
11637 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11639 for (int idom = orderDom.size()-1; idom >=0; idom--)
11640 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11641 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11644 string namegrp = "jointsMultiples";
11645 if (!mapOfJunctionGroups.count(namegrp))
11646 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11647 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11649 sgrp->Add(vol->GetID());
11653 INFOS("Quadratic multiple joints not implemented");
11654 // TODO quadratic nodes
11659 // --- list the explicit faces and edges of the mesh that need to be modified,
11660 // i.e. faces and edges built with one or more duplicated nodes.
11661 // associate these faces or edges to their corresponding domain.
11662 // only the first domain found is kept when a face or edge is shared
11664 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11665 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11666 faceOrEdgeDom.clear();
11669 MESSAGE(".. Modification of elements");
11670 for (int idomain = idom0; idomain < nbDomains; idomain++)
11672 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11673 for (; itnod != nodeDomains.end(); ++itnod)
11675 int oldId = itnod->first;
11676 //MESSAGE(" node " << oldId);
11677 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11678 for (int i = 0; i < l.ncells; i++)
11680 int vtkId = l.cells[i];
11681 int vtkType = grid->GetCellType(vtkId);
11682 int downId = grid->CellIdToDownId(vtkId);
11684 continue; // new cells: not to be modified
11685 DownIdType aCell(downId, vtkType);
11686 int volParents[1000];
11687 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11688 for (int j = 0; j < nbvol; j++)
11689 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11690 if (!feDom.count(vtkId))
11692 feDom[vtkId] = idomain;
11693 faceOrEdgeDom[aCell] = emptyMap;
11694 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11695 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11696 // << " type " << vtkType << " downId " << downId);
11702 // --- iterate on shared faces (volumes to modify, face to extrude)
11703 // get node id's of the face
11704 // replace old nodes by new nodes in volumes, and update inverse connectivity
11706 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11707 for (int m=0; m<3; m++)
11709 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11710 itface = (*amap).begin();
11711 for (; itface != (*amap).end(); ++itface)
11713 DownIdType face = itface->first;
11714 std::set<int> oldNodes;
11715 std::set<int>::iterator itn;
11717 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11718 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11719 std::map<int, int> localClonedNodeIds;
11721 std::map<int, int> domvol = itface->second;
11722 std::map<int, int>::iterator itdom = domvol.begin();
11723 for (; itdom != domvol.end(); ++itdom)
11725 int idom = itdom->first;
11726 int vtkVolId = itdom->second;
11727 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11728 localClonedNodeIds.clear();
11729 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11732 if (nodeDomains[oldId].count(idom))
11734 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11735 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11738 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11743 // Remove empty groups (issue 0022812)
11744 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11745 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11747 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11748 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11751 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11752 grid->BuildLinks();
11760 * \brief Double nodes on some external faces and create flat elements.
11761 * Flat elements are mainly used by some types of mechanic calculations.
11763 * Each group of the list must be constituted of faces.
11764 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11765 * @param theElems - list of groups of faces, where a group of faces is a set of
11766 * SMDS_MeshElements sorted by Id.
11767 * @return TRUE if operation has been completed successfully, FALSE otherwise
11769 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11771 MESSAGE("-------------------------------------------------");
11772 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11773 MESSAGE("-------------------------------------------------");
11775 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11777 // --- For each group of faces
11778 // duplicate the nodes, create a flat element based on the face
11779 // replace the nodes of the faces by their clones
11781 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11782 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11783 clonedNodes.clear();
11784 intermediateNodes.clear();
11785 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11786 mapOfJunctionGroups.clear();
11788 for (int idom = 0; idom < theElems.size(); idom++)
11790 const TIDSortedElemSet& domain = theElems[idom];
11791 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11792 for (; elemItr != domain.end(); ++elemItr)
11794 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11795 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11798 // MESSAGE("aFace=" << aFace->GetID());
11799 bool isQuad = aFace->IsQuadratic();
11800 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11802 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11804 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11805 while (nodeIt->more())
11807 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11808 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11810 ln2.push_back(node);
11812 ln0.push_back(node);
11814 const SMDS_MeshNode* clone = 0;
11815 if (!clonedNodes.count(node))
11817 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11818 copyPosition( node, clone );
11819 clonedNodes[node] = clone;
11822 clone = clonedNodes[node];
11825 ln3.push_back(clone);
11827 ln1.push_back(clone);
11829 const SMDS_MeshNode* inter = 0;
11830 if (isQuad && (!isMedium))
11832 if (!intermediateNodes.count(node))
11834 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11835 copyPosition( node, inter );
11836 intermediateNodes[node] = inter;
11839 inter = intermediateNodes[node];
11840 ln4.push_back(inter);
11844 // --- extrude the face
11846 vector<const SMDS_MeshNode*> ln;
11847 SMDS_MeshVolume* vol = 0;
11848 vtkIdType aType = aFace->GetVtkType();
11852 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11853 // MESSAGE("vol prism " << vol->GetID());
11854 ln.push_back(ln1[0]);
11855 ln.push_back(ln1[1]);
11856 ln.push_back(ln1[2]);
11859 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11860 // MESSAGE("vol hexa " << vol->GetID());
11861 ln.push_back(ln1[0]);
11862 ln.push_back(ln1[1]);
11863 ln.push_back(ln1[2]);
11864 ln.push_back(ln1[3]);
11866 case VTK_QUADRATIC_TRIANGLE:
11867 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11868 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11869 // MESSAGE("vol quad prism " << vol->GetID());
11870 ln.push_back(ln1[0]);
11871 ln.push_back(ln1[1]);
11872 ln.push_back(ln1[2]);
11873 ln.push_back(ln3[0]);
11874 ln.push_back(ln3[1]);
11875 ln.push_back(ln3[2]);
11877 case VTK_QUADRATIC_QUAD:
11878 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11879 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11880 // ln4[0], ln4[1], ln4[2], ln4[3]);
11881 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11882 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11883 ln4[0], ln4[1], ln4[2], ln4[3]);
11884 // MESSAGE("vol quad hexa " << vol->GetID());
11885 ln.push_back(ln1[0]);
11886 ln.push_back(ln1[1]);
11887 ln.push_back(ln1[2]);
11888 ln.push_back(ln1[3]);
11889 ln.push_back(ln3[0]);
11890 ln.push_back(ln3[1]);
11891 ln.push_back(ln3[2]);
11892 ln.push_back(ln3[3]);
11902 stringstream grpname;
11906 string namegrp = grpname.str();
11907 if (!mapOfJunctionGroups.count(namegrp))
11908 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11909 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11911 sgrp->Add(vol->GetID());
11914 // --- modify the face
11916 aFace->ChangeNodes(&ln[0], ln.size());
11923 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11924 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11925 * groups of faces to remove inside the object, (idem edges).
11926 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11928 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11929 const TopoDS_Shape& theShape,
11930 SMESH_NodeSearcher* theNodeSearcher,
11931 const char* groupName,
11932 std::vector<double>& nodesCoords,
11933 std::vector<std::vector<int> >& listOfListOfNodes)
11935 MESSAGE("--------------------------------");
11936 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11937 MESSAGE("--------------------------------");
11939 // --- zone of volumes to remove is given :
11940 // 1 either by a geom shape (one or more vertices) and a radius,
11941 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11942 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11943 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11944 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11945 // defined by it's name.
11947 SMESHDS_GroupBase* groupDS = 0;
11948 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11949 while ( groupIt->more() )
11952 SMESH_Group * group = groupIt->next();
11953 if ( !group ) continue;
11954 groupDS = group->GetGroupDS();
11955 if ( !groupDS || groupDS->IsEmpty() ) continue;
11956 std::string grpName = group->GetName();
11957 //MESSAGE("grpName=" << grpName);
11958 if (grpName == groupName)
11964 bool isNodeGroup = false;
11965 bool isNodeCoords = false;
11968 if (groupDS->GetType() != SMDSAbs_Node)
11970 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11973 if (nodesCoords.size() > 0)
11974 isNodeCoords = true; // a list o nodes given by their coordinates
11975 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11977 // --- define groups to build
11979 int idg; // --- group of SMDS volumes
11980 string grpvName = groupName;
11981 grpvName += "_vol";
11982 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11985 MESSAGE("group not created " << grpvName);
11988 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11990 int idgs; // --- group of SMDS faces on the skin
11991 string grpsName = groupName;
11992 grpsName += "_skin";
11993 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11996 MESSAGE("group not created " << grpsName);
11999 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12001 int idgi; // --- group of SMDS faces internal (several shapes)
12002 string grpiName = groupName;
12003 grpiName += "_internalFaces";
12004 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12007 MESSAGE("group not created " << grpiName);
12010 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12012 int idgei; // --- group of SMDS faces internal (several shapes)
12013 string grpeiName = groupName;
12014 grpeiName += "_internalEdges";
12015 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12018 MESSAGE("group not created " << grpeiName);
12021 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12023 // --- build downward connectivity
12025 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12026 meshDS->BuildDownWardConnectivity(true);
12027 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12029 // --- set of volumes detected inside
12031 std::set<int> setOfInsideVol;
12032 std::set<int> setOfVolToCheck;
12034 std::vector<gp_Pnt> gpnts;
12037 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12039 MESSAGE("group of nodes provided");
12040 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12041 while ( elemIt->more() )
12043 const SMDS_MeshElement* elem = elemIt->next();
12046 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12049 SMDS_MeshElement* vol = 0;
12050 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12051 while (volItr->more())
12053 vol = (SMDS_MeshElement*)volItr->next();
12054 setOfInsideVol.insert(vol->getVtkId());
12055 sgrp->Add(vol->GetID());
12059 else if (isNodeCoords)
12061 MESSAGE("list of nodes coordinates provided");
12064 while (i < nodesCoords.size()-2)
12066 double x = nodesCoords[i++];
12067 double y = nodesCoords[i++];
12068 double z = nodesCoords[i++];
12069 gp_Pnt p = gp_Pnt(x, y ,z);
12070 gpnts.push_back(p);
12071 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12075 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12077 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12078 TopTools_IndexedMapOfShape vertexMap;
12079 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12080 gp_Pnt p = gp_Pnt(0,0,0);
12081 if (vertexMap.Extent() < 1)
12084 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12086 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12087 p = BRep_Tool::Pnt(vertex);
12088 gpnts.push_back(p);
12089 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12093 if (gpnts.size() > 0)
12096 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12098 nodeId = startNode->GetID();
12099 MESSAGE("nodeId " << nodeId);
12101 double radius2 = radius*radius;
12102 MESSAGE("radius2 " << radius2);
12104 // --- volumes on start node
12106 setOfVolToCheck.clear();
12107 SMDS_MeshElement* startVol = 0;
12108 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12109 while (volItr->more())
12111 startVol = (SMDS_MeshElement*)volItr->next();
12112 setOfVolToCheck.insert(startVol->getVtkId());
12114 if (setOfVolToCheck.empty())
12116 MESSAGE("No volumes found");
12120 // --- starting with central volumes then their neighbors, check if they are inside
12121 // or outside the domain, until no more new neighbor volume is inside.
12122 // Fill the group of inside volumes
12124 std::map<int, double> mapOfNodeDistance2;
12125 mapOfNodeDistance2.clear();
12126 std::set<int> setOfOutsideVol;
12127 while (!setOfVolToCheck.empty())
12129 std::set<int>::iterator it = setOfVolToCheck.begin();
12131 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12132 bool volInside = false;
12133 vtkIdType npts = 0;
12134 vtkIdType* pts = 0;
12135 grid->GetCellPoints(vtkId, npts, pts);
12136 for (int i=0; i<npts; i++)
12138 double distance2 = 0;
12139 if (mapOfNodeDistance2.count(pts[i]))
12141 distance2 = mapOfNodeDistance2[pts[i]];
12142 MESSAGE("point " << pts[i] << " distance2 " << distance2);
12146 double *coords = grid->GetPoint(pts[i]);
12147 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12149 for (int j=0; j<gpnts.size(); j++)
12151 double d2 = aPoint.SquareDistance(gpnts[j]);
12152 if (d2 < distance2)
12155 if (distance2 < radius2)
12159 mapOfNodeDistance2[pts[i]] = distance2;
12160 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12162 if (distance2 < radius2)
12164 volInside = true; // one or more nodes inside the domain
12165 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12171 setOfInsideVol.insert(vtkId);
12172 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12173 int neighborsVtkIds[NBMAXNEIGHBORS];
12174 int downIds[NBMAXNEIGHBORS];
12175 unsigned char downTypes[NBMAXNEIGHBORS];
12176 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12177 for (int n = 0; n < nbNeighbors; n++)
12178 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12179 setOfVolToCheck.insert(neighborsVtkIds[n]);
12183 setOfOutsideVol.insert(vtkId);
12184 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12186 setOfVolToCheck.erase(vtkId);
12190 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12191 // If yes, add the volume to the inside set
12193 bool addedInside = true;
12194 std::set<int> setOfVolToReCheck;
12195 while (addedInside)
12197 MESSAGE(" --------------------------- re check");
12198 addedInside = false;
12199 std::set<int>::iterator itv = setOfInsideVol.begin();
12200 for (; itv != setOfInsideVol.end(); ++itv)
12203 int neighborsVtkIds[NBMAXNEIGHBORS];
12204 int downIds[NBMAXNEIGHBORS];
12205 unsigned char downTypes[NBMAXNEIGHBORS];
12206 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12207 for (int n = 0; n < nbNeighbors; n++)
12208 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12209 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12211 setOfVolToCheck = setOfVolToReCheck;
12212 setOfVolToReCheck.clear();
12213 while (!setOfVolToCheck.empty())
12215 std::set<int>::iterator it = setOfVolToCheck.begin();
12217 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12219 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12220 int countInside = 0;
12221 int neighborsVtkIds[NBMAXNEIGHBORS];
12222 int downIds[NBMAXNEIGHBORS];
12223 unsigned char downTypes[NBMAXNEIGHBORS];
12224 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12225 for (int n = 0; n < nbNeighbors; n++)
12226 if (setOfInsideVol.count(neighborsVtkIds[n]))
12228 MESSAGE("countInside " << countInside);
12229 if (countInside > 1)
12231 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12232 setOfInsideVol.insert(vtkId);
12233 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12234 addedInside = true;
12237 setOfVolToReCheck.insert(vtkId);
12239 setOfVolToCheck.erase(vtkId);
12243 // --- map of Downward faces at the boundary, inside the global volume
12244 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12245 // fill group of SMDS faces inside the volume (when several volume shapes)
12246 // fill group of SMDS faces on the skin of the global volume (if skin)
12248 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12249 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12250 std::set<int>::iterator it = setOfInsideVol.begin();
12251 for (; it != setOfInsideVol.end(); ++it)
12254 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12255 int neighborsVtkIds[NBMAXNEIGHBORS];
12256 int downIds[NBMAXNEIGHBORS];
12257 unsigned char downTypes[NBMAXNEIGHBORS];
12258 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12259 for (int n = 0; n < nbNeighbors; n++)
12261 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12262 if (neighborDim == 3)
12264 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12266 DownIdType face(downIds[n], downTypes[n]);
12267 boundaryFaces[face] = vtkId;
12269 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12270 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12271 if (vtkFaceId >= 0)
12273 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12274 // find also the smds edges on this face
12275 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12276 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12277 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12278 for (int i = 0; i < nbEdges; i++)
12280 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12281 if (vtkEdgeId >= 0)
12282 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12286 else if (neighborDim == 2) // skin of the volume
12288 DownIdType face(downIds[n], downTypes[n]);
12289 skinFaces[face] = vtkId;
12290 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12291 if (vtkFaceId >= 0)
12292 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12297 // --- identify the edges constituting the wire of each subshape on the skin
12298 // define polylines with the nodes of edges, equivalent to wires
12299 // project polylines on subshapes, and partition, to get geom faces
12301 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12302 std::set<int> emptySet;
12304 std::set<int> shapeIds;
12306 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12307 while (itelem->more())
12309 const SMDS_MeshElement *elem = itelem->next();
12310 int shapeId = elem->getshapeId();
12311 int vtkId = elem->getVtkId();
12312 if (!shapeIdToVtkIdSet.count(shapeId))
12314 shapeIdToVtkIdSet[shapeId] = emptySet;
12315 shapeIds.insert(shapeId);
12317 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12320 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12321 std::set<DownIdType, DownIdCompare> emptyEdges;
12322 emptyEdges.clear();
12324 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12325 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12327 int shapeId = itShape->first;
12328 MESSAGE(" --- Shape ID --- "<< shapeId);
12329 shapeIdToEdges[shapeId] = emptyEdges;
12331 std::vector<int> nodesEdges;
12333 std::set<int>::iterator its = itShape->second.begin();
12334 for (; its != itShape->second.end(); ++its)
12337 MESSAGE(" " << vtkId);
12338 int neighborsVtkIds[NBMAXNEIGHBORS];
12339 int downIds[NBMAXNEIGHBORS];
12340 unsigned char downTypes[NBMAXNEIGHBORS];
12341 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12342 for (int n = 0; n < nbNeighbors; n++)
12344 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12346 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12347 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12348 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12350 DownIdType edge(downIds[n], downTypes[n]);
12351 if (!shapeIdToEdges[shapeId].count(edge))
12353 shapeIdToEdges[shapeId].insert(edge);
12355 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12356 nodesEdges.push_back(vtkNodeId[0]);
12357 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12358 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12364 std::list<int> order;
12366 if (nodesEdges.size() > 0)
12368 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12369 nodesEdges[0] = -1;
12370 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12371 nodesEdges[1] = -1; // do not reuse this edge
12375 int nodeTofind = order.back(); // try first to push back
12377 for (i = 0; i<nodesEdges.size(); i++)
12378 if (nodesEdges[i] == nodeTofind)
12380 if (i == nodesEdges.size())
12381 found = false; // no follower found on back
12384 if (i%2) // odd ==> use the previous one
12385 if (nodesEdges[i-1] < 0)
12389 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12390 nodesEdges[i-1] = -1;
12392 else // even ==> use the next one
12393 if (nodesEdges[i+1] < 0)
12397 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12398 nodesEdges[i+1] = -1;
12403 // try to push front
12405 nodeTofind = order.front(); // try to push front
12406 for (i = 0; i<nodesEdges.size(); i++)
12407 if (nodesEdges[i] == nodeTofind)
12409 if (i == nodesEdges.size())
12411 found = false; // no predecessor found on front
12414 if (i%2) // odd ==> use the previous one
12415 if (nodesEdges[i-1] < 0)
12419 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12420 nodesEdges[i-1] = -1;
12422 else // even ==> use the next one
12423 if (nodesEdges[i+1] < 0)
12427 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12428 nodesEdges[i+1] = -1;
12434 std::vector<int> nodes;
12435 nodes.push_back(shapeId);
12436 std::list<int>::iterator itl = order.begin();
12437 for (; itl != order.end(); itl++)
12439 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12440 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12442 listOfListOfNodes.push_back(nodes);
12445 // partition geom faces with blocFissure
12446 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12447 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12453 //================================================================================
12455 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12456 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12457 * \return TRUE if operation has been completed successfully, FALSE otherwise
12459 //================================================================================
12461 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12463 // iterates on volume elements and detect all free faces on them
12464 SMESHDS_Mesh* aMesh = GetMeshDS();
12468 ElemFeatures faceType( SMDSAbs_Face );
12469 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12470 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12473 const SMDS_MeshVolume* volume = vIt->next();
12474 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12475 vTool.SetExternalNormal();
12476 const int iQuad = volume->IsQuadratic();
12477 faceType.SetQuad( iQuad );
12478 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12480 if (!vTool.IsFreeFace(iface))
12483 vector<const SMDS_MeshNode *> nodes;
12484 int nbFaceNodes = vTool.NbFaceNodes(iface);
12485 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12487 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12488 nodes.push_back(faceNodes[inode]);
12490 if (iQuad) // add medium nodes
12492 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12493 nodes.push_back(faceNodes[inode]);
12494 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12495 nodes.push_back(faceNodes[8]);
12497 // add new face based on volume nodes
12498 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12500 nbExisted++; // face already exsist
12504 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12509 return ( nbFree == ( nbExisted + nbCreated ));
12514 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12516 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12518 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12521 //================================================================================
12523 * \brief Creates missing boundary elements
12524 * \param elements - elements whose boundary is to be checked
12525 * \param dimension - defines type of boundary elements to create
12526 * \param group - a group to store created boundary elements in
12527 * \param targetMesh - a mesh to store created boundary elements in
12528 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12529 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12530 * boundary elements will be copied into the targetMesh
12531 * \param toAddExistingBondary - if true, not only new but also pre-existing
12532 * boundary elements will be added into the new group
12533 * \param aroundElements - if true, elements will be created on boundary of given
12534 * elements else, on boundary of the whole mesh.
12535 * \return nb of added boundary elements
12537 //================================================================================
12539 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12540 Bnd_Dimension dimension,
12541 SMESH_Group* group/*=0*/,
12542 SMESH_Mesh* targetMesh/*=0*/,
12543 bool toCopyElements/*=false*/,
12544 bool toCopyExistingBoundary/*=false*/,
12545 bool toAddExistingBondary/*= false*/,
12546 bool aroundElements/*= false*/)
12548 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12549 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12550 // hope that all elements are of the same type, do not check them all
12551 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12552 throw SALOME_Exception(LOCALIZED("wrong element type"));
12555 toCopyElements = toCopyExistingBoundary = false;
12557 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12558 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12559 int nbAddedBnd = 0;
12561 // editor adding present bnd elements and optionally holding elements to add to the group
12562 SMESH_MeshEditor* presentEditor;
12563 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12564 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12566 SMESH_MesherHelper helper( *myMesh );
12567 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12568 SMDS_VolumeTool vTool;
12569 TIDSortedElemSet avoidSet;
12570 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12573 typedef vector<const SMDS_MeshNode*> TConnectivity;
12574 TConnectivity tgtNodes;
12575 ElemFeatures elemKind( missType ), elemToCopy;
12577 SMDS_ElemIteratorPtr eIt;
12578 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12579 else eIt = elemSetIterator( elements );
12581 while (eIt->more())
12583 const SMDS_MeshElement* elem = eIt->next();
12584 const int iQuad = elem->IsQuadratic();
12585 elemKind.SetQuad( iQuad );
12587 // ------------------------------------------------------------------------------------
12588 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12589 // ------------------------------------------------------------------------------------
12590 vector<const SMDS_MeshElement*> presentBndElems;
12591 vector<TConnectivity> missingBndElems;
12592 TConnectivity nodes, elemNodes;
12593 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12595 vTool.SetExternalNormal();
12596 const SMDS_MeshElement* otherVol = 0;
12597 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12599 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12600 ( !aroundElements || elements.count( otherVol )))
12602 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12603 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12604 if ( missType == SMDSAbs_Edge ) // boundary edges
12606 nodes.resize( 2+iQuad );
12607 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12609 for ( int j = 0; j < nodes.size(); ++j )
12610 nodes[j] = nn[ i+j ];
12611 if ( const SMDS_MeshElement* edge =
12612 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12613 presentBndElems.push_back( edge );
12615 missingBndElems.push_back( nodes );
12618 else // boundary face
12621 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12622 nodes.push_back( nn[inode] ); // add corner nodes
12624 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12625 nodes.push_back( nn[inode] ); // add medium nodes
12626 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12628 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12630 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12631 SMDSAbs_Face, /*noMedium=*/false ))
12632 presentBndElems.push_back( f );
12634 missingBndElems.push_back( nodes );
12636 if ( targetMesh != myMesh )
12638 // add 1D elements on face boundary to be added to a new mesh
12639 const SMDS_MeshElement* edge;
12640 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12643 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12645 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12646 if ( edge && avoidSet.insert( edge ).second )
12647 presentBndElems.push_back( edge );
12653 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12655 avoidSet.clear(), avoidSet.insert( elem );
12656 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12657 SMDS_MeshElement::iterator() );
12658 elemNodes.push_back( elemNodes[0] );
12659 nodes.resize( 2 + iQuad );
12660 const int nbLinks = elem->NbCornerNodes();
12661 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12663 nodes[0] = elemNodes[iN];
12664 nodes[1] = elemNodes[iN+1+iQuad];
12665 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12666 continue; // not free link
12668 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12669 if ( const SMDS_MeshElement* edge =
12670 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12671 presentBndElems.push_back( edge );
12673 missingBndElems.push_back( nodes );
12677 // ---------------------------------
12678 // 2. Add missing boundary elements
12679 // ---------------------------------
12680 if ( targetMesh != myMesh )
12681 // instead of making a map of nodes in this mesh and targetMesh,
12682 // we create nodes with same IDs.
12683 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12685 TConnectivity& srcNodes = missingBndElems[i];
12686 tgtNodes.resize( srcNodes.size() );
12687 for ( inode = 0; inode < srcNodes.size(); ++inode )
12688 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12689 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12691 /*noMedium=*/false))
12693 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12697 for ( int i = 0; i < missingBndElems.size(); ++i )
12699 TConnectivity& nodes = missingBndElems[i];
12700 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12702 /*noMedium=*/false))
12704 SMDS_MeshElement* newElem =
12705 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12706 nbAddedBnd += bool( newElem );
12708 // try to set a new element to a shape
12709 if ( myMesh->HasShapeToMesh() )
12712 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12713 const size_t nbN = nodes.size() / (iQuad+1 );
12714 for ( inode = 0; inode < nbN && ok; ++inode )
12716 pair<int, TopAbs_ShapeEnum> i_stype =
12717 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12718 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12719 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12721 if ( ok && mediumShapes.size() > 1 )
12723 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12724 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12725 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12727 if (( ok = ( stype_i->first != stype_i_0.first )))
12728 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12729 aMesh->IndexToShape( stype_i_0.second ));
12732 if ( ok && mediumShapes.begin()->first == missShapeType )
12733 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12737 // ----------------------------------
12738 // 3. Copy present boundary elements
12739 // ----------------------------------
12740 if ( toCopyExistingBoundary )
12741 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12743 const SMDS_MeshElement* e = presentBndElems[i];
12744 tgtNodes.resize( e->NbNodes() );
12745 for ( inode = 0; inode < nodes.size(); ++inode )
12746 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12747 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12749 else // store present elements to add them to a group
12750 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12752 presentEditor->myLastCreatedElems.Append( presentBndElems[i] );
12755 } // loop on given elements
12757 // ---------------------------------------------
12758 // 4. Fill group with boundary elements
12759 // ---------------------------------------------
12762 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12763 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12764 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12766 tgtEditor.myLastCreatedElems.Clear();
12767 tgtEditor2.myLastCreatedElems.Clear();
12769 // -----------------------
12770 // 5. Copy given elements
12771 // -----------------------
12772 if ( toCopyElements && targetMesh != myMesh )
12774 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12775 else eIt = elemSetIterator( elements );
12776 while (eIt->more())
12778 const SMDS_MeshElement* elem = eIt->next();
12779 tgtNodes.resize( elem->NbNodes() );
12780 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12781 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12782 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12784 tgtEditor.myLastCreatedElems.Clear();
12790 //================================================================================
12792 * \brief Copy node position and set \a to node on the same geometry
12794 //================================================================================
12796 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12797 const SMDS_MeshNode* to )
12799 if ( !from || !to ) return;
12801 SMDS_PositionPtr pos = from->GetPosition();
12802 if ( !pos || from->getshapeId() < 1 ) return;
12804 switch ( pos->GetTypeOfPosition() )
12806 case SMDS_TOP_3DSPACE: break;
12808 case SMDS_TOP_FACE:
12810 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12811 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12812 fPos->GetUParameter(), fPos->GetVParameter() );
12815 case SMDS_TOP_EDGE:
12817 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12818 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12819 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12822 case SMDS_TOP_VERTEX:
12824 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12827 case SMDS_TOP_UNSPEC: