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->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2979 aMesh->RemoveElement( elem );
2982 // Quadratic quadrangle
2984 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2986 // get surface elem is on
2987 int aShapeId = FindShape( elem );
2988 if ( aShapeId != helper.GetSubShapeID() ) {
2992 shape = aMesh->IndexToShape( aShapeId );
2993 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2994 TopoDS_Face face = TopoDS::Face( shape );
2995 surface = BRep_Tool::Surface( face );
2996 if ( !surface.IsNull() )
2997 helper.SetSubShape( shape );
3001 const SMDS_MeshNode* aNodes [8];
3002 const SMDS_MeshNode* inFaceNode = 0;
3003 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3005 while ( itN->more() ) {
3006 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3007 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
3008 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
3010 inFaceNode = aNodes[ i-1 ];
3014 // find middle point for (0,1,2,3)
3015 // and create a node in this point;
3017 if ( surface.IsNull() ) {
3019 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
3023 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
3026 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
3028 p = surface->Value( uv.X(), uv.Y() ).XYZ();
3030 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
3031 myLastCreatedNodes.Append(newN);
3033 // create a new element
3034 const SMDS_MeshElement* newElem1 = 0;
3035 const SMDS_MeshElement* newElem2 = 0;
3037 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3038 aNodes[6], aNodes[7], newN );
3039 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3040 newN, aNodes[4], aNodes[5] );
3043 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3044 aNodes[7], aNodes[4], newN );
3045 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3046 newN, aNodes[5], aNodes[6] );
3048 myLastCreatedElems.Append(newElem1);
3049 myLastCreatedElems.Append(newElem2);
3050 // put a new triangle on the same shape and add to the same groups
3053 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3054 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3056 AddToSameGroups( newElem1, elem, aMesh );
3057 AddToSameGroups( newElem2, elem, aMesh );
3058 aMesh->RemoveElement( elem );
3065 //=======================================================================
3066 //function : getAngle
3068 //=======================================================================
3070 double getAngle(const SMDS_MeshElement * tr1,
3071 const SMDS_MeshElement * tr2,
3072 const SMDS_MeshNode * n1,
3073 const SMDS_MeshNode * n2)
3075 double angle = 2. * M_PI; // bad angle
3078 SMESH::Controls::TSequenceOfXYZ P1, P2;
3079 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3080 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3083 if(!tr1->IsQuadratic())
3084 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3086 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3087 if ( N1.SquareMagnitude() <= gp::Resolution() )
3089 if(!tr2->IsQuadratic())
3090 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3092 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3093 if ( N2.SquareMagnitude() <= gp::Resolution() )
3096 // find the first diagonal node n1 in the triangles:
3097 // take in account a diagonal link orientation
3098 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3099 for ( int t = 0; t < 2; t++ ) {
3100 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3101 int i = 0, iDiag = -1;
3102 while ( it->more()) {
3103 const SMDS_MeshElement *n = it->next();
3104 if ( n == n1 || n == n2 ) {
3108 if ( i - iDiag == 1 )
3109 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3118 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3121 angle = N1.Angle( N2 );
3126 // =================================================
3127 // class generating a unique ID for a pair of nodes
3128 // and able to return nodes by that ID
3129 // =================================================
3133 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3134 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3137 long GetLinkID (const SMDS_MeshNode * n1,
3138 const SMDS_MeshNode * n2) const
3140 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3143 bool GetNodes (const long theLinkID,
3144 const SMDS_MeshNode* & theNode1,
3145 const SMDS_MeshNode* & theNode2) const
3147 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3148 if ( !theNode1 ) return false;
3149 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3150 if ( !theNode2 ) return false;
3156 const SMESHDS_Mesh* myMesh;
3161 //=======================================================================
3162 //function : TriToQuad
3163 //purpose : Fuse neighbour triangles into quadrangles.
3164 // theCrit is used to select a neighbour to fuse with.
3165 // theMaxAngle is a max angle between element normals at which
3166 // fusion is still performed.
3167 //=======================================================================
3169 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3170 SMESH::Controls::NumericalFunctorPtr theCrit,
3171 const double theMaxAngle)
3173 myLastCreatedElems.Clear();
3174 myLastCreatedNodes.Clear();
3176 MESSAGE( "::TriToQuad()" );
3178 if ( !theCrit.get() )
3181 SMESHDS_Mesh * aMesh = GetMeshDS();
3183 // Prepare data for algo: build
3184 // 1. map of elements with their linkIDs
3185 // 2. map of linkIDs with their elements
3187 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3188 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3189 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3190 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3192 TIDSortedElemSet::iterator itElem;
3193 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3195 const SMDS_MeshElement* elem = *itElem;
3196 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3197 bool IsTria = ( elem->NbCornerNodes()==3 );
3198 if (!IsTria) continue;
3200 // retrieve element nodes
3201 const SMDS_MeshNode* aNodes [4];
3202 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3205 aNodes[ i++ ] = itN->next();
3206 aNodes[ 3 ] = aNodes[ 0 ];
3209 for ( i = 0; i < 3; i++ ) {
3210 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3211 // check if elements sharing a link can be fused
3212 itLE = mapLi_listEl.find( link );
3213 if ( itLE != mapLi_listEl.end() ) {
3214 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3216 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3217 //if ( FindShape( elem ) != FindShape( elem2 ))
3218 // continue; // do not fuse triangles laying on different shapes
3219 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3220 continue; // avoid making badly shaped quads
3221 (*itLE).second.push_back( elem );
3224 mapLi_listEl[ link ].push_back( elem );
3226 mapEl_setLi [ elem ].insert( link );
3229 // Clean the maps from the links shared by a sole element, ie
3230 // links to which only one element is bound in mapLi_listEl
3232 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3233 int nbElems = (*itLE).second.size();
3234 if ( nbElems < 2 ) {
3235 const SMDS_MeshElement* elem = (*itLE).second.front();
3236 SMESH_TLink link = (*itLE).first;
3237 mapEl_setLi[ elem ].erase( link );
3238 if ( mapEl_setLi[ elem ].empty() )
3239 mapEl_setLi.erase( elem );
3243 // Algo: fuse triangles into quadrangles
3245 while ( ! mapEl_setLi.empty() ) {
3246 // Look for the start element:
3247 // the element having the least nb of shared links
3248 const SMDS_MeshElement* startElem = 0;
3250 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3251 int nbLinks = (*itEL).second.size();
3252 if ( nbLinks < minNbLinks ) {
3253 startElem = (*itEL).first;
3254 minNbLinks = nbLinks;
3255 if ( minNbLinks == 1 )
3260 // search elements to fuse starting from startElem or links of elements
3261 // fused earlyer - startLinks
3262 list< SMESH_TLink > startLinks;
3263 while ( startElem || !startLinks.empty() ) {
3264 while ( !startElem && !startLinks.empty() ) {
3265 // Get an element to start, by a link
3266 SMESH_TLink linkId = startLinks.front();
3267 startLinks.pop_front();
3268 itLE = mapLi_listEl.find( linkId );
3269 if ( itLE != mapLi_listEl.end() ) {
3270 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3271 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3272 for ( ; itE != listElem.end() ; itE++ )
3273 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3275 mapLi_listEl.erase( itLE );
3280 // Get candidates to be fused
3281 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3282 const SMESH_TLink *link12, *link13;
3284 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3285 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3286 ASSERT( !setLi.empty() );
3287 set< SMESH_TLink >::iterator itLi;
3288 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3290 const SMESH_TLink & link = (*itLi);
3291 itLE = mapLi_listEl.find( link );
3292 if ( itLE == mapLi_listEl.end() )
3295 const SMDS_MeshElement* elem = (*itLE).second.front();
3297 elem = (*itLE).second.back();
3298 mapLi_listEl.erase( itLE );
3299 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3310 // add other links of elem to list of links to re-start from
3311 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3312 set< SMESH_TLink >::iterator it;
3313 for ( it = links.begin(); it != links.end(); it++ ) {
3314 const SMESH_TLink& link2 = (*it);
3315 if ( link2 != link )
3316 startLinks.push_back( link2 );
3320 // Get nodes of possible quadrangles
3321 const SMDS_MeshNode *n12 [4], *n13 [4];
3322 bool Ok12 = false, Ok13 = false;
3323 const SMDS_MeshNode *linkNode1, *linkNode2;
3325 linkNode1 = link12->first;
3326 linkNode2 = link12->second;
3327 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3331 linkNode1 = link13->first;
3332 linkNode2 = link13->second;
3333 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3337 // Choose a pair to fuse
3338 if ( Ok12 && Ok13 ) {
3339 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3340 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3341 double aBadRate12 = getBadRate( &quad12, theCrit );
3342 double aBadRate13 = getBadRate( &quad13, theCrit );
3343 if ( aBadRate13 < aBadRate12 )
3350 // and remove fused elems and remove links from the maps
3351 mapEl_setLi.erase( tr1 );
3354 mapEl_setLi.erase( tr2 );
3355 mapLi_listEl.erase( *link12 );
3356 if ( tr1->NbNodes() == 3 )
3358 const SMDS_MeshElement* newElem = 0;
3359 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3360 myLastCreatedElems.Append(newElem);
3361 AddToSameGroups( newElem, tr1, aMesh );
3362 int aShapeId = tr1->getshapeId();
3364 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3365 aMesh->RemoveElement( tr1 );
3366 aMesh->RemoveElement( tr2 );
3369 vector< const SMDS_MeshNode* > N1;
3370 vector< const SMDS_MeshNode* > N2;
3371 getNodesFromTwoTria(tr1,tr2,N1,N2);
3372 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3373 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3374 // i.e. first nodes from both arrays form a new diagonal
3375 const SMDS_MeshNode* aNodes[8];
3384 const SMDS_MeshElement* newElem = 0;
3385 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3386 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3387 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3389 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3390 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3391 myLastCreatedElems.Append(newElem);
3392 AddToSameGroups( newElem, tr1, aMesh );
3393 int aShapeId = tr1->getshapeId();
3395 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3396 aMesh->RemoveElement( tr1 );
3397 aMesh->RemoveElement( tr2 );
3398 // remove middle node (9)
3399 if ( N1[4]->NbInverseElements() == 0 )
3400 aMesh->RemoveNode( N1[4] );
3401 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3402 aMesh->RemoveNode( N1[6] );
3403 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3404 aMesh->RemoveNode( N2[6] );
3409 mapEl_setLi.erase( tr3 );
3410 mapLi_listEl.erase( *link13 );
3411 if ( tr1->NbNodes() == 3 ) {
3412 const SMDS_MeshElement* newElem = 0;
3413 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3414 myLastCreatedElems.Append(newElem);
3415 AddToSameGroups( newElem, tr1, aMesh );
3416 int aShapeId = tr1->getshapeId();
3418 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3419 aMesh->RemoveElement( tr1 );
3420 aMesh->RemoveElement( tr3 );
3423 vector< const SMDS_MeshNode* > N1;
3424 vector< const SMDS_MeshNode* > N2;
3425 getNodesFromTwoTria(tr1,tr3,N1,N2);
3426 // now we receive following N1 and N2 (using numeration as above image)
3427 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3428 // i.e. first nodes from both arrays form a new diagonal
3429 const SMDS_MeshNode* aNodes[8];
3438 const SMDS_MeshElement* newElem = 0;
3439 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3440 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3441 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3443 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3444 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3445 myLastCreatedElems.Append(newElem);
3446 AddToSameGroups( newElem, tr1, aMesh );
3447 int aShapeId = tr1->getshapeId();
3449 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3450 aMesh->RemoveElement( tr1 );
3451 aMesh->RemoveElement( tr3 );
3452 // remove middle node (9)
3453 if ( N1[4]->NbInverseElements() == 0 )
3454 aMesh->RemoveNode( N1[4] );
3455 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3456 aMesh->RemoveNode( N1[6] );
3457 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3458 aMesh->RemoveNode( N2[6] );
3462 // Next element to fuse: the rejected one
3464 startElem = Ok12 ? tr3 : tr2;
3466 } // if ( startElem )
3467 } // while ( startElem || !startLinks.empty() )
3468 } // while ( ! mapEl_setLi.empty() )
3474 /*#define DUMPSO(txt) \
3475 // cout << txt << endl;
3476 //=============================================================================
3480 //=============================================================================
3481 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3485 int tmp = idNodes[ i1 ];
3486 idNodes[ i1 ] = idNodes[ i2 ];
3487 idNodes[ i2 ] = tmp;
3488 gp_Pnt Ptmp = P[ i1 ];
3491 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3494 //=======================================================================
3495 //function : SortQuadNodes
3496 //purpose : Set 4 nodes of a quadrangle face in a good order.
3497 // Swap 1<->2 or 2<->3 nodes and correspondingly return
3499 //=======================================================================
3501 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3506 for ( i = 0; i < 4; i++ ) {
3507 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3509 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3512 gp_Vec V1(P[0], P[1]);
3513 gp_Vec V2(P[0], P[2]);
3514 gp_Vec V3(P[0], P[3]);
3516 gp_Vec Cross1 = V1 ^ V2;
3517 gp_Vec Cross2 = V2 ^ V3;
3520 if (Cross1.Dot(Cross2) < 0)
3525 if (Cross1.Dot(Cross2) < 0)
3529 swap ( i, i + 1, idNodes, P );
3531 // for ( int ii = 0; ii < 4; ii++ ) {
3532 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3533 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3539 //=======================================================================
3540 //function : SortHexaNodes
3541 //purpose : Set 8 nodes of a hexahedron in a good order.
3542 // Return success status
3543 //=======================================================================
3545 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3550 DUMPSO( "INPUT: ========================================");
3551 for ( i = 0; i < 8; i++ ) {
3552 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3553 if ( !n ) return false;
3554 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3555 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3557 DUMPSO( "========================================");
3560 set<int> faceNodes; // ids of bottom face nodes, to be found
3561 set<int> checkedId1; // ids of tried 2-nd nodes
3562 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3563 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
3564 int iMin, iLoop1 = 0;
3566 // Loop to try the 2-nd nodes
3568 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3570 // Find not checked 2-nd node
3571 for ( i = 1; i < 8; i++ )
3572 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3573 int id1 = idNodes[i];
3574 swap ( 1, i, idNodes, P );
3575 checkedId1.insert ( id1 );
3579 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3580 // ie that all but meybe one (id3 which is on the same face) nodes
3581 // lay on the same side from the triangle plane.
3583 bool manyInPlane = false; // more than 4 nodes lay in plane
3585 while ( ++iLoop2 < 6 ) {
3587 // get 1-2-3 plane coeffs
3588 Standard_Real A, B, C, D;
3589 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3590 if ( N.SquareMagnitude() > gp::Resolution() )
3592 gp_Pln pln ( P[0], N );
3593 pln.Coefficients( A, B, C, D );
3595 // find the node (iMin) closest to pln
3596 Standard_Real dist[ 8 ], minDist = DBL_MAX;
3598 for ( i = 3; i < 8; i++ ) {
3599 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3600 if ( fabs( dist[i] ) < minDist ) {
3601 minDist = fabs( dist[i] );
3604 if ( fabs( dist[i] ) <= tol )
3605 idInPln.insert( idNodes[i] );
3608 // there should not be more than 4 nodes in bottom plane
3609 if ( idInPln.size() > 1 )
3611 DUMPSO( "### idInPln.size() = " << idInPln.size());
3612 // idInPlane does not contain the first 3 nodes
3613 if ( manyInPlane || idInPln.size() == 5)
3614 return false; // all nodes in one plane
3617 // set the 1-st node to be not in plane
3618 for ( i = 3; i < 8; i++ ) {
3619 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3620 DUMPSO( "### Reset 0-th node");
3621 swap( 0, i, idNodes, P );
3626 // reset to re-check second nodes
3627 leastDist = DBL_MAX;
3631 break; // from iLoop2;
3634 // check that the other 4 nodes are on the same side
3635 bool sameSide = true;
3636 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3637 for ( i = 3; sameSide && i < 8; i++ ) {
3639 sameSide = ( isNeg == dist[i] <= 0.);
3642 // keep best solution
3643 if ( sameSide && minDist < leastDist ) {
3644 leastDist = minDist;
3646 faceNodes.insert( idNodes[ 1 ] );
3647 faceNodes.insert( idNodes[ 2 ] );
3648 faceNodes.insert( idNodes[ iMin ] );
3649 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3650 << " leastDist = " << leastDist);
3651 if ( leastDist <= DBL_MIN )
3656 // set next 3-d node to check
3657 int iNext = 2 + iLoop2;
3659 DUMPSO( "Try 2-nd");
3660 swap ( 2, iNext, idNodes, P );
3662 } // while ( iLoop2 < 6 )
3665 if ( faceNodes.empty() ) return false;
3667 // Put the faceNodes in proper places
3668 for ( i = 4; i < 8; i++ ) {
3669 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3670 // find a place to put
3672 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3674 DUMPSO( "Set faceNodes");
3675 swap ( iTo, i, idNodes, P );
3680 // Set nodes of the found bottom face in good order
3681 DUMPSO( " Found bottom face: ");
3682 i = SortQuadNodes( theMesh, idNodes );
3684 gp_Pnt Ptmp = P[ i ];
3689 // for ( int ii = 0; ii < 4; ii++ ) {
3690 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3691 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3694 // Gravity center of the top and bottom faces
3695 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3696 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3698 // Get direction from the bottom to the top face
3699 gp_Vec upDir ( aGCb, aGCt );
3700 Standard_Real upDirSize = upDir.Magnitude();
3701 if ( upDirSize <= gp::Resolution() ) return false;
3704 // Assure that the bottom face normal points up
3705 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3706 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3707 if ( Nb.Dot( upDir ) < 0 ) {
3708 DUMPSO( "Reverse bottom face");
3709 swap( 1, 3, idNodes, P );
3712 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3713 Standard_Real minDist = DBL_MAX;
3714 for ( i = 4; i < 8; i++ ) {
3715 // projection of P[i] to the plane defined by P[0] and upDir
3716 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3717 Standard_Real sqDist = P[0].SquareDistance( Pp );
3718 if ( sqDist < minDist ) {
3723 DUMPSO( "Set 4-th");
3724 swap ( 4, iMin, idNodes, P );
3726 // Set nodes of the top face in good order
3727 DUMPSO( "Sort top face");
3728 i = SortQuadNodes( theMesh, &idNodes[4] );
3731 gp_Pnt Ptmp = P[ i ];
3736 // Assure that direction of the top face normal is from the bottom face
3737 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3738 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3739 if ( Nt.Dot( upDir ) < 0 ) {
3740 DUMPSO( "Reverse top face");
3741 swap( 5, 7, idNodes, P );
3744 // DUMPSO( "OUTPUT: ========================================");
3745 // for ( i = 0; i < 8; i++ ) {
3746 // float *p = ugrid->GetPoint(idNodes[i]);
3747 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3753 //================================================================================
3755 * \brief Return nodes linked to the given one
3756 * \param theNode - the node
3757 * \param linkedNodes - the found nodes
3758 * \param type - the type of elements to check
3760 * Medium nodes are ignored
3762 //================================================================================
3764 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3765 TIDSortedElemSet & linkedNodes,
3766 SMDSAbs_ElementType type )
3768 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3769 while ( elemIt->more() )
3771 const SMDS_MeshElement* elem = elemIt->next();
3772 if(elem->GetType() == SMDSAbs_0DElement)
3775 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3776 if ( elem->GetType() == SMDSAbs_Volume )
3778 SMDS_VolumeTool vol( elem );
3779 while ( nodeIt->more() ) {
3780 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3781 if ( theNode != n && vol.IsLinked( theNode, n ))
3782 linkedNodes.insert( n );
3787 for ( int i = 0; nodeIt->more(); ++i ) {
3788 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3789 if ( n == theNode ) {
3790 int iBefore = i - 1;
3792 if ( elem->IsQuadratic() ) {
3793 int nb = elem->NbNodes() / 2;
3794 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3795 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3797 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3798 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3805 //=======================================================================
3806 //function : laplacianSmooth
3807 //purpose : pulls theNode toward the center of surrounding nodes directly
3808 // connected to that node along an element edge
3809 //=======================================================================
3811 void laplacianSmooth(const SMDS_MeshNode* theNode,
3812 const Handle(Geom_Surface)& theSurface,
3813 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3815 // find surrounding nodes
3817 TIDSortedElemSet nodeSet;
3818 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3820 // compute new coodrs
3822 double coord[] = { 0., 0., 0. };
3823 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3824 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3825 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3826 if ( theSurface.IsNull() ) { // smooth in 3D
3827 coord[0] += node->X();
3828 coord[1] += node->Y();
3829 coord[2] += node->Z();
3831 else { // smooth in 2D
3832 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3833 gp_XY* uv = theUVMap[ node ];
3834 coord[0] += uv->X();
3835 coord[1] += uv->Y();
3838 int nbNodes = nodeSet.size();
3841 coord[0] /= nbNodes;
3842 coord[1] /= nbNodes;
3844 if ( !theSurface.IsNull() ) {
3845 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3846 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3847 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3853 coord[2] /= nbNodes;
3857 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3860 //=======================================================================
3861 //function : centroidalSmooth
3862 //purpose : pulls theNode toward the element-area-weighted centroid of the
3863 // surrounding elements
3864 //=======================================================================
3866 void centroidalSmooth(const SMDS_MeshNode* theNode,
3867 const Handle(Geom_Surface)& theSurface,
3868 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3870 gp_XYZ aNewXYZ(0.,0.,0.);
3871 SMESH::Controls::Area anAreaFunc;
3872 double totalArea = 0.;
3877 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3878 while ( elemIt->more() )
3880 const SMDS_MeshElement* elem = elemIt->next();
3883 gp_XYZ elemCenter(0.,0.,0.);
3884 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3885 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3886 int nn = elem->NbNodes();
3887 if(elem->IsQuadratic()) nn = nn/2;
3889 //while ( itN->more() ) {
3891 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3893 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3894 aNodePoints.push_back( aP );
3895 if ( !theSurface.IsNull() ) { // smooth in 2D
3896 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3897 gp_XY* uv = theUVMap[ aNode ];
3898 aP.SetCoord( uv->X(), uv->Y(), 0. );
3902 double elemArea = anAreaFunc.GetValue( aNodePoints );
3903 totalArea += elemArea;
3905 aNewXYZ += elemCenter * elemArea;
3907 aNewXYZ /= totalArea;
3908 if ( !theSurface.IsNull() ) {
3909 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3910 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3915 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3918 //=======================================================================
3919 //function : getClosestUV
3920 //purpose : return UV of closest projection
3921 //=======================================================================
3923 static bool getClosestUV (Extrema_GenExtPS& projector,
3924 const gp_Pnt& point,
3927 projector.Perform( point );
3928 if ( projector.IsDone() ) {
3929 double u, v, minVal = DBL_MAX;
3930 for ( int i = projector.NbExt(); i > 0; i-- )
3931 if ( projector.SquareDistance( i ) < minVal ) {
3932 minVal = projector.SquareDistance( i );
3933 projector.Point( i ).Parameter( u, v );
3935 result.SetCoord( u, v );
3941 //=======================================================================
3943 //purpose : Smooth theElements during theNbIterations or until a worst
3944 // element has aspect ratio <= theTgtAspectRatio.
3945 // Aspect Ratio varies in range [1.0, inf].
3946 // If theElements is empty, the whole mesh is smoothed.
3947 // theFixedNodes contains additionally fixed nodes. Nodes built
3948 // on edges and boundary nodes are always fixed.
3949 //=======================================================================
3951 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3952 set<const SMDS_MeshNode*> & theFixedNodes,
3953 const SmoothMethod theSmoothMethod,
3954 const int theNbIterations,
3955 double theTgtAspectRatio,
3958 myLastCreatedElems.Clear();
3959 myLastCreatedNodes.Clear();
3961 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3963 if ( theTgtAspectRatio < 1.0 )
3964 theTgtAspectRatio = 1.0;
3966 const double disttol = 1.e-16;
3968 SMESH::Controls::AspectRatio aQualityFunc;
3970 SMESHDS_Mesh* aMesh = GetMeshDS();
3972 if ( theElems.empty() ) {
3973 // add all faces to theElems
3974 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3975 while ( fIt->more() ) {
3976 const SMDS_MeshElement* face = fIt->next();
3977 theElems.insert( theElems.end(), face );
3980 // get all face ids theElems are on
3981 set< int > faceIdSet;
3982 TIDSortedElemSet::iterator itElem;
3984 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3985 int fId = FindShape( *itElem );
3986 // check that corresponding submesh exists and a shape is face
3988 faceIdSet.find( fId ) == faceIdSet.end() &&
3989 aMesh->MeshElements( fId )) {
3990 TopoDS_Shape F = aMesh->IndexToShape( fId );
3991 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3992 faceIdSet.insert( fId );
3995 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3997 // ===============================================
3998 // smooth elements on each TopoDS_Face separately
3999 // ===============================================
4001 SMESH_MesherHelper helper( *GetMesh() );
4003 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
4004 for ( ; fId != faceIdSet.rend(); ++fId )
4006 // get face surface and submesh
4007 Handle(Geom_Surface) surface;
4008 SMESHDS_SubMesh* faceSubMesh = 0;
4010 double fToler2 = 0, f,l;
4011 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
4012 bool isUPeriodic = false, isVPeriodic = false;
4015 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
4016 surface = BRep_Tool::Surface( face );
4017 faceSubMesh = aMesh->MeshElements( *fId );
4018 fToler2 = BRep_Tool::Tolerance( face );
4019 fToler2 *= fToler2 * 10.;
4020 isUPeriodic = surface->IsUPeriodic();
4023 isVPeriodic = surface->IsVPeriodic();
4026 surface->Bounds( u1, u2, v1, v2 );
4027 helper.SetSubShape( face );
4029 // ---------------------------------------------------------
4030 // for elements on a face, find movable and fixed nodes and
4031 // compute UV for them
4032 // ---------------------------------------------------------
4033 bool checkBoundaryNodes = false;
4034 bool isQuadratic = false;
4035 set<const SMDS_MeshNode*> setMovableNodes;
4036 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4037 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4038 list< const SMDS_MeshElement* > elemsOnFace;
4040 Extrema_GenExtPS projector;
4041 GeomAdaptor_Surface surfAdaptor;
4042 if ( !surface.IsNull() ) {
4043 surfAdaptor.Load( surface );
4044 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4046 int nbElemOnFace = 0;
4047 itElem = theElems.begin();
4048 // loop on not yet smoothed elements: look for elems on a face
4049 while ( itElem != theElems.end() )
4051 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4052 break; // all elements found
4054 const SMDS_MeshElement* elem = *itElem;
4055 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4056 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4060 elemsOnFace.push_back( elem );
4061 theElems.erase( itElem++ );
4065 isQuadratic = elem->IsQuadratic();
4067 // get movable nodes of elem
4068 const SMDS_MeshNode* node;
4069 SMDS_TypeOfPosition posType;
4070 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4071 int nn = 0, nbn = elem->NbNodes();
4072 if(elem->IsQuadratic())
4074 while ( nn++ < nbn ) {
4075 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4076 const SMDS_PositionPtr& pos = node->GetPosition();
4077 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4078 if (posType != SMDS_TOP_EDGE &&
4079 posType != SMDS_TOP_VERTEX &&
4080 theFixedNodes.find( node ) == theFixedNodes.end())
4082 // check if all faces around the node are on faceSubMesh
4083 // because a node on edge may be bound to face
4084 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4086 if ( faceSubMesh ) {
4087 while ( eIt->more() && all ) {
4088 const SMDS_MeshElement* e = eIt->next();
4089 all = faceSubMesh->Contains( e );
4093 setMovableNodes.insert( node );
4095 checkBoundaryNodes = true;
4097 if ( posType == SMDS_TOP_3DSPACE )
4098 checkBoundaryNodes = true;
4101 if ( surface.IsNull() )
4104 // get nodes to check UV
4105 list< const SMDS_MeshNode* > uvCheckNodes;
4106 const SMDS_MeshNode* nodeInFace = 0;
4107 itN = elem->nodesIterator();
4108 nn = 0; nbn = elem->NbNodes();
4109 if(elem->IsQuadratic())
4111 while ( nn++ < nbn ) {
4112 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4113 if ( node->GetPosition()->GetDim() == 2 )
4115 if ( uvMap.find( node ) == uvMap.end() )
4116 uvCheckNodes.push_back( node );
4117 // add nodes of elems sharing node
4118 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4119 // while ( eIt->more() ) {
4120 // const SMDS_MeshElement* e = eIt->next();
4121 // if ( e != elem ) {
4122 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4123 // while ( nIt->more() ) {
4124 // const SMDS_MeshNode* n =
4125 // static_cast<const SMDS_MeshNode*>( nIt->next() );
4126 // if ( uvMap.find( n ) == uvMap.end() )
4127 // uvCheckNodes.push_back( n );
4133 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4134 for ( ; n != uvCheckNodes.end(); ++n ) {
4137 const SMDS_PositionPtr& pos = node->GetPosition();
4138 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4142 bool toCheck = true;
4143 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4145 // compute not existing UV
4146 bool project = ( posType == SMDS_TOP_3DSPACE );
4147 // double dist1 = DBL_MAX, dist2 = 0;
4148 // if ( posType != SMDS_TOP_3DSPACE ) {
4149 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4150 // project = dist1 > fToler2;
4152 if ( project ) { // compute new UV
4154 gp_Pnt pNode = SMESH_TNodeXYZ( node );
4155 if ( !getClosestUV( projector, pNode, newUV )) {
4156 MESSAGE("Node Projection Failed " << node);
4160 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4162 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4164 // if ( posType != SMDS_TOP_3DSPACE )
4165 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4166 // if ( dist2 < dist1 )
4170 // store UV in the map
4171 listUV.push_back( uv );
4172 uvMap.insert( make_pair( node, &listUV.back() ));
4174 } // loop on not yet smoothed elements
4176 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4177 checkBoundaryNodes = true;
4179 // fix nodes on mesh boundary
4181 if ( checkBoundaryNodes ) {
4182 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4183 map< SMESH_TLink, int >::iterator link_nb;
4184 // put all elements links to linkNbMap
4185 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4186 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4187 const SMDS_MeshElement* elem = (*elemIt);
4188 int nbn = elem->NbCornerNodes();
4189 // loop on elem links: insert them in linkNbMap
4190 for ( int iN = 0; iN < nbn; ++iN ) {
4191 const SMDS_MeshNode* n1 = elem->GetNode( iN );
4192 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4193 SMESH_TLink link( n1, n2 );
4194 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4198 // remove nodes that are in links encountered only once from setMovableNodes
4199 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4200 if ( link_nb->second == 1 ) {
4201 setMovableNodes.erase( link_nb->first.node1() );
4202 setMovableNodes.erase( link_nb->first.node2() );
4207 // -----------------------------------------------------
4208 // for nodes on seam edge, compute one more UV ( uvMap2 );
4209 // find movable nodes linked to nodes on seam and which
4210 // are to be smoothed using the second UV ( uvMap2 )
4211 // -----------------------------------------------------
4213 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4214 if ( !surface.IsNull() ) {
4215 TopExp_Explorer eExp( face, TopAbs_EDGE );
4216 for ( ; eExp.More(); eExp.Next() ) {
4217 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4218 if ( !BRep_Tool::IsClosed( edge, face ))
4220 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4221 if ( !sm ) continue;
4222 // find out which parameter varies for a node on seam
4225 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4226 if ( pcurve.IsNull() ) continue;
4227 uv1 = pcurve->Value( f );
4229 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4230 if ( pcurve.IsNull() ) continue;
4231 uv2 = pcurve->Value( f );
4232 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4234 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4235 std::swap( uv1, uv2 );
4236 // get nodes on seam and its vertices
4237 list< const SMDS_MeshNode* > seamNodes;
4238 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4239 while ( nSeamIt->more() ) {
4240 const SMDS_MeshNode* node = nSeamIt->next();
4241 if ( !isQuadratic || !IsMedium( node ))
4242 seamNodes.push_back( node );
4244 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4245 for ( ; vExp.More(); vExp.Next() ) {
4246 sm = aMesh->MeshElements( vExp.Current() );
4248 nSeamIt = sm->GetNodes();
4249 while ( nSeamIt->more() )
4250 seamNodes.push_back( nSeamIt->next() );
4253 // loop on nodes on seam
4254 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4255 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4256 const SMDS_MeshNode* nSeam = *noSeIt;
4257 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4258 if ( n_uv == uvMap.end() )
4261 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4262 // set the second UV
4263 listUV.push_back( *n_uv->second );
4264 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4265 if ( uvMap2.empty() )
4266 uvMap2 = uvMap; // copy the uvMap contents
4267 uvMap2[ nSeam ] = &listUV.back();
4269 // collect movable nodes linked to ones on seam in nodesNearSeam
4270 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4271 while ( eIt->more() ) {
4272 const SMDS_MeshElement* e = eIt->next();
4273 int nbUseMap1 = 0, nbUseMap2 = 0;
4274 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4275 int nn = 0, nbn = e->NbNodes();
4276 if(e->IsQuadratic()) nbn = nbn/2;
4277 while ( nn++ < nbn )
4279 const SMDS_MeshNode* n =
4280 static_cast<const SMDS_MeshNode*>( nIt->next() );
4282 setMovableNodes.find( n ) == setMovableNodes.end() )
4284 // add only nodes being closer to uv2 than to uv1
4285 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4286 // 0.5 * ( n->Y() + nSeam->Y() ),
4287 // 0.5 * ( n->Z() + nSeam->Z() ));
4289 // getClosestUV( projector, pMid, uv );
4290 double x = uvMap[ n ]->Coord( iPar );
4291 if ( Abs( uv1.Coord( iPar ) - x ) >
4292 Abs( uv2.Coord( iPar ) - x )) {
4293 nodesNearSeam.insert( n );
4299 // for centroidalSmooth all element nodes must
4300 // be on one side of a seam
4301 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4302 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4304 while ( nn++ < nbn ) {
4305 const SMDS_MeshNode* n =
4306 static_cast<const SMDS_MeshNode*>( nIt->next() );
4307 setMovableNodes.erase( n );
4311 } // loop on nodes on seam
4312 } // loop on edge of a face
4313 } // if ( !face.IsNull() )
4315 if ( setMovableNodes.empty() ) {
4316 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4317 continue; // goto next face
4325 double maxRatio = -1., maxDisplacement = -1.;
4326 set<const SMDS_MeshNode*>::iterator nodeToMove;
4327 for ( it = 0; it < theNbIterations; it++ ) {
4328 maxDisplacement = 0.;
4329 nodeToMove = setMovableNodes.begin();
4330 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4331 const SMDS_MeshNode* node = (*nodeToMove);
4332 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4335 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4336 if ( theSmoothMethod == LAPLACIAN )
4337 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4339 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4341 // node displacement
4342 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4343 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4344 if ( aDispl > maxDisplacement )
4345 maxDisplacement = aDispl;
4347 // no node movement => exit
4348 //if ( maxDisplacement < 1.e-16 ) {
4349 if ( maxDisplacement < disttol ) {
4350 MESSAGE("-- no node movement --");
4354 // check elements quality
4356 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4357 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4358 const SMDS_MeshElement* elem = (*elemIt);
4359 if ( !elem || elem->GetType() != SMDSAbs_Face )
4361 SMESH::Controls::TSequenceOfXYZ aPoints;
4362 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4363 double aValue = aQualityFunc.GetValue( aPoints );
4364 if ( aValue > maxRatio )
4368 if ( maxRatio <= theTgtAspectRatio ) {
4369 MESSAGE("-- quality achived --");
4372 if (it+1 == theNbIterations) {
4373 MESSAGE("-- Iteration limit exceeded --");
4375 } // smoothing iterations
4377 MESSAGE(" Face id: " << *fId <<
4378 " Nb iterstions: " << it <<
4379 " Displacement: " << maxDisplacement <<
4380 " Aspect Ratio " << maxRatio);
4382 // ---------------------------------------
4383 // new nodes positions are computed,
4384 // record movement in DS and set new UV
4385 // ---------------------------------------
4386 nodeToMove = setMovableNodes.begin();
4387 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4388 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4389 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4390 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4391 if ( node_uv != uvMap.end() ) {
4392 gp_XY* uv = node_uv->second;
4394 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4398 // move medium nodes of quadratic elements
4401 vector<const SMDS_MeshNode*> nodes;
4403 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4404 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4406 const SMDS_MeshElement* QF = *elemIt;
4407 if ( QF->IsQuadratic() )
4409 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4410 SMDS_MeshElement::iterator() );
4411 nodes.push_back( nodes[0] );
4413 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4415 if ( !surface.IsNull() )
4417 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4418 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4419 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4420 xyz = surface->Value( uv.X(), uv.Y() );
4423 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4425 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4426 // we have to move a medium node
4427 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4433 } // loop on face ids
4439 //=======================================================================
4440 //function : isReverse
4441 //purpose : Return true if normal of prevNodes is not co-directied with
4442 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4443 // iNotSame is where prevNodes and nextNodes are different.
4444 // If result is true then future volume orientation is OK
4445 //=======================================================================
4447 bool isReverse(const SMDS_MeshElement* face,
4448 const vector<const SMDS_MeshNode*>& prevNodes,
4449 const vector<const SMDS_MeshNode*>& nextNodes,
4453 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4454 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4455 gp_XYZ extrDir( pN - pP ), faceNorm;
4456 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4458 return faceNorm * extrDir < 0.0;
4461 //================================================================================
4463 * \brief Assure that theElemSets[0] holds elements, not nodes
4465 //================================================================================
4467 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4469 if ( !theElemSets[0].empty() &&
4470 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4472 std::swap( theElemSets[0], theElemSets[1] );
4474 else if ( !theElemSets[1].empty() &&
4475 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4477 std::swap( theElemSets[0], theElemSets[1] );
4482 //=======================================================================
4484 * \brief Create elements by sweeping an element
4485 * \param elem - element to sweep
4486 * \param newNodesItVec - nodes generated from each node of the element
4487 * \param newElems - generated elements
4488 * \param nbSteps - number of sweeping steps
4489 * \param srcElements - to append elem for each generated element
4491 //=======================================================================
4493 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4494 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4495 list<const SMDS_MeshElement*>& newElems,
4497 SMESH_SequenceOfElemPtr& srcElements)
4499 //MESSAGE("sweepElement " << nbSteps);
4500 SMESHDS_Mesh* aMesh = GetMeshDS();
4502 const int nbNodes = elem->NbNodes();
4503 const int nbCorners = elem->NbCornerNodes();
4504 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4505 polyhedron creation !!! */
4506 // Loop on elem nodes:
4507 // find new nodes and detect same nodes indices
4508 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4509 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4510 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4511 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4513 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4514 vector<int> sames(nbNodes);
4515 vector<bool> isSingleNode(nbNodes);
4517 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4518 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4519 const SMDS_MeshNode* node = nnIt->first;
4520 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4521 if ( listNewNodes.empty() )
4524 itNN [ iNode ] = listNewNodes.begin();
4525 prevNod[ iNode ] = node;
4526 nextNod[ iNode ] = listNewNodes.front();
4528 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4529 corner node of linear */
4530 if ( prevNod[ iNode ] != nextNod [ iNode ])
4531 nbDouble += !isSingleNode[iNode];
4533 if( iNode < nbCorners ) { // check corners only
4534 if ( prevNod[ iNode ] == nextNod [ iNode ])
4535 sames[nbSame++] = iNode;
4537 iNotSameNode = iNode;
4541 if ( nbSame == nbNodes || nbSame > 2) {
4542 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4546 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4548 // fix nodes order to have bottom normal external
4549 if ( baseType == SMDSEntity_Polygon )
4551 std::reverse( itNN.begin(), itNN.end() );
4552 std::reverse( prevNod.begin(), prevNod.end() );
4553 std::reverse( midlNod.begin(), midlNod.end() );
4554 std::reverse( nextNod.begin(), nextNod.end() );
4555 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4559 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4560 SMDS_MeshCell::applyInterlace( ind, itNN );
4561 SMDS_MeshCell::applyInterlace( ind, prevNod );
4562 SMDS_MeshCell::applyInterlace( ind, nextNod );
4563 SMDS_MeshCell::applyInterlace( ind, midlNod );
4564 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4567 sames[nbSame] = iNotSameNode;
4568 for ( int j = 0; j <= nbSame; ++j )
4569 for ( size_t i = 0; i < ind.size(); ++i )
4570 if ( ind[i] == sames[j] )
4575 iNotSameNode = sames[nbSame];
4579 else if ( elem->GetType() == SMDSAbs_Edge )
4581 // orient a new face same as adjacent one
4583 const SMDS_MeshElement* e;
4584 TIDSortedElemSet dummy;
4585 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4586 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4587 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4589 // there is an adjacent face, check order of nodes in it
4590 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4593 std::swap( itNN[0], itNN[1] );
4594 std::swap( prevNod[0], prevNod[1] );
4595 std::swap( nextNod[0], nextNod[1] );
4596 isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4598 sames[0] = 1 - sames[0];
4599 iNotSameNode = 1 - iNotSameNode;
4604 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4606 iSameNode = sames[ nbSame-1 ];
4607 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4608 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4609 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4612 if ( baseType == SMDSEntity_Polygon )
4614 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4615 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4617 else if ( baseType == SMDSEntity_Quad_Polygon )
4619 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4620 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4623 // make new elements
4624 for (int iStep = 0; iStep < nbSteps; iStep++ )
4627 for ( iNode = 0; iNode < nbNodes; iNode++ )
4629 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4630 nextNod[ iNode ] = *itNN[ iNode ]++;
4633 SMDS_MeshElement* aNewElem = 0;
4634 /*if(!elem->IsPoly())*/ {
4635 switch ( baseType ) {
4637 case SMDSEntity_Node: { // sweep NODE
4638 if ( nbSame == 0 ) {
4639 if ( isSingleNode[0] )
4640 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4642 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4648 case SMDSEntity_Edge: { // sweep EDGE
4649 if ( nbDouble == 0 )
4651 if ( nbSame == 0 ) // ---> quadrangle
4652 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4653 nextNod[ 1 ], nextNod[ 0 ] );
4654 else // ---> triangle
4655 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4656 nextNod[ iNotSameNode ] );
4658 else // ---> polygon
4660 vector<const SMDS_MeshNode*> poly_nodes;
4661 poly_nodes.push_back( prevNod[0] );
4662 poly_nodes.push_back( prevNod[1] );
4663 if ( prevNod[1] != nextNod[1] )
4665 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4666 poly_nodes.push_back( nextNod[1] );
4668 if ( prevNod[0] != nextNod[0] )
4670 poly_nodes.push_back( nextNod[0] );
4671 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4673 switch ( poly_nodes.size() ) {
4675 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4678 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4679 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4682 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4687 case SMDSEntity_Triangle: // TRIANGLE --->
4689 if ( nbDouble > 0 ) break;
4690 if ( nbSame == 0 ) // ---> pentahedron
4691 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4692 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4694 else if ( nbSame == 1 ) // ---> pyramid
4695 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4696 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4697 nextNod[ iSameNode ]);
4699 else // 2 same nodes: ---> tetrahedron
4700 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4701 nextNod[ iNotSameNode ]);
4704 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4708 if ( nbDouble+nbSame == 2 )
4710 if(nbSame==0) { // ---> quadratic quadrangle
4711 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4712 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4714 else { //(nbSame==1) // ---> quadratic triangle
4716 return; // medium node on axis
4718 else if(sames[0]==0)
4719 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4720 prevNod[2], midlNod[1], nextNod[2] );
4722 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4723 prevNod[2], nextNod[2], midlNod[0]);
4726 else if ( nbDouble == 3 )
4728 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4729 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4730 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4737 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4738 if ( nbDouble > 0 ) break;
4740 if ( nbSame == 0 ) // ---> hexahedron
4741 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4742 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4744 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4745 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4746 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4747 nextNod[ iSameNode ]);
4748 newElems.push_back( aNewElem );
4749 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4750 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4751 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4753 else if ( nbSame == 2 ) { // ---> pentahedron
4754 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4755 // iBeforeSame is same too
4756 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4757 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4758 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4760 // iAfterSame is same too
4761 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4762 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4763 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4767 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4768 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4769 if ( nbDouble+nbSame != 3 ) break;
4771 // ---> pentahedron with 15 nodes
4772 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4773 nextNod[0], nextNod[1], nextNod[2],
4774 prevNod[3], prevNod[4], prevNod[5],
4775 nextNod[3], nextNod[4], nextNod[5],
4776 midlNod[0], midlNod[1], midlNod[2]);
4778 else if(nbSame==1) {
4779 // ---> 2d order pyramid of 13 nodes
4780 int apex = iSameNode;
4781 int i0 = ( apex + 1 ) % nbCorners;
4782 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4786 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4787 nextNod[i0], nextNod[i1], prevNod[apex],
4788 prevNod[i01], midlNod[i0],
4789 nextNod[i01], midlNod[i1],
4790 prevNod[i1a], prevNod[i0a],
4791 nextNod[i0a], nextNod[i1a]);
4793 else if(nbSame==2) {
4794 // ---> 2d order tetrahedron of 10 nodes
4795 int n1 = iNotSameNode;
4796 int n2 = ( n1 + 1 ) % nbCorners;
4797 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4801 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4802 prevNod[n12], prevNod[n23], prevNod[n31],
4803 midlNod[n1], nextNod[n12], nextNod[n31]);
4807 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4809 if ( nbDouble != 4 ) break;
4810 // ---> hexahedron with 20 nodes
4811 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4812 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4813 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4814 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4815 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4817 else if(nbSame==1) {
4818 // ---> pyramid + pentahedron - can not be created since it is needed
4819 // additional middle node at the center of face
4820 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4823 else if( nbSame == 2 ) {
4824 if ( nbDouble != 2 ) break;
4825 // ---> 2d order Pentahedron with 15 nodes
4827 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4828 // iBeforeSame is same too
4835 // iAfterSame is same too
4845 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4846 prevNod[n4], prevNod[n5], nextNod[n5],
4847 prevNod[n12], midlNod[n2], nextNod[n12],
4848 prevNod[n45], midlNod[n5], nextNod[n45],
4849 prevNod[n14], prevNod[n25], nextNod[n25]);
4853 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4855 if( nbSame == 0 && nbDouble == 9 ) {
4856 // ---> tri-quadratic hexahedron with 27 nodes
4857 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4858 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4859 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4860 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4861 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4862 prevNod[8], // bottom center
4863 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4864 nextNod[8], // top center
4865 midlNod[8]);// elem center
4873 case SMDSEntity_Polygon: { // sweep POLYGON
4875 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4876 // ---> hexagonal prism
4877 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4878 prevNod[3], prevNod[4], prevNod[5],
4879 nextNod[0], nextNod[1], nextNod[2],
4880 nextNod[3], nextNod[4], nextNod[5]);
4884 case SMDSEntity_Ball:
4889 } // switch ( baseType )
4892 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4894 if ( baseType != SMDSEntity_Polygon )
4896 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4897 SMDS_MeshCell::applyInterlace( ind, prevNod );
4898 SMDS_MeshCell::applyInterlace( ind, nextNod );
4899 SMDS_MeshCell::applyInterlace( ind, midlNod );
4900 SMDS_MeshCell::applyInterlace( ind, itNN );
4901 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4902 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4904 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4905 vector<int> quantities (nbNodes + 2);
4906 polyedre_nodes.clear();
4910 for (int inode = 0; inode < nbNodes; inode++)
4911 polyedre_nodes.push_back( prevNod[inode] );
4912 quantities.push_back( nbNodes );
4915 polyedre_nodes.push_back( nextNod[0] );
4916 for (int inode = nbNodes; inode-1; --inode )
4917 polyedre_nodes.push_back( nextNod[inode-1] );
4918 quantities.push_back( nbNodes );
4926 const int iQuad = elem->IsQuadratic();
4927 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4929 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4930 int inextface = (iface+1+iQuad) % nbNodes;
4931 int imid = (iface+1) % nbNodes;
4932 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4933 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4934 polyedre_nodes.push_back( prevNod[iface] ); // 1
4935 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4937 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4938 polyedre_nodes.push_back( nextNod[iface] ); // 2
4940 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4941 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4943 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4944 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4946 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4947 if ( nbFaceNodes > 2 )
4948 quantities.push_back( nbFaceNodes );
4949 else // degenerated face
4950 polyedre_nodes.resize( prevNbNodes );
4952 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4954 } // try to create a polyherdal prism
4957 newElems.push_back( aNewElem );
4958 myLastCreatedElems.Append(aNewElem);
4959 srcElements.Append( elem );
4962 // set new prev nodes
4963 for ( iNode = 0; iNode < nbNodes; iNode++ )
4964 prevNod[ iNode ] = nextNod[ iNode ];
4969 //=======================================================================
4971 * \brief Create 1D and 2D elements around swept elements
4972 * \param mapNewNodes - source nodes and ones generated from them
4973 * \param newElemsMap - source elements and ones generated from them
4974 * \param elemNewNodesMap - nodes generated from each node of each element
4975 * \param elemSet - all swept elements
4976 * \param nbSteps - number of sweeping steps
4977 * \param srcElements - to append elem for each generated element
4979 //=======================================================================
4981 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4982 TTElemOfElemListMap & newElemsMap,
4983 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4984 TIDSortedElemSet& elemSet,
4986 SMESH_SequenceOfElemPtr& srcElements)
4988 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4989 SMESHDS_Mesh* aMesh = GetMeshDS();
4991 // Find nodes belonging to only one initial element - sweep them into edges.
4993 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4994 for ( ; nList != mapNewNodes.end(); nList++ )
4996 const SMDS_MeshNode* node =
4997 static_cast<const SMDS_MeshNode*>( nList->first );
4998 if ( newElemsMap.count( node ))
4999 continue; // node was extruded into edge
5000 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
5001 int nbInitElems = 0;
5002 const SMDS_MeshElement* el = 0;
5003 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
5004 while ( eIt->more() && nbInitElems < 2 ) {
5005 const SMDS_MeshElement* e = eIt->next();
5006 SMDSAbs_ElementType type = e->GetType();
5007 if ( type == SMDSAbs_Volume || type < highType ) continue;
5008 if ( type > highType ) {
5013 nbInitElems += elemSet.count(el);
5015 if ( nbInitElems < 2 ) {
5016 bool NotCreateEdge = el && el->IsMediumNode(node);
5017 if(!NotCreateEdge) {
5018 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5019 list<const SMDS_MeshElement*> newEdges;
5020 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5025 // Make a ceiling for each element ie an equal element of last new nodes.
5026 // Find free links of faces - make edges and sweep them into faces.
5028 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5030 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
5031 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5032 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5034 const SMDS_MeshElement* elem = itElem->first;
5035 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5037 if(itElem->second.size()==0) continue;
5039 const bool isQuadratic = elem->IsQuadratic();
5041 if ( elem->GetType() == SMDSAbs_Edge ) {
5042 // create a ceiling edge
5043 if ( !isQuadratic ) {
5044 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5045 vecNewNodes[ 1 ]->second.back())) {
5046 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5047 vecNewNodes[ 1 ]->second.back()));
5048 srcElements.Append( elem );
5052 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5053 vecNewNodes[ 1 ]->second.back(),
5054 vecNewNodes[ 2 ]->second.back())) {
5055 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5056 vecNewNodes[ 1 ]->second.back(),
5057 vecNewNodes[ 2 ]->second.back()));
5058 srcElements.Append( elem );
5062 if ( elem->GetType() != SMDSAbs_Face )
5065 bool hasFreeLinks = false;
5067 TIDSortedElemSet avoidSet;
5068 avoidSet.insert( elem );
5070 set<const SMDS_MeshNode*> aFaceLastNodes;
5071 int iNode, nbNodes = vecNewNodes.size();
5072 if ( !isQuadratic ) {
5073 // loop on the face nodes
5074 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5075 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5076 // look for free links of the face
5077 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5078 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5079 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5080 // check if a link n1-n2 is free
5081 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5082 hasFreeLinks = true;
5083 // make a new edge and a ceiling for a new edge
5084 const SMDS_MeshElement* edge;
5085 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5086 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5087 srcElements.Append( myLastCreatedElems.Last() );
5089 n1 = vecNewNodes[ iNode ]->second.back();
5090 n2 = vecNewNodes[ iNext ]->second.back();
5091 if ( !aMesh->FindEdge( n1, n2 )) {
5092 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5093 srcElements.Append( edge );
5098 else { // elem is quadratic face
5099 int nbn = nbNodes/2;
5100 for ( iNode = 0; iNode < nbn; iNode++ ) {
5101 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5102 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5103 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5104 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5105 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5106 // check if a link is free
5107 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5108 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5109 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5110 hasFreeLinks = true;
5111 // make an edge and a ceiling for a new edge
5113 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5114 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5115 srcElements.Append( elem );
5117 n1 = vecNewNodes[ iNode ]->second.back();
5118 n2 = vecNewNodes[ iNext ]->second.back();
5119 n3 = vecNewNodes[ iNode+nbn ]->second.back();
5120 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5121 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5122 srcElements.Append( elem );
5126 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5127 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5131 // sweep free links into faces
5133 if ( hasFreeLinks ) {
5134 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5135 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5137 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5138 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5139 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5140 initNodeSet.insert( vecNewNodes[ iNode ]->first );
5141 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5143 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
5144 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5145 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5147 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5148 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5149 std::advance( v, volNb );
5150 // find indices of free faces of a volume and their source edges
5151 list< int > freeInd;
5152 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5153 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5154 int iF, nbF = vTool.NbFaces();
5155 for ( iF = 0; iF < nbF; iF ++ ) {
5156 if (vTool.IsFreeFace( iF ) &&
5157 vTool.GetFaceNodes( iF, faceNodeSet ) &&
5158 initNodeSet != faceNodeSet) // except an initial face
5160 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5162 if ( faceNodeSet == initNodeSetNoCenter )
5164 freeInd.push_back( iF );
5165 // find source edge of a free face iF
5166 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5167 vector<const SMDS_MeshNode*>::iterator lastCommom;
5168 commonNodes.resize( nbNodes, 0 );
5169 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5170 initNodeSet.begin(), initNodeSet.end(),
5171 commonNodes.begin());
5172 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5173 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5175 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5177 if ( !srcEdges.back() )
5179 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5180 << iF << " of volume #" << vTool.ID() << endl;
5185 if ( freeInd.empty() )
5188 // create wall faces for all steps;
5189 // if such a face has been already created by sweep of edge,
5190 // assure that its orientation is OK
5191 for ( int iStep = 0; iStep < nbSteps; iStep++ )
5193 vTool.Set( *v, /*ignoreCentralNodes=*/false );
5194 vTool.SetExternalNormal();
5195 const int nextShift = vTool.IsForward() ? +1 : -1;
5196 list< int >::iterator ind = freeInd.begin();
5197 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5198 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5200 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5201 int nbn = vTool.NbFaceNodes( *ind );
5202 const SMDS_MeshElement * f = 0;
5203 if ( nbn == 3 ) ///// triangle
5205 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5207 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5209 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5211 nodes[ 1 + nextShift ] };
5213 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5215 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5219 else if ( nbn == 4 ) ///// quadrangle
5221 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5223 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5225 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5226 nodes[ 2 ], nodes[ 2+nextShift ] };
5228 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5230 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5231 newOrder[ 2 ], newOrder[ 3 ]));
5234 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5236 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5238 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5240 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5242 nodes[2 + 2*nextShift],
5243 nodes[3 - 2*nextShift],
5245 nodes[3 + 2*nextShift]};
5247 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5249 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5257 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5259 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5260 nodes[1], nodes[3], nodes[5], nodes[7] );
5262 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5264 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5265 nodes[4 - 2*nextShift],
5267 nodes[4 + 2*nextShift],
5269 nodes[5 - 2*nextShift],
5271 nodes[5 + 2*nextShift] };
5273 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5275 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5276 newOrder[ 2 ], newOrder[ 3 ],
5277 newOrder[ 4 ], newOrder[ 5 ],
5278 newOrder[ 6 ], newOrder[ 7 ]));
5281 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5283 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5284 SMDSAbs_Face, /*noMedium=*/false);
5286 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5288 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5289 nodes[4 - 2*nextShift],
5291 nodes[4 + 2*nextShift],
5293 nodes[5 - 2*nextShift],
5295 nodes[5 + 2*nextShift],
5298 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5300 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5301 newOrder[ 2 ], newOrder[ 3 ],
5302 newOrder[ 4 ], newOrder[ 5 ],
5303 newOrder[ 6 ], newOrder[ 7 ],
5307 else //////// polygon
5309 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5310 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5312 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5314 if ( !vTool.IsForward() )
5315 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5317 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5319 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5323 while ( srcElements.Length() < myLastCreatedElems.Length() )
5324 srcElements.Append( *srcEdge );
5326 } // loop on free faces
5328 // go to the next volume
5330 while ( iVol++ < nbVolumesByStep ) v++;
5333 } // loop on volumes of one step
5334 } // sweep free links into faces
5336 // Make a ceiling face with a normal external to a volume
5338 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5339 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5340 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5342 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5343 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5344 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5348 lastVol.SetExternalNormal();
5349 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5350 const int nbn = lastVol.NbFaceNodes( iF );
5351 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5352 if ( !hasFreeLinks ||
5353 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5355 const vector<int>& interlace =
5356 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5357 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5359 if ( const SMDS_MeshElement* face = AddElement( nodeVec, anyFace.Init( elem )))
5360 myLastCreatedElems.Append( face );
5362 while ( srcElements.Length() < myLastCreatedElems.Length() )
5363 srcElements.Append( elem );
5366 } // loop on swept elements
5369 //=======================================================================
5370 //function : RotationSweep
5372 //=======================================================================
5374 SMESH_MeshEditor::PGroupIDs
5375 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5376 const gp_Ax1& theAxis,
5377 const double theAngle,
5378 const int theNbSteps,
5379 const double theTol,
5380 const bool theMakeGroups,
5381 const bool theMakeWalls)
5383 myLastCreatedElems.Clear();
5384 myLastCreatedNodes.Clear();
5386 // source elements for each generated one
5387 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5389 MESSAGE( "RotationSweep()");
5391 aTrsf.SetRotation( theAxis, theAngle );
5393 aTrsf2.SetRotation( theAxis, theAngle/2. );
5395 gp_Lin aLine( theAxis );
5396 double aSqTol = theTol * theTol;
5398 SMESHDS_Mesh* aMesh = GetMeshDS();
5400 TNodeOfNodeListMap mapNewNodes;
5401 TElemOfVecOfNnlmiMap mapElemNewNodes;
5402 TTElemOfElemListMap newElemsMap;
5404 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5405 myMesh->NbFaces(ORDER_QUADRATIC) +
5406 myMesh->NbVolumes(ORDER_QUADRATIC) );
5407 // loop on theElemSets
5408 setElemsFirst( theElemSets );
5409 TIDSortedElemSet::iterator itElem;
5410 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5412 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5413 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5414 const SMDS_MeshElement* elem = *itElem;
5415 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5417 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5418 newNodesItVec.reserve( elem->NbNodes() );
5420 // loop on elem nodes
5421 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5422 while ( itN->more() )
5424 const SMDS_MeshNode* node = cast2Node( itN->next() );
5426 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5428 aXYZ.Coord( coord[0], coord[1], coord[2] );
5429 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5431 // check if a node has been already sweeped
5432 TNodeOfNodeListMapItr nIt =
5433 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5434 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5435 if ( listNewNodes.empty() )
5437 // check if we are to create medium nodes between corner ones
5438 bool needMediumNodes = false;
5439 if ( isQuadraticMesh )
5441 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5442 while (it->more() && !needMediumNodes )
5444 const SMDS_MeshElement* invElem = it->next();
5445 if ( invElem != elem && !theElems.count( invElem )) continue;
5446 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5447 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5448 needMediumNodes = true;
5453 const SMDS_MeshNode * newNode = node;
5454 for ( int i = 0; i < theNbSteps; i++ ) {
5456 if ( needMediumNodes ) // create a medium node
5458 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5459 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5460 myLastCreatedNodes.Append(newNode);
5461 srcNodes.Append( node );
5462 listNewNodes.push_back( newNode );
5463 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5466 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5468 // create a corner node
5469 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5470 myLastCreatedNodes.Append(newNode);
5471 srcNodes.Append( node );
5472 listNewNodes.push_back( newNode );
5475 listNewNodes.push_back( newNode );
5476 // if ( needMediumNodes )
5477 // listNewNodes.push_back( newNode );
5481 newNodesItVec.push_back( nIt );
5483 // make new elements
5484 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5489 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5491 PGroupIDs newGroupIDs;
5492 if ( theMakeGroups )
5493 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5498 //=======================================================================
5499 //function : ExtrusParam
5500 //purpose : standard construction
5501 //=======================================================================
5503 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5504 const int theNbSteps,
5506 const double theTolerance):
5508 myFlags( theFlags ),
5509 myTolerance( theTolerance ),
5510 myElemsToUse( NULL )
5512 mySteps = new TColStd_HSequenceOfReal;
5513 const double stepSize = theStep.Magnitude();
5514 for (int i=1; i<=theNbSteps; i++ )
5515 mySteps->Append( stepSize );
5517 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5518 ( theTolerance > 0 ))
5520 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5524 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5528 //=======================================================================
5529 //function : ExtrusParam
5530 //purpose : steps are given explicitly
5531 //=======================================================================
5533 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5534 Handle(TColStd_HSequenceOfReal) theSteps,
5536 const double theTolerance):
5538 mySteps( theSteps ),
5539 myFlags( theFlags ),
5540 myTolerance( theTolerance ),
5541 myElemsToUse( NULL )
5543 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5544 ( theTolerance > 0 ))
5546 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5550 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5554 //=======================================================================
5555 //function : ExtrusParam
5556 //purpose : for extrusion by normal
5557 //=======================================================================
5559 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5560 const int theNbSteps,
5564 mySteps( new TColStd_HSequenceOfReal ),
5565 myFlags( theFlags ),
5567 myElemsToUse( NULL )
5569 for (int i = 0; i < theNbSteps; i++ )
5570 mySteps->Append( theStepSize );
5574 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5578 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5582 //=======================================================================
5583 //function : ExtrusParam::SetElementsToUse
5584 //purpose : stores elements to use for extrusion by normal, depending on
5585 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5586 //=======================================================================
5588 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5590 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5593 //=======================================================================
5594 //function : ExtrusParam::beginStepIter
5595 //purpose : prepare iteration on steps
5596 //=======================================================================
5598 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5600 myWithMediumNodes = withMediumNodes;
5604 //=======================================================================
5605 //function : ExtrusParam::moreSteps
5606 //purpose : are there more steps?
5607 //=======================================================================
5609 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5611 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5613 //=======================================================================
5614 //function : ExtrusParam::nextStep
5615 //purpose : returns the next step
5616 //=======================================================================
5618 double SMESH_MeshEditor::ExtrusParam::nextStep()
5621 if ( !myCurSteps.empty() )
5623 res = myCurSteps.back();
5624 myCurSteps.pop_back();
5626 else if ( myNextStep <= mySteps->Length() )
5628 myCurSteps.push_back( mySteps->Value( myNextStep ));
5630 if ( myWithMediumNodes )
5632 myCurSteps.back() /= 2.;
5633 myCurSteps.push_back( myCurSteps.back() );
5640 //=======================================================================
5641 //function : ExtrusParam::makeNodesByDir
5642 //purpose : create nodes for standard extrusion
5643 //=======================================================================
5645 int SMESH_MeshEditor::ExtrusParam::
5646 makeNodesByDir( SMESHDS_Mesh* mesh,
5647 const SMDS_MeshNode* srcNode,
5648 std::list<const SMDS_MeshNode*> & newNodes,
5649 const bool makeMediumNodes)
5651 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5654 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5656 p += myDir.XYZ() * nextStep();
5657 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5658 newNodes.push_back( newNode );
5663 //=======================================================================
5664 //function : ExtrusParam::makeNodesByDirAndSew
5665 //purpose : create nodes for standard extrusion with sewing
5666 //=======================================================================
5668 int SMESH_MeshEditor::ExtrusParam::
5669 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5670 const SMDS_MeshNode* srcNode,
5671 std::list<const SMDS_MeshNode*> & newNodes,
5672 const bool makeMediumNodes)
5674 gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5677 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5679 P1 += myDir.XYZ() * nextStep();
5681 // try to search in sequence of existing nodes
5682 // if myNodes.Length()>0 we 'nave to use given sequence
5683 // else - use all nodes of mesh
5684 const SMDS_MeshNode * node = 0;
5685 if ( myNodes.Length() > 0 ) {
5687 for(i=1; i<=myNodes.Length(); i++) {
5688 gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5689 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5691 node = myNodes.Value(i);
5697 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5698 while(itn->more()) {
5699 SMESH_TNodeXYZ P2( itn->next() );
5700 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5709 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5711 newNodes.push_back( node );
5718 //=======================================================================
5719 //function : ExtrusParam::makeNodesByNormal2D
5720 //purpose : create nodes for extrusion using normals of faces
5721 //=======================================================================
5723 int SMESH_MeshEditor::ExtrusParam::
5724 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5725 const SMDS_MeshNode* srcNode,
5726 std::list<const SMDS_MeshNode*> & newNodes,
5727 const bool makeMediumNodes)
5729 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5731 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5733 // get normals to faces sharing srcNode
5734 vector< gp_XYZ > norms, baryCenters;
5735 gp_XYZ norm, avgNorm( 0,0,0 );
5736 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5737 while ( faceIt->more() )
5739 const SMDS_MeshElement* face = faceIt->next();
5740 if ( myElemsToUse && !myElemsToUse->count( face ))
5742 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5744 norms.push_back( norm );
5746 if ( !alongAvgNorm )
5750 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5751 bc += SMESH_TNodeXYZ( nIt->next() );
5752 baryCenters.push_back( bc / nbN );
5757 if ( norms.empty() ) return 0;
5759 double normSize = avgNorm.Modulus();
5760 if ( normSize < std::numeric_limits<double>::min() )
5763 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5766 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5769 avgNorm /= normSize;
5772 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5775 double stepSize = nextStep();
5777 if ( norms.size() > 1 )
5779 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5781 // translate plane of a face
5782 baryCenters[ iF ] += norms[ iF ] * stepSize;
5784 // find point of intersection of the face plane located at baryCenters[ iF ]
5785 // and avgNorm located at pNew
5786 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5787 double dot = ( norms[ iF ] * avgNorm );
5788 if ( dot < std::numeric_limits<double>::min() )
5789 dot = stepSize * 1e-3;
5790 double step = -( norms[ iF ] * pNew + d ) / dot;
5791 pNew += step * avgNorm;
5796 pNew += stepSize * avgNorm;
5800 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5801 newNodes.push_back( newNode );
5806 //=======================================================================
5807 //function : ExtrusParam::makeNodesByNormal1D
5808 //purpose : create nodes for extrusion using normals of edges
5809 //=======================================================================
5811 int SMESH_MeshEditor::ExtrusParam::
5812 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5813 const SMDS_MeshNode* srcNode,
5814 std::list<const SMDS_MeshNode*> & newNodes,
5815 const bool makeMediumNodes)
5817 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5821 //=======================================================================
5822 //function : ExtrusionSweep
5824 //=======================================================================
5826 SMESH_MeshEditor::PGroupIDs
5827 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5828 const gp_Vec& theStep,
5829 const int theNbSteps,
5830 TTElemOfElemListMap& newElemsMap,
5832 const double theTolerance)
5834 ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5835 return ExtrusionSweep( theElems, aParams, newElemsMap );
5839 //=======================================================================
5840 //function : ExtrusionSweep
5842 //=======================================================================
5844 SMESH_MeshEditor::PGroupIDs
5845 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5846 ExtrusParam& theParams,
5847 TTElemOfElemListMap& newElemsMap)
5849 myLastCreatedElems.Clear();
5850 myLastCreatedNodes.Clear();
5852 // source elements for each generated one
5853 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5855 SMESHDS_Mesh* aMesh = GetMeshDS();
5857 setElemsFirst( theElemSets );
5858 const int nbSteps = theParams.NbSteps();
5859 theParams.SetElementsToUse( theElemSets[0] );
5861 TNodeOfNodeListMap mapNewNodes;
5862 //TNodeOfNodeVecMap mapNewNodes;
5863 TElemOfVecOfNnlmiMap mapElemNewNodes;
5864 //TElemOfVecOfMapNodesMap mapElemNewNodes;
5866 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5867 myMesh->NbFaces(ORDER_QUADRATIC) +
5868 myMesh->NbVolumes(ORDER_QUADRATIC) );
5870 TIDSortedElemSet::iterator itElem;
5871 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5873 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5874 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5876 // check element type
5877 const SMDS_MeshElement* elem = *itElem;
5878 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5881 const size_t nbNodes = elem->NbNodes();
5882 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5883 newNodesItVec.reserve( nbNodes );
5885 // loop on elem nodes
5886 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5887 while ( itN->more() )
5889 // check if a node has been already sweeped
5890 const SMDS_MeshNode* node = cast2Node( itN->next() );
5891 TNodeOfNodeListMap::iterator nIt =
5892 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5893 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5894 if ( listNewNodes.empty() )
5898 // check if we are to create medium nodes between corner ones
5899 bool needMediumNodes = false;
5900 if ( isQuadraticMesh )
5902 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5903 while (it->more() && !needMediumNodes )
5905 const SMDS_MeshElement* invElem = it->next();
5906 if ( invElem != elem && !theElems.count( invElem )) continue;
5907 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5908 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5909 needMediumNodes = true;
5912 // create nodes for all steps
5913 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5915 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5916 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5918 myLastCreatedNodes.Append( *newNodesIt );
5919 srcNodes.Append( node );
5924 break; // newNodesItVec will be shorter than nbNodes
5927 newNodesItVec.push_back( nIt );
5929 // make new elements
5930 if ( newNodesItVec.size() == nbNodes )
5931 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5935 if ( theParams.ToMakeBoundary() ) {
5936 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5938 PGroupIDs newGroupIDs;
5939 if ( theParams.ToMakeGroups() )
5940 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5945 //=======================================================================
5946 //function : ExtrusionAlongTrack
5948 //=======================================================================
5949 SMESH_MeshEditor::Extrusion_Error
5950 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5951 SMESH_subMesh* theTrack,
5952 const SMDS_MeshNode* theN1,
5953 const bool theHasAngles,
5954 list<double>& theAngles,
5955 const bool theLinearVariation,
5956 const bool theHasRefPoint,
5957 const gp_Pnt& theRefPoint,
5958 const bool theMakeGroups)
5960 MESSAGE("ExtrusionAlongTrack");
5961 myLastCreatedElems.Clear();
5962 myLastCreatedNodes.Clear();
5965 std::list<double> aPrms;
5966 TIDSortedElemSet::iterator itElem;
5969 TopoDS_Edge aTrackEdge;
5970 TopoDS_Vertex aV1, aV2;
5972 SMDS_ElemIteratorPtr aItE;
5973 SMDS_NodeIteratorPtr aItN;
5974 SMDSAbs_ElementType aTypeE;
5976 TNodeOfNodeListMap mapNewNodes;
5979 aNbE = theElements[0].size() + theElements[1].size();
5982 return EXTR_NO_ELEMENTS;
5984 // 1.1 Track Pattern
5987 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5989 aItE = pSubMeshDS->GetElements();
5990 while ( aItE->more() ) {
5991 const SMDS_MeshElement* pE = aItE->next();
5992 aTypeE = pE->GetType();
5993 // Pattern must contain links only
5994 if ( aTypeE != SMDSAbs_Edge )
5995 return EXTR_PATH_NOT_EDGE;
5998 list<SMESH_MeshEditor_PathPoint> fullList;
6000 const TopoDS_Shape& aS = theTrack->GetSubShape();
6001 // Sub-shape for the Pattern must be an Edge or Wire
6002 if( aS.ShapeType() == TopAbs_EDGE ) {
6003 aTrackEdge = TopoDS::Edge( aS );
6004 // the Edge must not be degenerated
6005 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6006 return EXTR_BAD_PATH_SHAPE;
6007 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6008 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
6009 const SMDS_MeshNode* aN1 = aItN->next();
6010 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
6011 const SMDS_MeshNode* aN2 = aItN->next();
6012 // starting node must be aN1 or aN2
6013 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6014 return EXTR_BAD_STARTING_NODE;
6015 aItN = pSubMeshDS->GetNodes();
6016 while ( aItN->more() ) {
6017 const SMDS_MeshNode* pNode = aItN->next();
6018 const SMDS_EdgePosition* pEPos =
6019 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6020 double aT = pEPos->GetUParameter();
6021 aPrms.push_back( aT );
6023 //Extrusion_Error err =
6024 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6025 } else if( aS.ShapeType() == TopAbs_WIRE ) {
6026 list< SMESH_subMesh* > LSM;
6027 TopTools_SequenceOfShape Edges;
6028 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6029 while(itSM->more()) {
6030 SMESH_subMesh* SM = itSM->next();
6032 const TopoDS_Shape& aS = SM->GetSubShape();
6035 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6036 int startNid = theN1->GetID();
6037 TColStd_MapOfInteger UsedNums;
6039 int NbEdges = Edges.Length();
6041 for(; i<=NbEdges; i++) {
6043 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6044 for(; itLSM!=LSM.end(); itLSM++) {
6046 if(UsedNums.Contains(k)) continue;
6047 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6048 SMESH_subMesh* locTrack = *itLSM;
6049 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6050 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6051 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6052 const SMDS_MeshNode* aN1 = aItN->next();
6053 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6054 const SMDS_MeshNode* aN2 = aItN->next();
6055 // starting node must be aN1 or aN2
6056 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6057 // 2. Collect parameters on the track edge
6059 aItN = locMeshDS->GetNodes();
6060 while ( aItN->more() ) {
6061 const SMDS_MeshNode* pNode = aItN->next();
6062 const SMDS_EdgePosition* pEPos =
6063 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6064 double aT = pEPos->GetUParameter();
6065 aPrms.push_back( aT );
6067 list<SMESH_MeshEditor_PathPoint> LPP;
6068 //Extrusion_Error err =
6069 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6070 LLPPs.push_back(LPP);
6072 // update startN for search following egde
6073 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6074 else startNid = aN1->GetID();
6078 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6079 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6080 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6081 for(; itPP!=firstList.end(); itPP++) {
6082 fullList.push_back( *itPP );
6084 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6085 fullList.pop_back();
6087 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6088 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6089 itPP = currList.begin();
6090 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6091 gp_Dir D1 = PP1.Tangent();
6092 gp_Dir D2 = PP2.Tangent();
6093 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6094 (D1.Z()+D2.Z())/2 ) );
6095 PP1.SetTangent(Dnew);
6096 fullList.push_back(PP1);
6098 for(; itPP!=firstList.end(); itPP++) {
6099 fullList.push_back( *itPP );
6101 PP1 = fullList.back();
6102 fullList.pop_back();
6104 // if wire not closed
6105 fullList.push_back(PP1);
6109 return EXTR_BAD_PATH_SHAPE;
6112 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6113 theHasRefPoint, theRefPoint, theMakeGroups);
6117 //=======================================================================
6118 //function : ExtrusionAlongTrack
6120 //=======================================================================
6121 SMESH_MeshEditor::Extrusion_Error
6122 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6123 SMESH_Mesh* theTrack,
6124 const SMDS_MeshNode* theN1,
6125 const bool theHasAngles,
6126 list<double>& theAngles,
6127 const bool theLinearVariation,
6128 const bool theHasRefPoint,
6129 const gp_Pnt& theRefPoint,
6130 const bool theMakeGroups)
6132 myLastCreatedElems.Clear();
6133 myLastCreatedNodes.Clear();
6136 std::list<double> aPrms;
6137 TIDSortedElemSet::iterator itElem;
6140 TopoDS_Edge aTrackEdge;
6141 TopoDS_Vertex aV1, aV2;
6143 SMDS_ElemIteratorPtr aItE;
6144 SMDS_NodeIteratorPtr aItN;
6145 SMDSAbs_ElementType aTypeE;
6147 TNodeOfNodeListMap mapNewNodes;
6150 aNbE = theElements[0].size() + theElements[1].size();
6153 return EXTR_NO_ELEMENTS;
6155 // 1.1 Track Pattern
6158 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6160 aItE = pMeshDS->elementsIterator();
6161 while ( aItE->more() ) {
6162 const SMDS_MeshElement* pE = aItE->next();
6163 aTypeE = pE->GetType();
6164 // Pattern must contain links only
6165 if ( aTypeE != SMDSAbs_Edge )
6166 return EXTR_PATH_NOT_EDGE;
6169 list<SMESH_MeshEditor_PathPoint> fullList;
6171 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6173 if ( !theTrack->HasShapeToMesh() ) {
6174 //Mesh without shape
6175 const SMDS_MeshNode* currentNode = NULL;
6176 const SMDS_MeshNode* prevNode = theN1;
6177 std::vector<const SMDS_MeshNode*> aNodesList;
6178 aNodesList.push_back(theN1);
6179 int nbEdges = 0, conn=0;
6180 const SMDS_MeshElement* prevElem = NULL;
6181 const SMDS_MeshElement* currentElem = NULL;
6182 int totalNbEdges = theTrack->NbEdges();
6183 SMDS_ElemIteratorPtr nIt;
6186 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6187 return EXTR_BAD_STARTING_NODE;
6190 conn = nbEdgeConnectivity(theN1);
6192 return EXTR_PATH_NOT_EDGE;
6194 aItE = theN1->GetInverseElementIterator();
6195 prevElem = aItE->next();
6196 currentElem = prevElem;
6198 if(totalNbEdges == 1 ) {
6199 nIt = currentElem->nodesIterator();
6200 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6201 if(currentNode == prevNode)
6202 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6203 aNodesList.push_back(currentNode);
6205 nIt = currentElem->nodesIterator();
6206 while( nIt->more() ) {
6207 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6208 if(currentNode == prevNode)
6209 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6210 aNodesList.push_back(currentNode);
6212 //case of the closed mesh
6213 if(currentNode == theN1) {
6218 conn = nbEdgeConnectivity(currentNode);
6220 return EXTR_PATH_NOT_EDGE;
6221 }else if( conn == 1 && nbEdges > 0 ) {
6226 prevNode = currentNode;
6227 aItE = currentNode->GetInverseElementIterator();
6228 currentElem = aItE->next();
6229 if( currentElem == prevElem)
6230 currentElem = aItE->next();
6231 nIt = currentElem->nodesIterator();
6232 prevElem = currentElem;
6238 if(nbEdges != totalNbEdges)
6239 return EXTR_PATH_NOT_EDGE;
6241 TopTools_SequenceOfShape Edges;
6242 double x1,x2,y1,y2,z1,z2;
6243 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6244 int startNid = theN1->GetID();
6245 for(int i = 1; i < aNodesList.size(); i++) {
6246 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
6247 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
6248 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
6249 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
6250 list<SMESH_MeshEditor_PathPoint> LPP;
6252 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6253 LLPPs.push_back(LPP);
6254 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
6255 else startNid = aNodesList[i-1]->GetID();
6259 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6260 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6261 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6262 for(; itPP!=firstList.end(); itPP++) {
6263 fullList.push_back( *itPP );
6266 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6267 SMESH_MeshEditor_PathPoint PP2;
6268 fullList.pop_back();
6270 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6271 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6272 itPP = currList.begin();
6273 PP2 = currList.front();
6274 gp_Dir D1 = PP1.Tangent();
6275 gp_Dir D2 = PP2.Tangent();
6276 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6277 (D1.Z()+D2.Z())/2 ) );
6278 PP1.SetTangent(Dnew);
6279 fullList.push_back(PP1);
6281 for(; itPP!=currList.end(); itPP++) {
6282 fullList.push_back( *itPP );
6284 PP1 = fullList.back();
6285 fullList.pop_back();
6287 fullList.push_back(PP1);
6289 } // Sub-shape for the Pattern must be an Edge or Wire
6290 else if( aS.ShapeType() == TopAbs_EDGE ) {
6291 aTrackEdge = TopoDS::Edge( aS );
6292 // the Edge must not be degenerated
6293 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6294 return EXTR_BAD_PATH_SHAPE;
6295 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6296 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6297 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6298 // starting node must be aN1 or aN2
6299 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6300 return EXTR_BAD_STARTING_NODE;
6301 aItN = pMeshDS->nodesIterator();
6302 while ( aItN->more() ) {
6303 const SMDS_MeshNode* pNode = aItN->next();
6304 if( pNode==aN1 || pNode==aN2 ) continue;
6305 const SMDS_EdgePosition* pEPos =
6306 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6307 double aT = pEPos->GetUParameter();
6308 aPrms.push_back( aT );
6310 //Extrusion_Error err =
6311 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6313 else if( aS.ShapeType() == TopAbs_WIRE ) {
6314 list< SMESH_subMesh* > LSM;
6315 TopTools_SequenceOfShape Edges;
6316 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6317 for(; eExp.More(); eExp.Next()) {
6318 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6319 if( SMESH_Algo::isDegenerated(E) ) continue;
6320 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6326 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6327 TopoDS_Vertex aVprev;
6328 TColStd_MapOfInteger UsedNums;
6329 int NbEdges = Edges.Length();
6331 for(; i<=NbEdges; i++) {
6333 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6334 for(; itLSM!=LSM.end(); itLSM++) {
6336 if(UsedNums.Contains(k)) continue;
6337 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6338 SMESH_subMesh* locTrack = *itLSM;
6339 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6340 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6341 bool aN1isOK = false, aN2isOK = false;
6342 if ( aVprev.IsNull() ) {
6343 // if previous vertex is not yet defined, it means that we in the beginning of wire
6344 // and we have to find initial vertex corresponding to starting node theN1
6345 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6346 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6347 // starting node must be aN1 or aN2
6348 aN1isOK = ( aN1 && aN1 == theN1 );
6349 aN2isOK = ( aN2 && aN2 == theN1 );
6352 // we have specified ending vertex of the previous edge on the previous iteration
6353 // and we have just to check that it corresponds to any vertex in current segment
6354 aN1isOK = aVprev.IsSame( aV1 );
6355 aN2isOK = aVprev.IsSame( aV2 );
6357 if ( !aN1isOK && !aN2isOK ) continue;
6358 // 2. Collect parameters on the track edge
6360 aItN = locMeshDS->GetNodes();
6361 while ( aItN->more() ) {
6362 const SMDS_MeshNode* pNode = aItN->next();
6363 const SMDS_EdgePosition* pEPos =
6364 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6365 double aT = pEPos->GetUParameter();
6366 aPrms.push_back( aT );
6368 list<SMESH_MeshEditor_PathPoint> LPP;
6369 //Extrusion_Error err =
6370 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6371 LLPPs.push_back(LPP);
6373 // update startN for search following egde
6374 if ( aN1isOK ) aVprev = aV2;
6379 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6380 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6381 fullList.splice( fullList.end(), firstList );
6383 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6384 fullList.pop_back();
6386 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6387 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6388 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6389 gp_Dir D1 = PP1.Tangent();
6390 gp_Dir D2 = PP2.Tangent();
6391 gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
6392 PP1.SetTangent(Dnew);
6393 fullList.push_back(PP1);
6394 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6395 PP1 = fullList.back();
6396 fullList.pop_back();
6398 // if wire not closed
6399 fullList.push_back(PP1);
6403 return EXTR_BAD_PATH_SHAPE;
6406 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6407 theHasRefPoint, theRefPoint, theMakeGroups);
6411 //=======================================================================
6412 //function : MakeEdgePathPoints
6413 //purpose : auxilary for ExtrusionAlongTrack
6414 //=======================================================================
6415 SMESH_MeshEditor::Extrusion_Error
6416 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
6417 const TopoDS_Edge& aTrackEdge,
6419 list<SMESH_MeshEditor_PathPoint>& LPP)
6421 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6423 aTolVec2=aTolVec*aTolVec;
6425 TopoDS_Vertex aV1, aV2;
6426 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6427 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6428 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6429 // 2. Collect parameters on the track edge
6430 aPrms.push_front( aT1 );
6431 aPrms.push_back( aT2 );
6434 if( FirstIsStart ) {
6445 SMESH_MeshEditor_PathPoint aPP;
6446 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6447 std::list<double>::iterator aItD = aPrms.begin();
6448 for(; aItD != aPrms.end(); ++aItD) {
6452 aC3D->D1( aT, aP3D, aVec );
6453 aL2 = aVec.SquareMagnitude();
6454 if ( aL2 < aTolVec2 )
6455 return EXTR_CANT_GET_TANGENT;
6456 gp_Dir aTgt( aVec );
6458 aPP.SetTangent( aTgt );
6459 aPP.SetParameter( aT );
6466 //=======================================================================
6467 //function : MakeExtrElements
6468 //purpose : auxilary for ExtrusionAlongTrack
6469 //=======================================================================
6470 SMESH_MeshEditor::Extrusion_Error
6471 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet theElemSets[2],
6472 list<SMESH_MeshEditor_PathPoint>& fullList,
6473 const bool theHasAngles,
6474 list<double>& theAngles,
6475 const bool theLinearVariation,
6476 const bool theHasRefPoint,
6477 const gp_Pnt& theRefPoint,
6478 const bool theMakeGroups)
6480 const int aNbTP = fullList.size();
6482 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6483 LinearAngleVariation(aNbTP-1, theAngles);
6484 // fill vector of path points with angles
6485 vector<SMESH_MeshEditor_PathPoint> aPPs;
6486 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6487 list<double>::iterator itAngles = theAngles.begin();
6488 aPPs.push_back( *itPP++ );
6489 for( ; itPP != fullList.end(); itPP++) {
6490 aPPs.push_back( *itPP );
6491 if ( theHasAngles && itAngles != theAngles.end() )
6492 aPPs.back().SetAngle( *itAngles++ );
6495 TNodeOfNodeListMap mapNewNodes;
6496 TElemOfVecOfNnlmiMap mapElemNewNodes;
6497 TTElemOfElemListMap newElemsMap;
6498 TIDSortedElemSet::iterator itElem;
6499 // source elements for each generated one
6500 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6502 // 3. Center of rotation aV0
6503 gp_Pnt aV0 = theRefPoint;
6504 if ( !theHasRefPoint )
6506 gp_XYZ aGC( 0.,0.,0. );
6507 TIDSortedElemSet newNodes;
6509 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6511 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6512 itElem = theElements.begin();
6513 for ( ; itElem != theElements.end(); itElem++ ) {
6514 const SMDS_MeshElement* elem = *itElem;
6516 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6517 while ( itN->more() ) {
6518 const SMDS_MeshElement* node = itN->next();
6519 if ( newNodes.insert( node ).second )
6520 aGC += SMESH_TNodeXYZ( node );
6524 aGC /= newNodes.size();
6526 } // if (!theHasRefPoint) {
6528 // 4. Processing the elements
6529 SMESHDS_Mesh* aMesh = GetMeshDS();
6531 setElemsFirst( theElemSets );
6532 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6534 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6535 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
6536 // check element type
6537 const SMDS_MeshElement* elem = *itElem;
6540 // SMDSAbs_ElementType aTypeE = elem->GetType();
6541 // if ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge )
6544 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6545 newNodesItVec.reserve( elem->NbNodes() );
6547 // loop on elem nodes
6549 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6550 while ( itN->more() )
6553 // check if a node has been already processed
6554 const SMDS_MeshNode* node =
6555 static_cast<const SMDS_MeshNode*>( itN->next() );
6556 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
6557 if ( nIt == mapNewNodes.end() ) {
6558 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6559 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6562 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6563 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6564 gp_Ax1 anAx1, anAxT1T0;
6565 gp_Dir aDT1x, aDT0x, aDT1T0;
6570 aPN0 = SMESH_TNodeXYZ( node );
6572 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6574 aDT0x= aPP0.Tangent();
6575 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
6577 for ( int j = 1; j < aNbTP; ++j ) {
6578 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6580 aDT1x = aPP1.Tangent();
6581 aAngle1x = aPP1.Angle();
6583 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6585 gp_Vec aV01x( aP0x, aP1x );
6586 aTrsf.SetTranslation( aV01x );
6589 aV1x = aV0x.Transformed( aTrsf );
6590 aPN1 = aPN0.Transformed( aTrsf );
6592 // rotation 1 [ T1,T0 ]
6593 aAngleT1T0=-aDT1x.Angle( aDT0x );
6594 if (fabs(aAngleT1T0) > aTolAng) {
6596 anAxT1T0.SetLocation( aV1x );
6597 anAxT1T0.SetDirection( aDT1T0 );
6598 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6600 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6604 if ( theHasAngles ) {
6605 anAx1.SetLocation( aV1x );
6606 anAx1.SetDirection( aDT1x );
6607 aTrsfRot.SetRotation( anAx1, aAngle1x );
6609 aPN1 = aPN1.Transformed( aTrsfRot );
6613 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
6614 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6615 // create additional node
6616 double x = ( aPN1.X() + aPN0.X() )/2.;
6617 double y = ( aPN1.Y() + aPN0.Y() )/2.;
6618 double z = ( aPN1.Z() + aPN0.Z() )/2.;
6619 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6620 myLastCreatedNodes.Append(newNode);
6621 srcNodes.Append( node );
6622 listNewNodes.push_back( newNode );
6624 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6625 myLastCreatedNodes.Append(newNode);
6626 srcNodes.Append( node );
6627 listNewNodes.push_back( newNode );
6637 // if current elem is quadratic and current node is not medium
6638 // we have to check - may be it is needed to insert additional nodes
6639 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6640 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6641 if(listNewNodes.size()==aNbTP-1) {
6642 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6643 gp_XYZ P(node->X(), node->Y(), node->Z());
6644 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6646 for(i=0; i<aNbTP-1; i++) {
6647 const SMDS_MeshNode* N = *it;
6648 double x = ( N->X() + P.X() )/2.;
6649 double y = ( N->Y() + P.Y() )/2.;
6650 double z = ( N->Z() + P.Z() )/2.;
6651 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6652 srcNodes.Append( node );
6653 myLastCreatedNodes.Append(newN);
6656 P = gp_XYZ(N->X(),N->Y(),N->Z());
6658 listNewNodes.clear();
6659 for(i=0; i<2*(aNbTP-1); i++) {
6660 listNewNodes.push_back(aNodes[i]);
6666 newNodesItVec.push_back( nIt );
6668 // make new elements
6669 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6670 // newNodesItVec[0]->second.size(), myLastCreatedElems );
6671 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6675 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6677 if ( theMakeGroups )
6678 generateGroups( srcNodes, srcElems, "extruded");
6684 //=======================================================================
6685 //function : LinearAngleVariation
6686 //purpose : auxilary for ExtrusionAlongTrack
6687 //=======================================================================
6688 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6689 list<double>& Angles)
6691 int nbAngles = Angles.size();
6692 if( nbSteps > nbAngles ) {
6693 vector<double> theAngles(nbAngles);
6694 list<double>::iterator it = Angles.begin();
6696 for(; it!=Angles.end(); it++) {
6698 theAngles[i] = (*it);
6701 double rAn2St = double( nbAngles ) / double( nbSteps );
6702 double angPrev = 0, angle;
6703 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6704 double angCur = rAn2St * ( iSt+1 );
6705 double angCurFloor = floor( angCur );
6706 double angPrevFloor = floor( angPrev );
6707 if ( angPrevFloor == angCurFloor )
6708 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6710 int iP = int( angPrevFloor );
6711 double angPrevCeil = ceil(angPrev);
6712 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6714 int iC = int( angCurFloor );
6715 if ( iC < nbAngles )
6716 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6718 iP = int( angPrevCeil );
6720 angle += theAngles[ iC ];
6722 res.push_back(angle);
6727 for(; it!=res.end(); it++)
6728 Angles.push_back( *it );
6733 //================================================================================
6735 * \brief Move or copy theElements applying theTrsf to their nodes
6736 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6737 * \param theTrsf - transformation to apply
6738 * \param theCopy - if true, create translated copies of theElems
6739 * \param theMakeGroups - if true and theCopy, create translated groups
6740 * \param theTargetMesh - mesh to copy translated elements into
6741 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6743 //================================================================================
6745 SMESH_MeshEditor::PGroupIDs
6746 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6747 const gp_Trsf& theTrsf,
6749 const bool theMakeGroups,
6750 SMESH_Mesh* theTargetMesh)
6752 myLastCreatedElems.Clear();
6753 myLastCreatedNodes.Clear();
6755 bool needReverse = false;
6756 string groupPostfix;
6757 switch ( theTrsf.Form() ) {
6759 MESSAGE("gp_PntMirror");
6761 groupPostfix = "mirrored";
6764 MESSAGE("gp_Ax1Mirror");
6765 groupPostfix = "mirrored";
6768 MESSAGE("gp_Ax2Mirror");
6770 groupPostfix = "mirrored";
6773 MESSAGE("gp_Rotation");
6774 groupPostfix = "rotated";
6776 case gp_Translation:
6777 MESSAGE("gp_Translation");
6778 groupPostfix = "translated";
6781 MESSAGE("gp_Scale");
6782 groupPostfix = "scaled";
6784 case gp_CompoundTrsf: // different scale by axis
6785 MESSAGE("gp_CompoundTrsf");
6786 groupPostfix = "scaled";
6790 needReverse = false;
6791 groupPostfix = "transformed";
6794 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6795 SMESHDS_Mesh* aMesh = GetMeshDS();
6797 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6798 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6799 SMESH_MeshEditor::ElemFeatures elemType;
6801 // map old node to new one
6802 TNodeNodeMap nodeMap;
6804 // elements sharing moved nodes; those of them which have all
6805 // nodes mirrored but are not in theElems are to be reversed
6806 TIDSortedElemSet inverseElemSet;
6808 // source elements for each generated one
6809 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6811 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6812 TIDSortedElemSet orphanNode;
6814 if ( theElems.empty() ) // transform the whole mesh
6817 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6818 while ( eIt->more() ) theElems.insert( eIt->next() );
6820 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6821 while ( nIt->more() )
6823 const SMDS_MeshNode* node = nIt->next();
6824 if ( node->NbInverseElements() == 0)
6825 orphanNode.insert( node );
6829 // loop on elements to transform nodes : first orphan nodes then elems
6830 TIDSortedElemSet::iterator itElem;
6831 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6832 for (int i=0; i<2; i++)
6833 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6835 const SMDS_MeshElement* elem = *itElem;
6839 // loop on elem nodes
6841 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6842 while ( itN->more() )
6844 const SMDS_MeshNode* node = cast2Node( itN->next() );
6845 // check if a node has been already transformed
6846 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6847 nodeMap.insert( make_pair ( node, node ));
6848 if ( !n2n_isnew.second )
6851 node->GetXYZ( coord );
6852 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6853 if ( theTargetMesh ) {
6854 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6855 n2n_isnew.first->second = newNode;
6856 myLastCreatedNodes.Append(newNode);
6857 srcNodes.Append( node );
6859 else if ( theCopy ) {
6860 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6861 n2n_isnew.first->second = newNode;
6862 myLastCreatedNodes.Append(newNode);
6863 srcNodes.Append( node );
6866 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6867 // node position on shape becomes invalid
6868 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6869 ( SMDS_SpacePosition::originSpacePosition() );
6872 // keep inverse elements
6873 if ( !theCopy && !theTargetMesh && needReverse ) {
6874 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6875 while ( invElemIt->more() ) {
6876 const SMDS_MeshElement* iel = invElemIt->next();
6877 inverseElemSet.insert( iel );
6881 } // loop on elems in { &orphanNode, &theElems };
6883 // either create new elements or reverse mirrored ones
6884 if ( !theCopy && !needReverse && !theTargetMesh )
6887 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6889 // Replicate or reverse elements
6891 std::vector<int> iForw;
6892 vector<const SMDS_MeshNode*> nodes;
6893 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6895 const SMDS_MeshElement* elem = *itElem;
6896 if ( !elem ) continue;
6898 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6899 int nbNodes = elem->NbNodes();
6900 if ( geomType == SMDSGeom_NONE ) continue; // node
6902 nodes.resize( nbNodes );
6904 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6906 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6910 bool allTransformed = true;
6911 int nbFaces = aPolyedre->NbFaces();
6912 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6914 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6915 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6917 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6918 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6919 if ( nodeMapIt == nodeMap.end() )
6920 allTransformed = false; // not all nodes transformed
6922 nodes.push_back((*nodeMapIt).second);
6924 if ( needReverse && allTransformed )
6925 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6927 if ( !allTransformed )
6928 continue; // not all nodes transformed
6930 else // ----------------------- the rest element types
6932 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6933 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6934 const vector<int>& i = needReverse ? iRev : iForw;
6936 // find transformed nodes
6938 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6939 while ( itN->more() ) {
6940 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6941 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6942 if ( nodeMapIt == nodeMap.end() )
6943 break; // not all nodes transformed
6944 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6946 if ( iNode != nbNodes )
6947 continue; // not all nodes transformed
6951 // copy in this or a new mesh
6952 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6953 srcElems.Append( elem );
6956 // reverse element as it was reversed by transformation
6958 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6961 } // loop on elements
6963 if ( editor && editor != this )
6964 myLastCreatedElems = editor->myLastCreatedElems;
6966 PGroupIDs newGroupIDs;
6968 if ( ( theMakeGroups && theCopy ) ||
6969 ( theMakeGroups && theTargetMesh ) )
6970 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6975 //=======================================================================
6977 * \brief Create groups of elements made during transformation
6978 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6979 * \param elemGens - elements making corresponding myLastCreatedElems
6980 * \param postfix - to append to names of new groups
6981 * \param targetMesh - mesh to create groups in
6982 * \param topPresent - is there "top" elements that are created by sweeping
6984 //=======================================================================
6986 SMESH_MeshEditor::PGroupIDs
6987 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6988 const SMESH_SequenceOfElemPtr& elemGens,
6989 const std::string& postfix,
6990 SMESH_Mesh* targetMesh,
6991 const bool topPresent)
6993 PGroupIDs newGroupIDs( new list<int> );
6994 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6996 // Sort existing groups by types and collect their names
6998 // containers to store an old group and generated new ones;
6999 // 1st new group is for result elems of different type than a source one;
7000 // 2nd new group is for same type result elems ("top" group at extrusion)
7002 using boost::make_tuple;
7003 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
7004 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
7005 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7007 set< string > groupNames;
7009 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7010 if ( !groupIt->more() ) return newGroupIDs;
7012 int newGroupID = mesh->GetGroupIds().back()+1;
7013 while ( groupIt->more() )
7015 SMESH_Group * group = groupIt->next();
7016 if ( !group ) continue;
7017 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7018 if ( !groupDS || groupDS->IsEmpty() ) continue;
7019 groupNames.insert ( group->GetName() );
7020 groupDS->SetStoreName( group->GetName() );
7021 const SMDSAbs_ElementType type = groupDS->GetType();
7022 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7023 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7024 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7025 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7028 // Loop on nodes and elements to add them in new groups
7030 vector< const SMDS_MeshElement* > resultElems;
7031 for ( int isNodes = 0; isNodes < 2; ++isNodes )
7033 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
7034 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7035 if ( gens.Length() != elems.Length() )
7036 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7038 // loop on created elements
7039 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7041 const SMDS_MeshElement* sourceElem = gens( iElem );
7042 if ( !sourceElem ) {
7043 MESSAGE("generateGroups(): NULL source element");
7046 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7047 if ( groupsOldNew.empty() ) { // no groups of this type at all
7048 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7049 ++iElem; // skip all elements made by sourceElem
7052 // collect all elements made by the iElem-th sourceElem
7053 resultElems.clear();
7054 if ( const SMDS_MeshElement* resElem = elems( iElem ))
7055 if ( resElem != sourceElem )
7056 resultElems.push_back( resElem );
7057 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7058 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7059 if ( resElem != sourceElem )
7060 resultElems.push_back( resElem );
7062 const SMDS_MeshElement* topElem = 0;
7063 if ( isNodes ) // there must be a top element
7065 topElem = resultElems.back();
7066 resultElems.pop_back();
7070 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7071 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7072 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7074 topElem = *resElemIt;
7075 *resElemIt = 0; // erase *resElemIt
7079 // add resultElems to groups originted from ones the sourceElem belongs to
7080 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7081 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7083 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7084 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7086 // fill in a new group
7087 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7088 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7089 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7091 newGroup.Add( *resElemIt );
7093 // fill a "top" group
7096 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7097 newTopGroup.Add( topElem );
7101 } // loop on created elements
7102 }// loop on nodes and elements
7104 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7106 list<int> topGrouIds;
7107 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7109 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
7110 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7111 orderedOldNewGroups[i]->get<2>() };
7112 for ( int is2nd = 0; is2nd < 2; ++is2nd )
7114 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7115 if ( newGroupDS->IsEmpty() )
7117 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7122 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7125 const bool isTop = ( topPresent &&
7126 newGroupDS->GetType() == oldGroupDS->GetType() &&
7129 string name = oldGroupDS->GetStoreName();
7130 { // remove trailing whitespaces (issue 22599)
7131 size_t size = name.size();
7132 while ( size > 1 && isspace( name[ size-1 ]))
7134 if ( size != name.size() )
7136 name.resize( size );
7137 oldGroupDS->SetStoreName( name.c_str() );
7140 if ( !targetMesh ) {
7141 string suffix = ( isTop ? "top": postfix.c_str() );
7145 while ( !groupNames.insert( name ).second ) // name exists
7146 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7151 newGroupDS->SetStoreName( name.c_str() );
7153 // make a SMESH_Groups
7154 mesh->AddGroup( newGroupDS );
7156 topGrouIds.push_back( newGroupDS->GetID() );
7158 newGroupIDs->push_back( newGroupDS->GetID() );
7162 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7167 //================================================================================
7169 * * \brief Return list of group of nodes close to each other within theTolerance
7170 * * Search among theNodes or in the whole mesh if theNodes is empty using
7171 * * an Octree algorithm
7172 * \param [in,out] theNodes - the nodes to treat
7173 * \param [in] theTolerance - the tolerance
7174 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7175 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7176 * corner and medium nodes in separate groups
7178 //================================================================================
7180 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7181 const double theTolerance,
7182 TListOfListOfNodes & theGroupsOfNodes,
7183 bool theSeparateCornersAndMedium)
7185 myLastCreatedElems.Clear();
7186 myLastCreatedNodes.Clear();
7188 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7189 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7190 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7191 theSeparateCornersAndMedium = false;
7193 TIDSortedNodeSet& corners = theNodes;
7194 TIDSortedNodeSet medium;
7196 if ( theNodes.empty() ) // get all nodes in the mesh
7198 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7199 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7200 if ( theSeparateCornersAndMedium )
7201 while ( nIt->more() )
7203 const SMDS_MeshNode* n = nIt->next();
7204 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7205 nodeSet->insert( nodeSet->end(), n );
7208 while ( nIt->more() )
7209 theNodes.insert( theNodes.end(),nIt->next() );
7211 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7213 TIDSortedNodeSet::iterator nIt = corners.begin();
7214 while ( nIt != corners.end() )
7215 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7217 medium.insert( medium.end(), *nIt );
7218 corners.erase( nIt++ );
7226 if ( !corners.empty() )
7227 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7228 if ( !medium.empty() )
7229 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7232 //=======================================================================
7233 //function : SimplifyFace
7234 //purpose : split a chain of nodes into several closed chains
7235 //=======================================================================
7237 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7238 vector<const SMDS_MeshNode *>& poly_nodes,
7239 vector<int>& quantities) const
7241 int nbNodes = faceNodes.size();
7246 set<const SMDS_MeshNode*> nodeSet;
7248 // get simple seq of nodes
7249 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7250 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7251 int iSimple = 0, nbUnique = 0;
7253 simpleNodes[iSimple++] = faceNodes[0];
7255 for (int iCur = 1; iCur < nbNodes; iCur++) {
7256 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7257 simpleNodes[iSimple++] = faceNodes[iCur];
7258 if (nodeSet.insert( faceNodes[iCur] ).second)
7262 int nbSimple = iSimple;
7263 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7273 bool foundLoop = (nbSimple > nbUnique);
7276 set<const SMDS_MeshNode*> loopSet;
7277 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7278 const SMDS_MeshNode* n = simpleNodes[iSimple];
7279 if (!loopSet.insert( n ).second) {
7283 int iC = 0, curLast = iSimple;
7284 for (; iC < curLast; iC++) {
7285 if (simpleNodes[iC] == n) break;
7287 int loopLen = curLast - iC;
7289 // create sub-element
7291 quantities.push_back(loopLen);
7292 for (; iC < curLast; iC++) {
7293 poly_nodes.push_back(simpleNodes[iC]);
7296 // shift the rest nodes (place from the first loop position)
7297 for (iC = curLast + 1; iC < nbSimple; iC++) {
7298 simpleNodes[iC - loopLen] = simpleNodes[iC];
7300 nbSimple -= loopLen;
7303 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7304 } // while (foundLoop)
7308 quantities.push_back(iSimple);
7309 for (int i = 0; i < iSimple; i++)
7310 poly_nodes.push_back(simpleNodes[i]);
7316 //=======================================================================
7317 //function : MergeNodes
7318 //purpose : In each group, the cdr of nodes are substituted by the first one
7320 //=======================================================================
7322 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7324 MESSAGE("MergeNodes");
7325 myLastCreatedElems.Clear();
7326 myLastCreatedNodes.Clear();
7328 SMESHDS_Mesh* aMesh = GetMeshDS();
7330 TNodeNodeMap nodeNodeMap; // node to replace - new node
7331 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7332 list< int > rmElemIds, rmNodeIds;
7334 // Fill nodeNodeMap and elems
7336 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7337 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7339 list<const SMDS_MeshNode*>& nodes = *grIt;
7340 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7341 const SMDS_MeshNode* nToKeep = *nIt;
7342 for ( ++nIt; nIt != nodes.end(); nIt++ )
7344 const SMDS_MeshNode* nToRemove = *nIt;
7345 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7346 if ( nToRemove != nToKeep )
7348 rmNodeIds.push_back( nToRemove->GetID() );
7349 AddToSameGroups( nToKeep, nToRemove, aMesh );
7350 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7351 // after MergeNodes() w/o creating node in place of merged ones.
7352 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7353 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7354 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7355 sm->SetIsAlwaysComputed( true );
7357 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7358 while ( invElemIt->more() ) {
7359 const SMDS_MeshElement* elem = invElemIt->next();
7364 // Change element nodes or remove an element
7366 set<const SMDS_MeshNode*> nodeSet;
7367 vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7369 ElemFeatures elemType;
7371 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7372 for ( ; eIt != elems.end(); eIt++ )
7374 const SMDS_MeshElement* elem = *eIt;
7375 int nbNodes = elem->NbNodes();
7376 int aShapeId = FindShape( elem );
7379 curNodes.resize( nbNodes );
7380 uniqueNodes.resize( nbNodes );
7381 iRepl.resize( nbNodes );
7382 int iUnique = 0, iCur = 0, nbRepl = 0;
7384 // get new seq of nodes
7385 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7386 while ( itN->more() )
7388 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7390 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7391 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7393 { ////////// BUG 0020185: begin
7394 bool stopRecur = false;
7395 set<const SMDS_MeshNode*> nodesRecur;
7396 nodesRecur.insert(n);
7397 while (!stopRecur) {
7398 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7399 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7400 n = (*nnIt_i).second;
7401 if (!nodesRecur.insert(n).second) {
7402 // error: recursive dependancy
7409 } ////////// BUG 0020185: end
7411 curNodes[ iCur ] = n;
7412 bool isUnique = nodeSet.insert( n ).second;
7414 uniqueNodes[ iUnique++ ] = n;
7416 iRepl[ nbRepl++ ] = iCur;
7420 // Analyse element topology after replacement
7423 int nbUniqueNodes = nodeSet.size();
7424 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7426 if (elem->IsPoly()) // Polygons and Polyhedral volumes
7428 if (elem->GetType() == SMDSAbs_Face) // Polygon
7430 elemType.Init( elem );
7431 const bool isQuad = elemType.myIsQuad;
7433 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7434 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7436 // a polygon can divide into several elements
7437 vector<const SMDS_MeshNode *> polygons_nodes;
7438 vector<int> quantities;
7439 int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7442 vector<const SMDS_MeshNode *> face_nodes;
7444 for (int iface = 0; iface < nbNew; iface++)
7446 int nbNewNodes = quantities[iface];
7447 face_nodes.assign( polygons_nodes.begin() + inode,
7448 polygons_nodes.begin() + inode + nbNewNodes );
7449 inode += nbNewNodes;
7450 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7452 bool isValid = ( nbNewNodes % 2 == 0 );
7453 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7454 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7455 elemType.SetQuad( isValid );
7456 if ( isValid ) // put medium nodes after corners
7457 SMDS_MeshCell::applyInterlaceRev
7458 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7459 nbNewNodes ), face_nodes );
7461 SMDS_MeshElement* newElem = AddElement( face_nodes, elemType );
7463 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7466 rmElemIds.push_back(elem->GetID());
7470 else if (elem->GetType() == SMDSAbs_Volume) // Polyhedral volume
7472 if (nbUniqueNodes < 4) {
7473 rmElemIds.push_back(elem->GetID());
7476 // each face has to be analyzed in order to check volume validity
7477 const SMDS_VtkVolume* aPolyedre =
7478 dynamic_cast<const SMDS_VtkVolume*>( elem );
7480 int nbFaces = aPolyedre->NbFaces();
7482 vector<const SMDS_MeshNode *> poly_nodes;
7483 vector<int> quantities;
7485 for (int iface = 1; iface <= nbFaces; iface++) {
7486 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7487 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7489 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7490 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7491 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7492 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7493 faceNode = (*nnIt).second;
7495 faceNodes[inode - 1] = faceNode;
7498 SimplifyFace(faceNodes, poly_nodes, quantities);
7501 if (quantities.size() > 3) {
7502 // to be done: remove coincident faces
7505 if (quantities.size() > 3)
7507 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7508 const SMDS_MeshElement* newElem =
7509 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7510 myLastCreatedElems.Append(newElem);
7511 if ( aShapeId && newElem )
7512 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7513 rmElemIds.push_back(elem->GetID());
7517 rmElemIds.push_back(elem->GetID());
7528 // TODO not all the possible cases are solved. Find something more generic?
7529 switch ( nbNodes ) {
7530 case 2: ///////////////////////////////////// EDGE
7531 isOk = false; break;
7532 case 3: ///////////////////////////////////// TRIANGLE
7533 isOk = false; break;
7535 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7537 else { //////////////////////////////////// QUADRANGLE
7538 if ( nbUniqueNodes < 3 )
7540 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7541 isOk = false; // opposite nodes stick
7542 //MESSAGE("isOk " << isOk);
7545 case 6: ///////////////////////////////////// PENTAHEDRON
7546 if ( nbUniqueNodes == 4 ) {
7547 // ---------------------------------> tetrahedron
7549 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7550 // all top nodes stick: reverse a bottom
7551 uniqueNodes[ 0 ] = curNodes [ 1 ];
7552 uniqueNodes[ 1 ] = curNodes [ 0 ];
7554 else if (nbRepl == 3 &&
7555 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7556 // all bottom nodes stick: set a top before
7557 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7558 uniqueNodes[ 0 ] = curNodes [ 3 ];
7559 uniqueNodes[ 1 ] = curNodes [ 4 ];
7560 uniqueNodes[ 2 ] = curNodes [ 5 ];
7562 else if (nbRepl == 4 &&
7563 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7564 // a lateral face turns into a line: reverse a bottom
7565 uniqueNodes[ 0 ] = curNodes [ 1 ];
7566 uniqueNodes[ 1 ] = curNodes [ 0 ];
7571 else if ( nbUniqueNodes == 5 ) {
7572 // PENTAHEDRON --------------------> 2 tetrahedrons
7573 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7574 // a bottom node sticks with a linked top one
7576 SMDS_MeshElement* newElem =
7577 aMesh->AddVolume(curNodes[ 3 ],
7580 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7581 myLastCreatedElems.Append(newElem);
7583 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7584 // 2. : reverse a bottom
7585 uniqueNodes[ 0 ] = curNodes [ 1 ];
7586 uniqueNodes[ 1 ] = curNodes [ 0 ];
7596 if(elem->IsQuadratic()) { // Quadratic quadrangle
7608 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7611 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7613 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7614 uniqueNodes[0] = curNodes[0];
7615 uniqueNodes[1] = curNodes[2];
7616 uniqueNodes[2] = curNodes[3];
7617 uniqueNodes[3] = curNodes[5];
7618 uniqueNodes[4] = curNodes[6];
7619 uniqueNodes[5] = curNodes[7];
7622 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7623 uniqueNodes[0] = curNodes[0];
7624 uniqueNodes[1] = curNodes[1];
7625 uniqueNodes[2] = curNodes[2];
7626 uniqueNodes[3] = curNodes[4];
7627 uniqueNodes[4] = curNodes[5];
7628 uniqueNodes[5] = curNodes[6];
7631 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7632 uniqueNodes[0] = curNodes[1];
7633 uniqueNodes[1] = curNodes[2];
7634 uniqueNodes[2] = curNodes[3];
7635 uniqueNodes[3] = curNodes[5];
7636 uniqueNodes[4] = curNodes[6];
7637 uniqueNodes[5] = curNodes[0];
7640 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7641 uniqueNodes[0] = curNodes[0];
7642 uniqueNodes[1] = curNodes[1];
7643 uniqueNodes[2] = curNodes[3];
7644 uniqueNodes[3] = curNodes[4];
7645 uniqueNodes[4] = curNodes[6];
7646 uniqueNodes[5] = curNodes[7];
7649 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7650 uniqueNodes[0] = curNodes[0];
7651 uniqueNodes[1] = curNodes[2];
7652 uniqueNodes[2] = curNodes[3];
7653 uniqueNodes[3] = curNodes[1];
7654 uniqueNodes[4] = curNodes[6];
7655 uniqueNodes[5] = curNodes[7];
7658 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7659 uniqueNodes[0] = curNodes[0];
7660 uniqueNodes[1] = curNodes[1];
7661 uniqueNodes[2] = curNodes[2];
7662 uniqueNodes[3] = curNodes[4];
7663 uniqueNodes[4] = curNodes[5];
7664 uniqueNodes[5] = curNodes[7];
7667 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7668 uniqueNodes[0] = curNodes[0];
7669 uniqueNodes[1] = curNodes[1];
7670 uniqueNodes[2] = curNodes[3];
7671 uniqueNodes[3] = curNodes[4];
7672 uniqueNodes[4] = curNodes[2];
7673 uniqueNodes[5] = curNodes[7];
7676 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7677 uniqueNodes[0] = curNodes[0];
7678 uniqueNodes[1] = curNodes[1];
7679 uniqueNodes[2] = curNodes[2];
7680 uniqueNodes[3] = curNodes[4];
7681 uniqueNodes[4] = curNodes[5];
7682 uniqueNodes[5] = curNodes[3];
7687 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7690 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7694 //////////////////////////////////// HEXAHEDRON
7696 SMDS_VolumeTool hexa (elem);
7697 hexa.SetExternalNormal();
7698 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7699 //////////////////////// HEX ---> 1 tetrahedron
7700 for ( int iFace = 0; iFace < 6; iFace++ ) {
7701 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7702 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7703 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7704 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7705 // one face turns into a point ...
7706 int iOppFace = hexa.GetOppFaceIndex( iFace );
7707 ind = hexa.GetFaceNodesIndices( iOppFace );
7709 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7710 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7713 if ( nbStick == 1 ) {
7714 // ... and the opposite one - into a triangle.
7716 ind = hexa.GetFaceNodesIndices( iFace );
7717 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7724 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7725 //////////////////////// HEX ---> 1 prism
7726 int nbTria = 0, iTria[3];
7727 const int *ind; // indices of face nodes
7728 // look for triangular faces
7729 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7730 ind = hexa.GetFaceNodesIndices( iFace );
7731 TIDSortedNodeSet faceNodes;
7732 for ( iCur = 0; iCur < 4; iCur++ )
7733 faceNodes.insert( curNodes[ind[iCur]] );
7734 if ( faceNodes.size() == 3 )
7735 iTria[ nbTria++ ] = iFace;
7737 // check if triangles are opposite
7738 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7741 // set nodes of the bottom triangle
7742 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7744 for ( iCur = 0; iCur < 4; iCur++ )
7745 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7746 indB.push_back( ind[iCur] );
7747 if ( !hexa.IsForward() )
7748 std::swap( indB[0], indB[2] );
7749 for ( iCur = 0; iCur < 3; iCur++ )
7750 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7751 // set nodes of the top triangle
7752 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7753 for ( iCur = 0; iCur < 3; ++iCur )
7754 for ( int j = 0; j < 4; ++j )
7755 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7757 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7763 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7764 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7765 for ( int iFace = 0; iFace < 6; iFace++ ) {
7766 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7767 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7768 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7769 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7770 // one face turns into a point ...
7771 int iOppFace = hexa.GetOppFaceIndex( iFace );
7772 ind = hexa.GetFaceNodesIndices( iOppFace );
7774 iUnique = 2; // reverse a tetrahedron 1 bottom
7775 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7776 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7778 else if ( iUnique >= 0 )
7779 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7781 if ( nbStick == 0 ) {
7782 // ... and the opposite one is a quadrangle
7784 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7785 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7788 SMDS_MeshElement* newElem =
7789 aMesh->AddVolume(curNodes[ind[ 0 ]],
7792 curNodes[indTop[ 0 ]]);
7793 myLastCreatedElems.Append(newElem);
7795 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7802 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7803 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7804 // find indices of quad and tri faces
7805 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7806 for ( iFace = 0; iFace < 6; iFace++ ) {
7807 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7809 for ( iCur = 0; iCur < 4; iCur++ )
7810 nodeSet.insert( curNodes[ind[ iCur ]] );
7811 nbUniqueNodes = nodeSet.size();
7812 if ( nbUniqueNodes == 3 )
7813 iTriFace[ nbTri++ ] = iFace;
7814 else if ( nbUniqueNodes == 4 )
7815 iQuadFace[ nbQuad++ ] = iFace;
7817 if (nbQuad == 2 && nbTri == 4 &&
7818 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7819 // 2 opposite quadrangles stuck with a diagonal;
7820 // sample groups of merged indices: (0-4)(2-6)
7821 // --------------------------------------------> 2 tetrahedrons
7822 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7823 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7824 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7825 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7826 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7827 // stuck with 0-2 diagonal
7835 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7836 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7837 // stuck with 1-3 diagonal
7849 uniqueNodes[ 0 ] = curNodes [ i0 ];
7850 uniqueNodes[ 1 ] = curNodes [ i1d ];
7851 uniqueNodes[ 2 ] = curNodes [ i3d ];
7852 uniqueNodes[ 3 ] = curNodes [ i0t ];
7855 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7859 myLastCreatedElems.Append(newElem);
7861 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7864 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7865 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7866 // --------------------------------------------> prism
7867 // find 2 opposite triangles
7869 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7870 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7871 // find indices of kept and replaced nodes
7872 // and fill unique nodes of 2 opposite triangles
7873 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7874 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7875 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7876 // fill unique nodes
7879 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7880 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7881 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7883 // iCur of a linked node of the opposite face (make normals co-directed):
7884 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7885 // check that correspondent corners of triangles are linked
7886 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7889 uniqueNodes[ iUnique ] = n;
7890 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7899 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7902 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7909 } // switch ( nbNodes )
7911 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7913 if ( isOk ) // the non-poly elem remains valid after sticking nodes
7915 elemType.Init( elem ).SetID( elem->GetID() );
7917 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7918 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7920 uniqueNodes.resize(nbUniqueNodes);
7921 SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7922 if ( sm && newElem )
7923 sm->AddElement( newElem );
7924 if ( elem != newElem )
7925 ReplaceElemInGroups( elem, newElem, aMesh );
7928 // Remove invalid regular element or invalid polygon
7929 rmElemIds.push_back( elem->GetID() );
7932 } // loop on elements
7934 // Remove bad elements, then equal nodes (order important)
7936 Remove( rmElemIds, false );
7937 Remove( rmNodeIds, true );
7943 // ========================================================
7944 // class : SortableElement
7945 // purpose : allow sorting elements basing on their nodes
7946 // ========================================================
7947 class SortableElement : public set <const SMDS_MeshElement*>
7951 SortableElement( const SMDS_MeshElement* theElem )
7954 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7955 while ( nodeIt->more() )
7956 this->insert( nodeIt->next() );
7959 const SMDS_MeshElement* Get() const
7962 void Set(const SMDS_MeshElement* e) const
7967 mutable const SMDS_MeshElement* myElem;
7970 //=======================================================================
7971 //function : FindEqualElements
7972 //purpose : Return list of group of elements built on the same nodes.
7973 // Search among theElements or in the whole mesh if theElements is empty
7974 //=======================================================================
7976 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7977 TListOfListOfElementsID & theGroupsOfElementsID)
7979 myLastCreatedElems.Clear();
7980 myLastCreatedNodes.Clear();
7982 typedef map< SortableElement, int > TMapOfNodeSet;
7983 typedef list<int> TGroupOfElems;
7985 if ( theElements.empty() )
7986 { // get all elements in the mesh
7987 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7988 while ( eIt->more() )
7989 theElements.insert( theElements.end(), eIt->next() );
7992 vector< TGroupOfElems > arrayOfGroups;
7993 TGroupOfElems groupOfElems;
7994 TMapOfNodeSet mapOfNodeSet;
7996 TIDSortedElemSet::iterator elemIt = theElements.begin();
7997 for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7999 const SMDS_MeshElement* curElem = *elemIt;
8000 SortableElement SE(curElem);
8002 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8003 if ( !pp.second ) { // one more coincident elem
8004 TMapOfNodeSet::iterator& itSE = pp.first;
8005 int ind = (*itSE).second;
8006 arrayOfGroups[ind].push_back( curElem->GetID() );
8009 arrayOfGroups.push_back( groupOfElems );
8010 arrayOfGroups.back().push_back( curElem->GetID() );
8015 groupOfElems.clear();
8016 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8017 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8019 if ( groupIt->size() > 1 ) {
8020 //groupOfElems.sort(); -- theElements is sorted already
8021 theGroupsOfElementsID.push_back( groupOfElems );
8022 theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8027 //=======================================================================
8028 //function : MergeElements
8029 //purpose : In each given group, substitute all elements by the first one.
8030 //=======================================================================
8032 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8034 myLastCreatedElems.Clear();
8035 myLastCreatedNodes.Clear();
8037 typedef list<int> TListOfIDs;
8038 TListOfIDs rmElemIds; // IDs of elems to remove
8040 SMESHDS_Mesh* aMesh = GetMeshDS();
8042 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8043 while ( groupsIt != theGroupsOfElementsID.end() ) {
8044 TListOfIDs& aGroupOfElemID = *groupsIt;
8045 aGroupOfElemID.sort();
8046 int elemIDToKeep = aGroupOfElemID.front();
8047 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8048 aGroupOfElemID.pop_front();
8049 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8050 while ( idIt != aGroupOfElemID.end() ) {
8051 int elemIDToRemove = *idIt;
8052 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8053 // add the kept element in groups of removed one (PAL15188)
8054 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8055 rmElemIds.push_back( elemIDToRemove );
8061 Remove( rmElemIds, false );
8064 //=======================================================================
8065 //function : MergeEqualElements
8066 //purpose : Remove all but one of elements built on the same nodes.
8067 //=======================================================================
8069 void SMESH_MeshEditor::MergeEqualElements()
8071 TIDSortedElemSet aMeshElements; /* empty input ==
8072 to merge equal elements in the whole mesh */
8073 TListOfListOfElementsID aGroupsOfElementsID;
8074 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8075 MergeElements(aGroupsOfElementsID);
8078 //=======================================================================
8079 //function : findAdjacentFace
8081 //=======================================================================
8083 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8084 const SMDS_MeshNode* n2,
8085 const SMDS_MeshElement* elem)
8087 TIDSortedElemSet elemSet, avoidSet;
8089 avoidSet.insert ( elem );
8090 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8093 //=======================================================================
8094 //function : FindFreeBorder
8096 //=======================================================================
8098 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8100 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8101 const SMDS_MeshNode* theSecondNode,
8102 const SMDS_MeshNode* theLastNode,
8103 list< const SMDS_MeshNode* > & theNodes,
8104 list< const SMDS_MeshElement* >& theFaces)
8106 if ( !theFirstNode || !theSecondNode )
8108 // find border face between theFirstNode and theSecondNode
8109 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8113 theFaces.push_back( curElem );
8114 theNodes.push_back( theFirstNode );
8115 theNodes.push_back( theSecondNode );
8117 //vector<const SMDS_MeshNode*> nodes;
8118 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8119 TIDSortedElemSet foundElems;
8120 bool needTheLast = ( theLastNode != 0 );
8122 while ( nStart != theLastNode ) {
8123 if ( nStart == theFirstNode )
8124 return !needTheLast;
8126 // find all free border faces sharing form nStart
8128 list< const SMDS_MeshElement* > curElemList;
8129 list< const SMDS_MeshNode* > nStartList;
8130 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8131 while ( invElemIt->more() ) {
8132 const SMDS_MeshElement* e = invElemIt->next();
8133 if ( e == curElem || foundElems.insert( e ).second ) {
8135 int iNode = 0, nbNodes = e->NbNodes();
8136 //const SMDS_MeshNode* nodes[nbNodes+1];
8137 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8139 if(e->IsQuadratic()) {
8140 const SMDS_VtkFace* F =
8141 dynamic_cast<const SMDS_VtkFace*>(e);
8142 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8143 // use special nodes iterator
8144 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8145 while( anIter->more() ) {
8146 nodes[ iNode++ ] = cast2Node(anIter->next());
8150 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8151 while ( nIt->more() )
8152 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8154 nodes[ iNode ] = nodes[ 0 ];
8156 for ( iNode = 0; iNode < nbNodes; iNode++ )
8157 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8158 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8159 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8161 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8162 curElemList.push_back( e );
8166 // analyse the found
8168 int nbNewBorders = curElemList.size();
8169 if ( nbNewBorders == 0 ) {
8170 // no free border furthermore
8171 return !needTheLast;
8173 else if ( nbNewBorders == 1 ) {
8174 // one more element found
8176 nStart = nStartList.front();
8177 curElem = curElemList.front();
8178 theFaces.push_back( curElem );
8179 theNodes.push_back( nStart );
8182 // several continuations found
8183 list< const SMDS_MeshElement* >::iterator curElemIt;
8184 list< const SMDS_MeshNode* >::iterator nStartIt;
8185 // check if one of them reached the last node
8186 if ( needTheLast ) {
8187 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8188 curElemIt!= curElemList.end();
8189 curElemIt++, nStartIt++ )
8190 if ( *nStartIt == theLastNode ) {
8191 theFaces.push_back( *curElemIt );
8192 theNodes.push_back( *nStartIt );
8196 // find the best free border by the continuations
8197 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8198 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8199 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8200 curElemIt!= curElemList.end();
8201 curElemIt++, nStartIt++ )
8203 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8204 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8205 // find one more free border
8206 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8210 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8211 // choice: clear a worse one
8212 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8213 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8214 contNodes[ iWorse ].clear();
8215 contFaces[ iWorse ].clear();
8218 if ( contNodes[0].empty() && contNodes[1].empty() )
8221 // append the best free border
8222 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8223 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8224 theNodes.pop_back(); // remove nIgnore
8225 theNodes.pop_back(); // remove nStart
8226 theFaces.pop_back(); // remove curElem
8227 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8228 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8229 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8230 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8233 } // several continuations found
8234 } // while ( nStart != theLastNode )
8239 //=======================================================================
8240 //function : CheckFreeBorderNodes
8241 //purpose : Return true if the tree nodes are on a free border
8242 //=======================================================================
8244 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8245 const SMDS_MeshNode* theNode2,
8246 const SMDS_MeshNode* theNode3)
8248 list< const SMDS_MeshNode* > nodes;
8249 list< const SMDS_MeshElement* > faces;
8250 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8253 //=======================================================================
8254 //function : SewFreeBorder
8256 //=======================================================================
8258 SMESH_MeshEditor::Sew_Error
8259 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8260 const SMDS_MeshNode* theBordSecondNode,
8261 const SMDS_MeshNode* theBordLastNode,
8262 const SMDS_MeshNode* theSideFirstNode,
8263 const SMDS_MeshNode* theSideSecondNode,
8264 const SMDS_MeshNode* theSideThirdNode,
8265 const bool theSideIsFreeBorder,
8266 const bool toCreatePolygons,
8267 const bool toCreatePolyedrs)
8269 myLastCreatedElems.Clear();
8270 myLastCreatedNodes.Clear();
8272 MESSAGE("::SewFreeBorder()");
8273 Sew_Error aResult = SEW_OK;
8275 // ====================================
8276 // find side nodes and elements
8277 // ====================================
8279 list< const SMDS_MeshNode* > nSide[ 2 ];
8280 list< const SMDS_MeshElement* > eSide[ 2 ];
8281 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8282 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8286 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8287 nSide[0], eSide[0])) {
8288 MESSAGE(" Free Border 1 not found " );
8289 aResult = SEW_BORDER1_NOT_FOUND;
8291 if (theSideIsFreeBorder) {
8294 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8295 nSide[1], eSide[1])) {
8296 MESSAGE(" Free Border 2 not found " );
8297 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8300 if ( aResult != SEW_OK )
8303 if (!theSideIsFreeBorder) {
8307 // -------------------------------------------------------------------------
8309 // 1. If nodes to merge are not coincident, move nodes of the free border
8310 // from the coord sys defined by the direction from the first to last
8311 // nodes of the border to the correspondent sys of the side 2
8312 // 2. On the side 2, find the links most co-directed with the correspondent
8313 // links of the free border
8314 // -------------------------------------------------------------------------
8316 // 1. Since sewing may break if there are volumes to split on the side 2,
8317 // we wont move nodes but just compute new coordinates for them
8318 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8319 TNodeXYZMap nBordXYZ;
8320 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8321 list< const SMDS_MeshNode* >::iterator nBordIt;
8323 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8324 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8325 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8326 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8327 double tol2 = 1.e-8;
8328 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8329 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8330 // Need node movement.
8332 // find X and Z axes to create trsf
8333 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8335 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8337 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8340 gp_Ax3 toBordAx( Pb1, Zb, X );
8341 gp_Ax3 fromSideAx( Ps1, Zs, X );
8342 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8344 gp_Trsf toBordSys, fromSide2Sys;
8345 toBordSys.SetTransformation( toBordAx );
8346 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8347 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8350 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8351 const SMDS_MeshNode* n = *nBordIt;
8352 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8353 toBordSys.Transforms( xyz );
8354 fromSide2Sys.Transforms( xyz );
8355 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8359 // just insert nodes XYZ in the nBordXYZ map
8360 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8361 const SMDS_MeshNode* n = *nBordIt;
8362 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8366 // 2. On the side 2, find the links most co-directed with the correspondent
8367 // links of the free border
8369 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8370 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8371 sideNodes.push_back( theSideFirstNode );
8373 bool hasVolumes = false;
8374 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8375 set<long> foundSideLinkIDs, checkedLinkIDs;
8376 SMDS_VolumeTool volume;
8377 //const SMDS_MeshNode* faceNodes[ 4 ];
8379 const SMDS_MeshNode* sideNode;
8380 const SMDS_MeshElement* sideElem;
8381 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8382 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8383 nBordIt = bordNodes.begin();
8385 // border node position and border link direction to compare with
8386 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8387 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8388 // choose next side node by link direction or by closeness to
8389 // the current border node:
8390 bool searchByDir = ( *nBordIt != theBordLastNode );
8392 // find the next node on the Side 2
8394 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8396 checkedLinkIDs.clear();
8397 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8399 // loop on inverse elements of current node (prevSideNode) on the Side 2
8400 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8401 while ( invElemIt->more() )
8403 const SMDS_MeshElement* elem = invElemIt->next();
8404 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8405 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8406 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8407 bool isVolume = volume.Set( elem );
8408 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8409 if ( isVolume ) // --volume
8411 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8412 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8413 if(elem->IsQuadratic()) {
8414 const SMDS_VtkFace* F =
8415 dynamic_cast<const SMDS_VtkFace*>(elem);
8416 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8417 // use special nodes iterator
8418 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8419 while( anIter->more() ) {
8420 nodes[ iNode ] = cast2Node(anIter->next());
8421 if ( nodes[ iNode++ ] == prevSideNode )
8422 iPrevNode = iNode - 1;
8426 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8427 while ( nIt->more() ) {
8428 nodes[ iNode ] = cast2Node( nIt->next() );
8429 if ( nodes[ iNode++ ] == prevSideNode )
8430 iPrevNode = iNode - 1;
8433 // there are 2 links to check
8438 // loop on links, to be precise, on the second node of links
8439 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8440 const SMDS_MeshNode* n = nodes[ iNode ];
8442 if ( !volume.IsLinked( n, prevSideNode ))
8446 if ( iNode ) // a node before prevSideNode
8447 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8448 else // a node after prevSideNode
8449 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8451 // check if this link was already used
8452 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8453 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8454 if (!isJustChecked &&
8455 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8457 // test a link geometrically
8458 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8459 bool linkIsBetter = false;
8460 double dot = 0.0, dist = 0.0;
8461 if ( searchByDir ) { // choose most co-directed link
8462 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8463 linkIsBetter = ( dot > maxDot );
8465 else { // choose link with the node closest to bordPos
8466 dist = ( nextXYZ - bordPos ).SquareModulus();
8467 linkIsBetter = ( dist < minDist );
8469 if ( linkIsBetter ) {
8478 } // loop on inverse elements of prevSideNode
8481 MESSAGE(" Cant find path by links of the Side 2 ");
8482 return SEW_BAD_SIDE_NODES;
8484 sideNodes.push_back( sideNode );
8485 sideElems.push_back( sideElem );
8486 foundSideLinkIDs.insert ( linkID );
8487 prevSideNode = sideNode;
8489 if ( *nBordIt == theBordLastNode )
8490 searchByDir = false;
8492 // find the next border link to compare with
8493 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8494 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8495 // move to next border node if sideNode is before forward border node (bordPos)
8496 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8497 prevBordNode = *nBordIt;
8499 bordPos = nBordXYZ[ *nBordIt ];
8500 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8501 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8505 while ( sideNode != theSideSecondNode );
8507 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8508 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8509 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8511 } // end nodes search on the side 2
8513 // ============================
8514 // sew the border to the side 2
8515 // ============================
8517 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8518 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8520 TListOfListOfNodes nodeGroupsToMerge;
8521 if ( nbNodes[0] == nbNodes[1] ||
8522 ( theSideIsFreeBorder && !theSideThirdNode)) {
8524 // all nodes are to be merged
8526 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8527 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8528 nIt[0]++, nIt[1]++ )
8530 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8531 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8532 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8537 // insert new nodes into the border and the side to get equal nb of segments
8539 // get normalized parameters of nodes on the borders
8540 //double param[ 2 ][ maxNbNodes ];
8542 param[0] = new double [ maxNbNodes ];
8543 param[1] = new double [ maxNbNodes ];
8545 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8546 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8547 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8548 const SMDS_MeshNode* nPrev = *nIt;
8549 double bordLength = 0;
8550 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8551 const SMDS_MeshNode* nCur = *nIt;
8552 gp_XYZ segment (nCur->X() - nPrev->X(),
8553 nCur->Y() - nPrev->Y(),
8554 nCur->Z() - nPrev->Z());
8555 double segmentLen = segment.Modulus();
8556 bordLength += segmentLen;
8557 param[ iBord ][ iNode ] = bordLength;
8560 // normalize within [0,1]
8561 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8562 param[ iBord ][ iNode ] /= bordLength;
8566 // loop on border segments
8567 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8568 int i[ 2 ] = { 0, 0 };
8569 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8570 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8572 TElemOfNodeListMap insertMap;
8573 TElemOfNodeListMap::iterator insertMapIt;
8575 // key: elem to insert nodes into
8576 // value: 2 nodes to insert between + nodes to be inserted
8578 bool next[ 2 ] = { false, false };
8580 // find min adjacent segment length after sewing
8581 double nextParam = 10., prevParam = 0;
8582 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8583 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8584 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8585 if ( i[ iBord ] > 0 )
8586 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8588 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8589 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8590 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8592 // choose to insert or to merge nodes
8593 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8594 if ( Abs( du ) <= minSegLen * 0.2 ) {
8597 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8598 const SMDS_MeshNode* n0 = *nIt[0];
8599 const SMDS_MeshNode* n1 = *nIt[1];
8600 nodeGroupsToMerge.back().push_back( n1 );
8601 nodeGroupsToMerge.back().push_back( n0 );
8602 // position of node of the border changes due to merge
8603 param[ 0 ][ i[0] ] += du;
8604 // move n1 for the sake of elem shape evaluation during insertion.
8605 // n1 will be removed by MergeNodes() anyway
8606 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8607 next[0] = next[1] = true;
8612 int intoBord = ( du < 0 ) ? 0 : 1;
8613 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8614 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8615 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8616 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8617 if ( intoBord == 1 ) {
8618 // move node of the border to be on a link of elem of the side
8619 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8620 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8621 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8622 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8623 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8625 insertMapIt = insertMap.find( elem );
8626 bool notFound = ( insertMapIt == insertMap.end() );
8627 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8629 // insert into another link of the same element:
8630 // 1. perform insertion into the other link of the elem
8631 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8632 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8633 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8634 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8635 // 2. perform insertion into the link of adjacent faces
8637 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8639 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8643 if (toCreatePolyedrs) {
8644 // perform insertion into the links of adjacent volumes
8645 UpdateVolumes(n12, n22, nodeList);
8647 // 3. find an element appeared on n1 and n2 after the insertion
8648 insertMap.erase( elem );
8649 elem = findAdjacentFace( n1, n2, 0 );
8651 if ( notFound || otherLink ) {
8652 // add element and nodes of the side into the insertMap
8653 insertMapIt = insertMap.insert
8654 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8655 (*insertMapIt).second.push_back( n1 );
8656 (*insertMapIt).second.push_back( n2 );
8658 // add node to be inserted into elem
8659 (*insertMapIt).second.push_back( nIns );
8660 next[ 1 - intoBord ] = true;
8663 // go to the next segment
8664 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8665 if ( next[ iBord ] ) {
8666 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8668 nPrev[ iBord ] = *nIt[ iBord ];
8669 nIt[ iBord ]++; i[ iBord ]++;
8673 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8675 // perform insertion of nodes into elements
8677 for (insertMapIt = insertMap.begin();
8678 insertMapIt != insertMap.end();
8681 const SMDS_MeshElement* elem = (*insertMapIt).first;
8682 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8683 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8684 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8686 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8688 if ( !theSideIsFreeBorder ) {
8689 // look for and insert nodes into the faces adjacent to elem
8691 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8693 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8698 if (toCreatePolyedrs) {
8699 // perform insertion into the links of adjacent volumes
8700 UpdateVolumes(n1, n2, nodeList);
8706 } // end: insert new nodes
8708 MergeNodes ( nodeGroupsToMerge );
8713 //=======================================================================
8714 //function : InsertNodesIntoLink
8715 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
8716 // and theBetweenNode2 and split theElement
8717 //=======================================================================
8719 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
8720 const SMDS_MeshNode* theBetweenNode1,
8721 const SMDS_MeshNode* theBetweenNode2,
8722 list<const SMDS_MeshNode*>& theNodesToInsert,
8723 const bool toCreatePoly)
8725 if ( theFace->GetType() != SMDSAbs_Face ) return;
8727 // find indices of 2 link nodes and of the rest nodes
8728 int iNode = 0, il1, il2, i3, i4;
8729 il1 = il2 = i3 = i4 = -1;
8730 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8731 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8733 if(theFace->IsQuadratic()) {
8734 const SMDS_VtkFace* F =
8735 dynamic_cast<const SMDS_VtkFace*>(theFace);
8736 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8737 // use special nodes iterator
8738 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8739 while( anIter->more() ) {
8740 const SMDS_MeshNode* n = cast2Node(anIter->next());
8741 if ( n == theBetweenNode1 )
8743 else if ( n == theBetweenNode2 )
8749 nodes[ iNode++ ] = n;
8753 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8754 while ( nodeIt->more() ) {
8755 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8756 if ( n == theBetweenNode1 )
8758 else if ( n == theBetweenNode2 )
8764 nodes[ iNode++ ] = n;
8767 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8770 // arrange link nodes to go one after another regarding the face orientation
8771 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8772 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8777 aNodesToInsert.reverse();
8779 // check that not link nodes of a quadrangles are in good order
8780 int nbFaceNodes = theFace->NbNodes();
8781 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8787 if (toCreatePoly || theFace->IsPoly()) {
8790 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8792 // add nodes of face up to first node of link
8795 if(theFace->IsQuadratic()) {
8796 const SMDS_VtkFace* F =
8797 dynamic_cast<const SMDS_VtkFace*>(theFace);
8798 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8799 // use special nodes iterator
8800 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8801 while( anIter->more() && !isFLN ) {
8802 const SMDS_MeshNode* n = cast2Node(anIter->next());
8803 poly_nodes[iNode++] = n;
8804 if (n == nodes[il1]) {
8808 // add nodes to insert
8809 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8810 for (; nIt != aNodesToInsert.end(); nIt++) {
8811 poly_nodes[iNode++] = *nIt;
8813 // add nodes of face starting from last node of link
8814 while ( anIter->more() ) {
8815 poly_nodes[iNode++] = cast2Node(anIter->next());
8819 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8820 while ( nodeIt->more() && !isFLN ) {
8821 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8822 poly_nodes[iNode++] = n;
8823 if (n == nodes[il1]) {
8827 // add nodes to insert
8828 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8829 for (; nIt != aNodesToInsert.end(); nIt++) {
8830 poly_nodes[iNode++] = *nIt;
8832 // add nodes of face starting from last node of link
8833 while ( nodeIt->more() ) {
8834 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8835 poly_nodes[iNode++] = n;
8839 // edit or replace the face
8840 SMESHDS_Mesh *aMesh = GetMeshDS();
8842 if (theFace->IsPoly()) {
8843 aMesh->ChangePolygonNodes(theFace, poly_nodes);
8846 int aShapeId = FindShape( theFace );
8848 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8849 myLastCreatedElems.Append(newElem);
8850 if ( aShapeId && newElem )
8851 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8853 aMesh->RemoveElement(theFace);
8858 SMESHDS_Mesh *aMesh = GetMeshDS();
8859 if( !theFace->IsQuadratic() ) {
8861 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8862 int nbLinkNodes = 2 + aNodesToInsert.size();
8863 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8864 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8865 linkNodes[ 0 ] = nodes[ il1 ];
8866 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8867 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8868 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8869 linkNodes[ iNode++ ] = *nIt;
8871 // decide how to split a quadrangle: compare possible variants
8872 // and choose which of splits to be a quadrangle
8873 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8874 if ( nbFaceNodes == 3 ) {
8875 iBestQuad = nbSplits;
8878 else if ( nbFaceNodes == 4 ) {
8879 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8880 double aBestRate = DBL_MAX;
8881 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8883 double aBadRate = 0;
8884 // evaluate elements quality
8885 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8886 if ( iSplit == iQuad ) {
8887 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8891 aBadRate += getBadRate( &quad, aCrit );
8894 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8896 nodes[ iSplit < iQuad ? i4 : i3 ]);
8897 aBadRate += getBadRate( &tria, aCrit );
8901 if ( aBadRate < aBestRate ) {
8903 aBestRate = aBadRate;
8908 // create new elements
8909 int aShapeId = FindShape( theFace );
8912 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8913 SMDS_MeshElement* newElem = 0;
8914 if ( iSplit == iBestQuad )
8915 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8920 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8922 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8923 myLastCreatedElems.Append(newElem);
8924 if ( aShapeId && newElem )
8925 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8928 // change nodes of theFace
8929 const SMDS_MeshNode* newNodes[ 4 ];
8930 newNodes[ 0 ] = linkNodes[ i1 ];
8931 newNodes[ 1 ] = linkNodes[ i2 ];
8932 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8933 newNodes[ 3 ] = nodes[ i4 ];
8934 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8935 const SMDS_MeshElement* newElem = 0;
8936 if (iSplit == iBestQuad)
8937 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8939 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8940 myLastCreatedElems.Append(newElem);
8941 if ( aShapeId && newElem )
8942 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8943 } // end if(!theFace->IsQuadratic())
8944 else { // theFace is quadratic
8945 // we have to split theFace on simple triangles and one simple quadrangle
8947 int nbshift = tmp*2;
8948 // shift nodes in nodes[] by nbshift
8950 for(i=0; i<nbshift; i++) {
8951 const SMDS_MeshNode* n = nodes[0];
8952 for(j=0; j<nbFaceNodes-1; j++) {
8953 nodes[j] = nodes[j+1];
8955 nodes[nbFaceNodes-1] = n;
8957 il1 = il1 - nbshift;
8958 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8959 // n0 n1 n2 n0 n1 n2
8960 // +-----+-----+ +-----+-----+
8969 // create new elements
8970 int aShapeId = FindShape( theFace );
8973 if(nbFaceNodes==6) { // quadratic triangle
8974 SMDS_MeshElement* newElem =
8975 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8976 myLastCreatedElems.Append(newElem);
8977 if ( aShapeId && newElem )
8978 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8979 if(theFace->IsMediumNode(nodes[il1])) {
8980 // create quadrangle
8981 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8982 myLastCreatedElems.Append(newElem);
8983 if ( aShapeId && newElem )
8984 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8990 // create quadrangle
8991 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8992 myLastCreatedElems.Append(newElem);
8993 if ( aShapeId && newElem )
8994 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9000 else { // nbFaceNodes==8 - quadratic quadrangle
9001 SMDS_MeshElement* newElem =
9002 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9003 myLastCreatedElems.Append(newElem);
9004 if ( aShapeId && newElem )
9005 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9006 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9007 myLastCreatedElems.Append(newElem);
9008 if ( aShapeId && newElem )
9009 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9010 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9011 myLastCreatedElems.Append(newElem);
9012 if ( aShapeId && newElem )
9013 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9014 if(theFace->IsMediumNode(nodes[il1])) {
9015 // create quadrangle
9016 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9017 myLastCreatedElems.Append(newElem);
9018 if ( aShapeId && newElem )
9019 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9025 // create quadrangle
9026 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9027 myLastCreatedElems.Append(newElem);
9028 if ( aShapeId && newElem )
9029 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9035 // create needed triangles using n1,n2,n3 and inserted nodes
9036 int nbn = 2 + aNodesToInsert.size();
9037 //const SMDS_MeshNode* aNodes[nbn];
9038 vector<const SMDS_MeshNode*> aNodes(nbn);
9039 aNodes[0] = nodes[n1];
9040 aNodes[nbn-1] = nodes[n2];
9041 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9042 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9043 aNodes[iNode++] = *nIt;
9045 for(i=1; i<nbn; i++) {
9046 SMDS_MeshElement* newElem =
9047 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9048 myLastCreatedElems.Append(newElem);
9049 if ( aShapeId && newElem )
9050 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9054 aMesh->RemoveElement(theFace);
9057 //=======================================================================
9058 //function : UpdateVolumes
9060 //=======================================================================
9061 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9062 const SMDS_MeshNode* theBetweenNode2,
9063 list<const SMDS_MeshNode*>& theNodesToInsert)
9065 myLastCreatedElems.Clear();
9066 myLastCreatedNodes.Clear();
9068 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9069 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9070 const SMDS_MeshElement* elem = invElemIt->next();
9072 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9073 SMDS_VolumeTool aVolume (elem);
9074 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9077 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9078 int iface, nbFaces = aVolume.NbFaces();
9079 vector<const SMDS_MeshNode *> poly_nodes;
9080 vector<int> quantities (nbFaces);
9082 for (iface = 0; iface < nbFaces; iface++) {
9083 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9084 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9085 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9087 for (int inode = 0; inode < nbFaceNodes; inode++) {
9088 poly_nodes.push_back(faceNodes[inode]);
9090 if (nbInserted == 0) {
9091 if (faceNodes[inode] == theBetweenNode1) {
9092 if (faceNodes[inode + 1] == theBetweenNode2) {
9093 nbInserted = theNodesToInsert.size();
9095 // add nodes to insert
9096 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9097 for (; nIt != theNodesToInsert.end(); nIt++) {
9098 poly_nodes.push_back(*nIt);
9102 else if (faceNodes[inode] == theBetweenNode2) {
9103 if (faceNodes[inode + 1] == theBetweenNode1) {
9104 nbInserted = theNodesToInsert.size();
9106 // add nodes to insert in reversed order
9107 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9109 for (; nIt != theNodesToInsert.begin(); nIt--) {
9110 poly_nodes.push_back(*nIt);
9112 poly_nodes.push_back(*nIt);
9119 quantities[iface] = nbFaceNodes + nbInserted;
9122 // Replace or update the volume
9123 SMESHDS_Mesh *aMesh = GetMeshDS();
9125 if (elem->IsPoly()) {
9126 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9130 int aShapeId = FindShape( elem );
9132 SMDS_MeshElement* newElem =
9133 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9134 myLastCreatedElems.Append(newElem);
9135 if (aShapeId && newElem)
9136 aMesh->SetMeshElementOnShape(newElem, aShapeId);
9138 aMesh->RemoveElement(elem);
9145 //================================================================================
9147 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9149 //================================================================================
9151 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9152 vector<const SMDS_MeshNode *> & nodes,
9153 vector<int> & nbNodeInFaces )
9156 nbNodeInFaces.clear();
9157 SMDS_VolumeTool vTool ( elem );
9158 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9160 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9161 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9162 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9167 //=======================================================================
9169 * \brief Convert elements contained in a sub-mesh to quadratic
9170 * \return int - nb of checked elements
9172 //=======================================================================
9174 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9175 SMESH_MesherHelper& theHelper,
9176 const bool theForce3d)
9179 if( !theSm ) return nbElem;
9181 vector<int> nbNodeInFaces;
9182 vector<const SMDS_MeshNode *> nodes;
9183 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9184 while(ElemItr->more())
9187 const SMDS_MeshElement* elem = ElemItr->next();
9188 if( !elem ) continue;
9190 // analyse a necessity of conversion
9191 const SMDSAbs_ElementType aType = elem->GetType();
9192 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9194 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9195 bool hasCentralNodes = false;
9196 if ( elem->IsQuadratic() )
9199 switch ( aGeomType ) {
9200 case SMDSEntity_Quad_Triangle:
9201 case SMDSEntity_Quad_Quadrangle:
9202 case SMDSEntity_Quad_Hexa:
9203 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9205 case SMDSEntity_BiQuad_Triangle:
9206 case SMDSEntity_BiQuad_Quadrangle:
9207 case SMDSEntity_TriQuad_Hexa:
9208 alreadyOK = theHelper.GetIsBiQuadratic();
9209 hasCentralNodes = true;
9214 // take into account already present modium nodes
9216 case SMDSAbs_Volume:
9217 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9219 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9221 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9227 // get elem data needed to re-create it
9229 const int id = elem->GetID();
9230 const int nbNodes = elem->NbCornerNodes();
9231 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9232 if ( aGeomType == SMDSEntity_Polyhedra )
9233 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9234 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9235 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9237 // remove a linear element
9238 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9240 // remove central nodes of biquadratic elements (biquad->quad convertion)
9241 if ( hasCentralNodes )
9242 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9243 if ( nodes[i]->NbInverseElements() == 0 )
9244 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9246 const SMDS_MeshElement* NewElem = 0;
9252 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9260 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9263 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9266 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9270 case SMDSAbs_Volume :
9274 case SMDSEntity_Tetra:
9275 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9277 case SMDSEntity_Pyramid:
9278 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9280 case SMDSEntity_Penta:
9281 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9283 case SMDSEntity_Hexa:
9284 case SMDSEntity_Quad_Hexa:
9285 case SMDSEntity_TriQuad_Hexa:
9286 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9287 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9289 case SMDSEntity_Hexagonal_Prism:
9291 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9298 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9299 if( NewElem && NewElem->getshapeId() < 1 )
9300 theSm->AddElement( NewElem );
9304 //=======================================================================
9305 //function : ConvertToQuadratic
9307 //=======================================================================
9309 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9311 SMESHDS_Mesh* meshDS = GetMeshDS();
9313 SMESH_MesherHelper aHelper(*myMesh);
9315 aHelper.SetIsQuadratic( true );
9316 aHelper.SetIsBiQuadratic( theToBiQuad );
9317 aHelper.SetElementsOnShape(true);
9318 aHelper.ToFixNodeParameters( true );
9320 // convert elements assigned to sub-meshes
9321 int nbCheckedElems = 0;
9322 if ( myMesh->HasShapeToMesh() )
9324 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9326 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9327 while ( smIt->more() ) {
9328 SMESH_subMesh* sm = smIt->next();
9329 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9330 aHelper.SetSubShape( sm->GetSubShape() );
9331 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9337 // convert elements NOT assigned to sub-meshes
9338 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9339 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9341 aHelper.SetElementsOnShape(false);
9342 SMESHDS_SubMesh *smDS = 0;
9345 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9346 while( aEdgeItr->more() )
9348 const SMDS_MeshEdge* edge = aEdgeItr->next();
9349 if ( !edge->IsQuadratic() )
9351 int id = edge->GetID();
9352 const SMDS_MeshNode* n1 = edge->GetNode(0);
9353 const SMDS_MeshNode* n2 = edge->GetNode(1);
9355 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9357 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9358 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9362 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9367 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9368 while( aFaceItr->more() )
9370 const SMDS_MeshFace* face = aFaceItr->next();
9371 if ( !face ) continue;
9373 const SMDSAbs_EntityType type = face->GetEntityType();
9377 case SMDSEntity_Quad_Triangle:
9378 case SMDSEntity_Quad_Quadrangle:
9379 alreadyOK = !theToBiQuad;
9380 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9382 case SMDSEntity_BiQuad_Triangle:
9383 case SMDSEntity_BiQuad_Quadrangle:
9384 alreadyOK = theToBiQuad;
9385 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9387 default: alreadyOK = false;
9392 const int id = face->GetID();
9393 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9395 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9397 SMDS_MeshFace * NewFace = 0;
9400 case SMDSEntity_Triangle:
9401 case SMDSEntity_Quad_Triangle:
9402 case SMDSEntity_BiQuad_Triangle:
9403 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9404 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9405 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9408 case SMDSEntity_Quadrangle:
9409 case SMDSEntity_Quad_Quadrangle:
9410 case SMDSEntity_BiQuad_Quadrangle:
9411 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9412 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9413 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9417 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9419 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9423 vector<int> nbNodeInFaces;
9424 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9425 while(aVolumeItr->more())
9427 const SMDS_MeshVolume* volume = aVolumeItr->next();
9428 if ( !volume ) continue;
9430 const SMDSAbs_EntityType type = volume->GetEntityType();
9431 if ( volume->IsQuadratic() )
9436 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9437 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9438 default: alreadyOK = true;
9442 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9446 const int id = volume->GetID();
9447 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9448 if ( type == SMDSEntity_Polyhedra )
9449 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9450 else if ( type == SMDSEntity_Hexagonal_Prism )
9451 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9453 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9455 SMDS_MeshVolume * NewVolume = 0;
9458 case SMDSEntity_Tetra:
9459 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9461 case SMDSEntity_Hexa:
9462 case SMDSEntity_Quad_Hexa:
9463 case SMDSEntity_TriQuad_Hexa:
9464 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9465 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9466 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9467 if ( nodes[i]->NbInverseElements() == 0 )
9468 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9470 case SMDSEntity_Pyramid:
9471 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9472 nodes[3], nodes[4], id, theForce3d);
9474 case SMDSEntity_Penta:
9475 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9476 nodes[3], nodes[4], nodes[5], id, theForce3d);
9478 case SMDSEntity_Hexagonal_Prism:
9480 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9482 ReplaceElemInGroups(volume, NewVolume, meshDS);
9487 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9488 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9489 // aHelper.FixQuadraticElements(myError);
9490 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9494 //================================================================================
9496 * \brief Makes given elements quadratic
9497 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9498 * \param theElements - elements to make quadratic
9500 //================================================================================
9502 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9503 TIDSortedElemSet& theElements,
9504 const bool theToBiQuad)
9506 if ( theElements.empty() ) return;
9508 // we believe that all theElements are of the same type
9509 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9511 // get all nodes shared by theElements
9512 TIDSortedNodeSet allNodes;
9513 TIDSortedElemSet::iterator eIt = theElements.begin();
9514 for ( ; eIt != theElements.end(); ++eIt )
9515 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9517 // complete theElements with elements of lower dim whose all nodes are in allNodes
9519 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9520 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9521 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9522 for ( ; nIt != allNodes.end(); ++nIt )
9524 const SMDS_MeshNode* n = *nIt;
9525 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9526 while ( invIt->more() )
9528 const SMDS_MeshElement* e = invIt->next();
9529 const SMDSAbs_ElementType type = e->GetType();
9530 if ( e->IsQuadratic() )
9532 quadAdjacentElems[ type ].insert( e );
9535 switch ( e->GetEntityType() ) {
9536 case SMDSEntity_Quad_Triangle:
9537 case SMDSEntity_Quad_Quadrangle:
9538 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9539 case SMDSEntity_BiQuad_Triangle:
9540 case SMDSEntity_BiQuad_Quadrangle:
9541 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9542 default: alreadyOK = true;
9547 if ( type >= elemType )
9548 continue; // same type or more complex linear element
9550 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9551 continue; // e is already checked
9555 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9556 while ( nodeIt->more() && allIn )
9557 allIn = allNodes.count( nodeIt->next() );
9559 theElements.insert(e );
9563 SMESH_MesherHelper helper(*myMesh);
9564 helper.SetIsQuadratic( true );
9565 helper.SetIsBiQuadratic( theToBiQuad );
9567 // add links of quadratic adjacent elements to the helper
9569 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9570 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9571 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9573 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9575 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9576 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9577 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9579 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9581 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9582 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9583 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9585 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9588 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9590 SMESHDS_Mesh* meshDS = GetMeshDS();
9591 SMESHDS_SubMesh* smDS = 0;
9592 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9594 const SMDS_MeshElement* elem = *eIt;
9597 int nbCentralNodes = 0;
9598 switch ( elem->GetEntityType() ) {
9599 // linear convertible
9600 case SMDSEntity_Edge:
9601 case SMDSEntity_Triangle:
9602 case SMDSEntity_Quadrangle:
9603 case SMDSEntity_Tetra:
9604 case SMDSEntity_Pyramid:
9605 case SMDSEntity_Hexa:
9606 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9607 // quadratic that can become bi-quadratic
9608 case SMDSEntity_Quad_Triangle:
9609 case SMDSEntity_Quad_Quadrangle:
9610 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9612 case SMDSEntity_BiQuad_Triangle:
9613 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9614 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9616 default: alreadyOK = true;
9618 if ( alreadyOK ) continue;
9620 const SMDSAbs_ElementType type = elem->GetType();
9621 const int id = elem->GetID();
9622 const int nbNodes = elem->NbCornerNodes();
9623 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9625 helper.SetSubShape( elem->getshapeId() );
9627 if ( !smDS || !smDS->Contains( elem ))
9628 smDS = meshDS->MeshElements( elem->getshapeId() );
9629 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9631 SMDS_MeshElement * newElem = 0;
9634 case 4: // cases for most frequently used element types go first (for optimization)
9635 if ( type == SMDSAbs_Volume )
9636 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9638 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9641 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9642 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9645 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9648 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9651 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9652 nodes[4], id, theForce3d);
9655 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9656 nodes[4], nodes[5], id, theForce3d);
9660 ReplaceElemInGroups( elem, newElem, meshDS);
9661 if( newElem && smDS )
9662 smDS->AddElement( newElem );
9664 // remove central nodes
9665 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9666 if ( nodes[i]->NbInverseElements() == 0 )
9667 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9669 } // loop on theElements
9672 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9673 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9674 // helper.FixQuadraticElements( myError );
9675 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9679 //=======================================================================
9681 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9682 * \return int - nb of checked elements
9684 //=======================================================================
9686 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9687 SMDS_ElemIteratorPtr theItr,
9688 const int theShapeID)
9691 SMESHDS_Mesh* meshDS = GetMeshDS();
9692 ElemFeatures elemType;
9693 vector<const SMDS_MeshNode *> nodes;
9695 while( theItr->more() )
9697 const SMDS_MeshElement* elem = theItr->next();
9699 if( elem && elem->IsQuadratic())
9702 int nbCornerNodes = elem->NbCornerNodes();
9703 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9705 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9707 //remove a quadratic element
9708 if ( !theSm || !theSm->Contains( elem ))
9709 theSm = meshDS->MeshElements( elem->getshapeId() );
9710 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9712 // remove medium nodes
9713 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9714 if ( nodes[i]->NbInverseElements() == 0 )
9715 meshDS->RemoveFreeNode( nodes[i], theSm );
9717 // add a linear element
9718 nodes.resize( nbCornerNodes );
9719 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9720 ReplaceElemInGroups(elem, newElem, meshDS);
9721 if( theSm && newElem )
9722 theSm->AddElement( newElem );
9728 //=======================================================================
9729 //function : ConvertFromQuadratic
9731 //=======================================================================
9733 bool SMESH_MeshEditor::ConvertFromQuadratic()
9735 int nbCheckedElems = 0;
9736 if ( myMesh->HasShapeToMesh() )
9738 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9740 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9741 while ( smIt->more() ) {
9742 SMESH_subMesh* sm = smIt->next();
9743 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9744 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9750 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9751 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9753 SMESHDS_SubMesh *aSM = 0;
9754 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9762 //================================================================================
9764 * \brief Return true if all medium nodes of the element are in the node set
9766 //================================================================================
9768 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9770 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9771 if ( !nodeSet.count( elem->GetNode(i) ))
9777 //================================================================================
9779 * \brief Makes given elements linear
9781 //================================================================================
9783 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9785 if ( theElements.empty() ) return;
9787 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9788 set<int> mediumNodeIDs;
9789 TIDSortedElemSet::iterator eIt = theElements.begin();
9790 for ( ; eIt != theElements.end(); ++eIt )
9792 const SMDS_MeshElement* e = *eIt;
9793 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9794 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9797 // replace given elements by linear ones
9798 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9799 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9801 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9802 // except those elements sharing medium nodes of quadratic element whose medium nodes
9803 // are not all in mediumNodeIDs
9805 // get remaining medium nodes
9806 TIDSortedNodeSet mediumNodes;
9807 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9808 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9809 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9810 mediumNodes.insert( mediumNodes.end(), n );
9812 // find more quadratic elements to convert
9813 TIDSortedElemSet moreElemsToConvert;
9814 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9815 for ( ; nIt != mediumNodes.end(); ++nIt )
9817 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9818 while ( invIt->more() )
9820 const SMDS_MeshElement* e = invIt->next();
9821 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9823 // find a more complex element including e and
9824 // whose medium nodes are not in mediumNodes
9825 bool complexFound = false;
9826 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9828 SMDS_ElemIteratorPtr invIt2 =
9829 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9830 while ( invIt2->more() )
9832 const SMDS_MeshElement* eComplex = invIt2->next();
9833 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9835 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9836 if ( nbCommonNodes == e->NbNodes())
9838 complexFound = true;
9839 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9845 if ( !complexFound )
9846 moreElemsToConvert.insert( e );
9850 elemIt = elemSetIterator( moreElemsToConvert );
9851 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9854 //=======================================================================
9855 //function : SewSideElements
9857 //=======================================================================
9859 SMESH_MeshEditor::Sew_Error
9860 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9861 TIDSortedElemSet& theSide2,
9862 const SMDS_MeshNode* theFirstNode1,
9863 const SMDS_MeshNode* theFirstNode2,
9864 const SMDS_MeshNode* theSecondNode1,
9865 const SMDS_MeshNode* theSecondNode2)
9867 myLastCreatedElems.Clear();
9868 myLastCreatedNodes.Clear();
9870 MESSAGE ("::::SewSideElements()");
9871 if ( theSide1.size() != theSide2.size() )
9872 return SEW_DIFF_NB_OF_ELEMENTS;
9874 Sew_Error aResult = SEW_OK;
9876 // 1. Build set of faces representing each side
9877 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9878 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9880 // =======================================================================
9881 // 1. Build set of faces representing each side:
9882 // =======================================================================
9883 // a. build set of nodes belonging to faces
9884 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9885 // c. create temporary faces representing side of volumes if correspondent
9886 // face does not exist
9888 SMESHDS_Mesh* aMesh = GetMeshDS();
9889 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9890 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9891 TIDSortedElemSet faceSet1, faceSet2;
9892 set<const SMDS_MeshElement*> volSet1, volSet2;
9893 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9894 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9895 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9896 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9897 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9898 int iSide, iFace, iNode;
9900 list<const SMDS_MeshElement* > tempFaceList;
9901 for ( iSide = 0; iSide < 2; iSide++ ) {
9902 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9903 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9904 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9905 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9906 set<const SMDS_MeshElement*>::iterator vIt;
9907 TIDSortedElemSet::iterator eIt;
9908 set<const SMDS_MeshNode*>::iterator nIt;
9910 // check that given nodes belong to given elements
9911 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9912 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9913 int firstIndex = -1, secondIndex = -1;
9914 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9915 const SMDS_MeshElement* elem = *eIt;
9916 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9917 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9918 if ( firstIndex > -1 && secondIndex > -1 ) break;
9920 if ( firstIndex < 0 || secondIndex < 0 ) {
9921 // we can simply return until temporary faces created
9922 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9925 // -----------------------------------------------------------
9926 // 1a. Collect nodes of existing faces
9927 // and build set of face nodes in order to detect missing
9928 // faces corresponding to sides of volumes
9929 // -----------------------------------------------------------
9931 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9933 // loop on the given element of a side
9934 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9935 //const SMDS_MeshElement* elem = *eIt;
9936 const SMDS_MeshElement* elem = *eIt;
9937 if ( elem->GetType() == SMDSAbs_Face ) {
9938 faceSet->insert( elem );
9939 set <const SMDS_MeshNode*> faceNodeSet;
9940 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9941 while ( nodeIt->more() ) {
9942 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9943 nodeSet->insert( n );
9944 faceNodeSet.insert( n );
9946 setOfFaceNodeSet.insert( faceNodeSet );
9948 else if ( elem->GetType() == SMDSAbs_Volume )
9949 volSet->insert( elem );
9951 // ------------------------------------------------------------------------------
9952 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9953 // ------------------------------------------------------------------------------
9955 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9956 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9957 while ( fIt->more() ) { // loop on faces sharing a node
9958 const SMDS_MeshElement* f = fIt->next();
9959 if ( faceSet->find( f ) == faceSet->end() ) {
9960 // check if all nodes are in nodeSet and
9961 // complete setOfFaceNodeSet if they are
9962 set <const SMDS_MeshNode*> faceNodeSet;
9963 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9964 bool allInSet = true;
9965 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9966 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9967 if ( nodeSet->find( n ) == nodeSet->end() )
9970 faceNodeSet.insert( n );
9973 faceSet->insert( f );
9974 setOfFaceNodeSet.insert( faceNodeSet );
9980 // -------------------------------------------------------------------------
9981 // 1c. Create temporary faces representing sides of volumes if correspondent
9982 // face does not exist
9983 // -------------------------------------------------------------------------
9985 if ( !volSet->empty() ) {
9986 //int nodeSetSize = nodeSet->size();
9988 // loop on given volumes
9989 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9990 SMDS_VolumeTool vol (*vIt);
9991 // loop on volume faces: find free faces
9992 // --------------------------------------
9993 list<const SMDS_MeshElement* > freeFaceList;
9994 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9995 if ( !vol.IsFreeFace( iFace ))
9997 // check if there is already a face with same nodes in a face set
9998 const SMDS_MeshElement* aFreeFace = 0;
9999 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10000 int nbNodes = vol.NbFaceNodes( iFace );
10001 set <const SMDS_MeshNode*> faceNodeSet;
10002 vol.GetFaceNodes( iFace, faceNodeSet );
10003 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10005 // no such a face is given but it still can exist, check it
10006 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10007 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10009 if ( !aFreeFace ) {
10010 // create a temporary face
10011 if ( nbNodes == 3 ) {
10012 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10013 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10015 else if ( nbNodes == 4 ) {
10016 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10017 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10020 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10021 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10022 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10025 tempFaceList.push_back( aFreeFace );
10029 freeFaceList.push_back( aFreeFace );
10031 } // loop on faces of a volume
10033 // choose one of several free faces of a volume
10034 // --------------------------------------------
10035 if ( freeFaceList.size() > 1 ) {
10036 // choose a face having max nb of nodes shared by other elems of a side
10037 int maxNbNodes = -1;
10038 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10039 while ( fIt != freeFaceList.end() ) { // loop on free faces
10040 int nbSharedNodes = 0;
10041 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10042 while ( nodeIt->more() ) { // loop on free face nodes
10043 const SMDS_MeshNode* n =
10044 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10045 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10046 while ( invElemIt->more() ) {
10047 const SMDS_MeshElement* e = invElemIt->next();
10048 nbSharedNodes += faceSet->count( e );
10049 nbSharedNodes += elemSet->count( e );
10052 if ( nbSharedNodes > maxNbNodes ) {
10053 maxNbNodes = nbSharedNodes;
10054 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10056 else if ( nbSharedNodes == maxNbNodes ) {
10060 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10063 if ( freeFaceList.size() > 1 )
10065 // could not choose one face, use another way
10066 // choose a face most close to the bary center of the opposite side
10067 gp_XYZ aBC( 0., 0., 0. );
10068 set <const SMDS_MeshNode*> addedNodes;
10069 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10070 eIt = elemSet2->begin();
10071 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10072 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10073 while ( nodeIt->more() ) { // loop on free face nodes
10074 const SMDS_MeshNode* n =
10075 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10076 if ( addedNodes.insert( n ).second )
10077 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10080 aBC /= addedNodes.size();
10081 double minDist = DBL_MAX;
10082 fIt = freeFaceList.begin();
10083 while ( fIt != freeFaceList.end() ) { // loop on free faces
10085 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10086 while ( nodeIt->more() ) { // loop on free face nodes
10087 const SMDS_MeshNode* n =
10088 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10089 gp_XYZ p( n->X(),n->Y(),n->Z() );
10090 dist += ( aBC - p ).SquareModulus();
10092 if ( dist < minDist ) {
10094 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10097 fIt = freeFaceList.erase( fIt++ );
10100 } // choose one of several free faces of a volume
10102 if ( freeFaceList.size() == 1 ) {
10103 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10104 faceSet->insert( aFreeFace );
10105 // complete a node set with nodes of a found free face
10106 // for ( iNode = 0; iNode < ; iNode++ )
10107 // nodeSet->insert( fNodes[ iNode ] );
10110 } // loop on volumes of a side
10112 // // complete a set of faces if new nodes in a nodeSet appeared
10113 // // ----------------------------------------------------------
10114 // if ( nodeSetSize != nodeSet->size() ) {
10115 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10116 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10117 // while ( fIt->more() ) { // loop on faces sharing a node
10118 // const SMDS_MeshElement* f = fIt->next();
10119 // if ( faceSet->find( f ) == faceSet->end() ) {
10120 // // check if all nodes are in nodeSet and
10121 // // complete setOfFaceNodeSet if they are
10122 // set <const SMDS_MeshNode*> faceNodeSet;
10123 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10124 // bool allInSet = true;
10125 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10126 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10127 // if ( nodeSet->find( n ) == nodeSet->end() )
10128 // allInSet = false;
10130 // faceNodeSet.insert( n );
10132 // if ( allInSet ) {
10133 // faceSet->insert( f );
10134 // setOfFaceNodeSet.insert( faceNodeSet );
10140 } // Create temporary faces, if there are volumes given
10143 if ( faceSet1.size() != faceSet2.size() ) {
10144 // delete temporary faces: they are in reverseElements of actual nodes
10145 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10146 // while ( tmpFaceIt->more() )
10147 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10148 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10149 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10150 // aMesh->RemoveElement(*tmpFaceIt);
10151 MESSAGE("Diff nb of faces");
10152 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10155 // ============================================================
10156 // 2. Find nodes to merge:
10157 // bind a node to remove to a node to put instead
10158 // ============================================================
10160 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10161 if ( theFirstNode1 != theFirstNode2 )
10162 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10163 if ( theSecondNode1 != theSecondNode2 )
10164 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10166 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10167 set< long > linkIdSet; // links to process
10168 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10170 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10171 list< NLink > linkList[2];
10172 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10173 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10174 // loop on links in linkList; find faces by links and append links
10175 // of the found faces to linkList
10176 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10177 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10179 NLink link[] = { *linkIt[0], *linkIt[1] };
10180 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10181 if ( !linkIdSet.count( linkID ) )
10184 // by links, find faces in the face sets,
10185 // and find indices of link nodes in the found faces;
10186 // in a face set, there is only one or no face sharing a link
10187 // ---------------------------------------------------------------
10189 const SMDS_MeshElement* face[] = { 0, 0 };
10190 vector<const SMDS_MeshNode*> fnodes[2];
10191 int iLinkNode[2][2];
10192 TIDSortedElemSet avoidSet;
10193 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10194 const SMDS_MeshNode* n1 = link[iSide].first;
10195 const SMDS_MeshNode* n2 = link[iSide].second;
10196 //cout << "Side " << iSide << " ";
10197 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10198 // find a face by two link nodes
10199 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10200 *faceSetPtr[ iSide ], avoidSet,
10201 &iLinkNode[iSide][0],
10202 &iLinkNode[iSide][1] );
10203 if ( face[ iSide ])
10205 //cout << " F " << face[ iSide]->GetID() <<endl;
10206 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10207 // put face nodes to fnodes
10208 if ( face[ iSide ]->IsQuadratic() )
10210 // use interlaced nodes iterator
10211 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10212 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10213 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10214 while ( nIter->more() )
10215 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10219 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10220 face[ iSide ]->end_nodes() );
10222 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10226 // check similarity of elements of the sides
10227 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10228 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10229 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10230 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10233 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10235 break; // do not return because it's necessary to remove tmp faces
10238 // set nodes to merge
10239 // -------------------
10241 if ( face[0] && face[1] ) {
10242 const int nbNodes = face[0]->NbNodes();
10243 if ( nbNodes != face[1]->NbNodes() ) {
10244 MESSAGE("Diff nb of face nodes");
10245 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10246 break; // do not return because it s necessary to remove tmp faces
10248 bool reverse[] = { false, false }; // order of nodes in the link
10249 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10250 // analyse link orientation in faces
10251 int i1 = iLinkNode[ iSide ][ 0 ];
10252 int i2 = iLinkNode[ iSide ][ 1 ];
10253 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10255 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10256 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10257 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10259 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10260 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10263 // add other links of the faces to linkList
10264 // -----------------------------------------
10266 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10267 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10268 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10269 if ( !iter_isnew.second ) { // already in a set: no need to process
10270 linkIdSet.erase( iter_isnew.first );
10272 else // new in set == encountered for the first time: add
10274 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10275 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10276 linkList[0].push_back ( NLink( n1, n2 ));
10277 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10282 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10285 } // loop on link lists
10287 if ( aResult == SEW_OK &&
10288 ( //linkIt[0] != linkList[0].end() ||
10289 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10290 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10291 " " << (faceSetPtr[1]->empty()));
10292 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10295 // ====================================================================
10296 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10297 // ====================================================================
10299 // delete temporary faces
10300 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10301 // while ( tmpFaceIt->more() )
10302 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10303 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10304 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10305 aMesh->RemoveElement(*tmpFaceIt);
10307 if ( aResult != SEW_OK)
10310 list< int > nodeIDsToRemove;
10311 vector< const SMDS_MeshNode*> nodes;
10312 ElemFeatures elemType;
10314 // loop on nodes replacement map
10315 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10316 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10317 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10319 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10320 nodeIDsToRemove.push_back( nToRemove->GetID() );
10321 // loop on elements sharing nToRemove
10322 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10323 while ( invElemIt->more() ) {
10324 const SMDS_MeshElement* e = invElemIt->next();
10325 // get a new suite of nodes: make replacement
10326 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10327 nodes.resize( nbNodes );
10328 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10329 while ( nIt->more() ) {
10330 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10331 nnIt = nReplaceMap.find( n );
10332 if ( nnIt != nReplaceMap.end() ) {
10334 n = (*nnIt).second;
10338 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10339 // elemIDsToRemove.push_back( e->GetID() );
10343 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10344 aMesh->RemoveElement( e );
10346 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10348 AddToSameGroups( newElem, e, aMesh );
10349 if ( int aShapeId = e->getshapeId() )
10350 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10356 Remove( nodeIDsToRemove, true );
10361 //================================================================================
10363 * \brief Find corresponding nodes in two sets of faces
10364 * \param theSide1 - first face set
10365 * \param theSide2 - second first face
10366 * \param theFirstNode1 - a boundary node of set 1
10367 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10368 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10369 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10370 * \param nReplaceMap - output map of corresponding nodes
10371 * \return bool - is a success or not
10373 //================================================================================
10376 //#define DEBUG_MATCHING_NODES
10379 SMESH_MeshEditor::Sew_Error
10380 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10381 set<const SMDS_MeshElement*>& theSide2,
10382 const SMDS_MeshNode* theFirstNode1,
10383 const SMDS_MeshNode* theFirstNode2,
10384 const SMDS_MeshNode* theSecondNode1,
10385 const SMDS_MeshNode* theSecondNode2,
10386 TNodeNodeMap & nReplaceMap)
10388 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10390 nReplaceMap.clear();
10391 if ( theFirstNode1 != theFirstNode2 )
10392 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10393 if ( theSecondNode1 != theSecondNode2 )
10394 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10396 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10397 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10399 list< NLink > linkList[2];
10400 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10401 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10403 // loop on links in linkList; find faces by links and append links
10404 // of the found faces to linkList
10405 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10406 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10407 NLink link[] = { *linkIt[0], *linkIt[1] };
10408 if ( linkSet.find( link[0] ) == linkSet.end() )
10411 // by links, find faces in the face sets,
10412 // and find indices of link nodes in the found faces;
10413 // in a face set, there is only one or no face sharing a link
10414 // ---------------------------------------------------------------
10416 const SMDS_MeshElement* face[] = { 0, 0 };
10417 list<const SMDS_MeshNode*> notLinkNodes[2];
10418 //bool reverse[] = { false, false }; // order of notLinkNodes
10420 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10422 const SMDS_MeshNode* n1 = link[iSide].first;
10423 const SMDS_MeshNode* n2 = link[iSide].second;
10424 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10425 set< const SMDS_MeshElement* > facesOfNode1;
10426 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10428 // during a loop of the first node, we find all faces around n1,
10429 // during a loop of the second node, we find one face sharing both n1 and n2
10430 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10431 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10432 while ( fIt->more() ) { // loop on faces sharing a node
10433 const SMDS_MeshElement* f = fIt->next();
10434 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10435 ! facesOfNode1.insert( f ).second ) // f encounters twice
10437 if ( face[ iSide ] ) {
10438 MESSAGE( "2 faces per link " );
10439 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10442 faceSet->erase( f );
10444 // get not link nodes
10445 int nbN = f->NbNodes();
10446 if ( f->IsQuadratic() )
10448 nbNodes[ iSide ] = nbN;
10449 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10450 int i1 = f->GetNodeIndex( n1 );
10451 int i2 = f->GetNodeIndex( n2 );
10452 int iEnd = nbN, iBeg = -1, iDelta = 1;
10453 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10455 std::swap( iEnd, iBeg ); iDelta = -1;
10460 if ( i == iEnd ) i = iBeg + iDelta;
10461 if ( i == i1 ) break;
10462 nodes.push_back ( f->GetNode( i ) );
10468 // check similarity of elements of the sides
10469 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10470 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10471 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10472 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10475 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10479 // set nodes to merge
10480 // -------------------
10482 if ( face[0] && face[1] ) {
10483 if ( nbNodes[0] != nbNodes[1] ) {
10484 MESSAGE("Diff nb of face nodes");
10485 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10487 #ifdef DEBUG_MATCHING_NODES
10488 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10489 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10490 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10492 int nbN = nbNodes[0];
10494 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10495 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10496 for ( int i = 0 ; i < nbN - 2; ++i ) {
10497 #ifdef DEBUG_MATCHING_NODES
10498 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10500 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10504 // add other links of the face 1 to linkList
10505 // -----------------------------------------
10507 const SMDS_MeshElement* f0 = face[0];
10508 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10509 for ( int i = 0; i < nbN; i++ )
10511 const SMDS_MeshNode* n2 = f0->GetNode( i );
10512 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10513 linkSet.insert( SMESH_TLink( n1, n2 ));
10514 if ( !iter_isnew.second ) { // already in a set: no need to process
10515 linkSet.erase( iter_isnew.first );
10517 else // new in set == encountered for the first time: add
10519 #ifdef DEBUG_MATCHING_NODES
10520 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10521 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10523 linkList[0].push_back ( NLink( n1, n2 ));
10524 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10529 } // loop on link lists
10534 //================================================================================
10536 * \brief Create elements equal (on same nodes) to given ones
10537 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10538 * elements of the uppest dimension are duplicated.
10540 //================================================================================
10542 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10544 ClearLastCreated();
10545 SMESHDS_Mesh* mesh = GetMeshDS();
10547 // get an element type and an iterator over elements
10549 SMDSAbs_ElementType type;
10550 SMDS_ElemIteratorPtr elemIt;
10551 vector< const SMDS_MeshElement* > allElems;
10552 if ( theElements.empty() )
10554 if ( mesh->NbNodes() == 0 )
10556 // get most complex type
10557 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10558 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10559 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10561 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10562 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10567 // put all elements in the vector <allElems>
10568 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10569 elemIt = mesh->elementsIterator( type );
10570 while ( elemIt->more() )
10571 allElems.push_back( elemIt->next());
10572 elemIt = elemSetIterator( allElems );
10576 type = (*theElements.begin())->GetType();
10577 elemIt = elemSetIterator( theElements );
10580 // duplicate elements
10582 ElemFeatures elemType;
10584 vector< const SMDS_MeshNode* > nodes;
10585 while ( elemIt->more() )
10587 const SMDS_MeshElement* elem = elemIt->next();
10588 if ( elem->GetType() != type )
10591 elemType.Init( elem, /*basicOnly=*/false );
10592 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10594 AddElement( nodes, elemType );
10598 //================================================================================
10600 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10601 \param theElems - the list of elements (edges or faces) to be replicated
10602 The nodes for duplication could be found from these elements
10603 \param theNodesNot - list of nodes to NOT replicate
10604 \param theAffectedElems - the list of elements (cells and edges) to which the
10605 replicated nodes should be associated to.
10606 \return TRUE if operation has been completed successfully, FALSE otherwise
10608 //================================================================================
10610 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10611 const TIDSortedElemSet& theNodesNot,
10612 const TIDSortedElemSet& theAffectedElems )
10614 myLastCreatedElems.Clear();
10615 myLastCreatedNodes.Clear();
10617 if ( theElems.size() == 0 )
10620 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10625 TNodeNodeMap anOldNodeToNewNode;
10626 // duplicate elements and nodes
10627 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10628 // replce nodes by duplications
10629 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10633 //================================================================================
10635 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10636 \param theMeshDS - mesh instance
10637 \param theElems - the elements replicated or modified (nodes should be changed)
10638 \param theNodesNot - nodes to NOT replicate
10639 \param theNodeNodeMap - relation of old node to new created node
10640 \param theIsDoubleElem - flag os to replicate element or modify
10641 \return TRUE if operation has been completed successfully, FALSE otherwise
10643 //================================================================================
10645 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10646 const TIDSortedElemSet& theElems,
10647 const TIDSortedElemSet& theNodesNot,
10648 TNodeNodeMap& theNodeNodeMap,
10649 const bool theIsDoubleElem )
10651 MESSAGE("doubleNodes");
10652 // iterate through element and duplicate them (by nodes duplication)
10654 std::vector<const SMDS_MeshNode*> newNodes;
10655 ElemFeatures elemType;
10657 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10658 for ( ; elemItr != theElems.end(); ++elemItr )
10660 const SMDS_MeshElement* anElem = *elemItr;
10664 // duplicate nodes to duplicate element
10665 bool isDuplicate = false;
10666 newNodes.resize( anElem->NbNodes() );
10667 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10669 while ( anIter->more() )
10671 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10672 const SMDS_MeshNode* aNewNode = aCurrNode;
10673 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10674 if ( n2n != theNodeNodeMap.end() )
10676 aNewNode = n2n->second;
10678 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10681 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10682 copyPosition( aCurrNode, aNewNode );
10683 theNodeNodeMap[ aCurrNode ] = aNewNode;
10684 myLastCreatedNodes.Append( aNewNode );
10686 isDuplicate |= (aCurrNode != aNewNode);
10687 newNodes[ ind++ ] = aNewNode;
10689 if ( !isDuplicate )
10692 if ( theIsDoubleElem )
10693 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10695 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10702 //================================================================================
10704 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10705 \param theNodes - identifiers of nodes to be doubled
10706 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10707 nodes. If list of element identifiers is empty then nodes are doubled but
10708 they not assigned to elements
10709 \return TRUE if operation has been completed successfully, FALSE otherwise
10711 //================================================================================
10713 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10714 const std::list< int >& theListOfModifiedElems )
10716 MESSAGE("DoubleNodes");
10717 myLastCreatedElems.Clear();
10718 myLastCreatedNodes.Clear();
10720 if ( theListOfNodes.size() == 0 )
10723 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10727 // iterate through nodes and duplicate them
10729 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10731 std::list< int >::const_iterator aNodeIter;
10732 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10734 int aCurr = *aNodeIter;
10735 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10741 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10744 copyPosition( aNode, aNewNode );
10745 anOldNodeToNewNode[ aNode ] = aNewNode;
10746 myLastCreatedNodes.Append( aNewNode );
10750 // Create map of new nodes for modified elements
10752 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10754 std::list< int >::const_iterator anElemIter;
10755 for ( anElemIter = theListOfModifiedElems.begin();
10756 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10758 int aCurr = *anElemIter;
10759 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10763 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10765 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10767 while ( anIter->more() )
10769 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10770 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10772 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10773 aNodeArr[ ind++ ] = aNewNode;
10776 aNodeArr[ ind++ ] = aCurrNode;
10778 anElemToNodes[ anElem ] = aNodeArr;
10781 // Change nodes of elements
10783 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10784 anElemToNodesIter = anElemToNodes.begin();
10785 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10787 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10788 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10791 MESSAGE("ChangeElementNodes");
10792 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10801 //================================================================================
10803 \brief Check if element located inside shape
10804 \return TRUE if IN or ON shape, FALSE otherwise
10806 //================================================================================
10808 template<class Classifier>
10809 bool isInside(const SMDS_MeshElement* theElem,
10810 Classifier& theClassifier,
10811 const double theTol)
10813 gp_XYZ centerXYZ (0, 0, 0);
10814 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10815 while (aNodeItr->more())
10816 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10818 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10819 theClassifier.Perform(aPnt, theTol);
10820 TopAbs_State aState = theClassifier.State();
10821 return (aState == TopAbs_IN || aState == TopAbs_ON );
10824 //================================================================================
10826 * \brief Classifier of the 3D point on the TopoDS_Face
10827 * with interaface suitable for isInside()
10829 //================================================================================
10831 struct _FaceClassifier
10833 Extrema_ExtPS _extremum;
10834 BRepAdaptor_Surface _surface;
10835 TopAbs_State _state;
10837 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10839 _extremum.Initialize( _surface,
10840 _surface.FirstUParameter(), _surface.LastUParameter(),
10841 _surface.FirstVParameter(), _surface.LastVParameter(),
10842 _surface.Tolerance(), _surface.Tolerance() );
10844 void Perform(const gp_Pnt& aPnt, double theTol)
10847 _state = TopAbs_OUT;
10848 _extremum.Perform(aPnt);
10849 if ( _extremum.IsDone() )
10850 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10851 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10853 TopAbs_State State() const
10860 //================================================================================
10862 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10863 This method is the first step of DoubleNodeElemGroupsInRegion.
10864 \param theElems - list of groups of elements (edges or faces) to be replicated
10865 \param theNodesNot - list of groups of nodes not to replicated
10866 \param theShape - shape to detect affected elements (element which geometric center
10867 located on or inside shape). If the shape is null, detection is done on faces orientations
10868 (select elements with a gravity center on the side given by faces normals).
10869 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10870 The replicated nodes should be associated to affected elements.
10871 \return groups of affected elements
10872 \sa DoubleNodeElemGroupsInRegion()
10874 //================================================================================
10876 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10877 const TIDSortedElemSet& theNodesNot,
10878 const TopoDS_Shape& theShape,
10879 TIDSortedElemSet& theAffectedElems)
10881 if ( theShape.IsNull() )
10883 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10884 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10885 std::set<const SMDS_MeshElement*> edgesToCheck;
10886 alreadyCheckedNodes.clear();
10887 alreadyCheckedElems.clear();
10888 edgesToCheck.clear();
10890 // --- iterates on elements to be replicated and get elements by back references from their nodes
10892 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10894 for ( ielem=1; elemItr != theElems.end(); ++elemItr )
10896 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10897 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10900 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10901 MESSAGE("element " << ielem++ << " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10902 std::set<const SMDS_MeshNode*> nodesElem;
10904 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10905 while ( nodeItr->more() )
10907 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10908 nodesElem.insert(aNode);
10910 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10911 for (; nodit != nodesElem.end(); nodit++)
10913 MESSAGE(" noeud ");
10914 const SMDS_MeshNode* aNode = *nodit;
10915 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10917 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10919 alreadyCheckedNodes.insert(aNode);
10920 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10921 while ( backElemItr->more() )
10923 MESSAGE(" backelem ");
10924 const SMDS_MeshElement* curElem = backElemItr->next();
10925 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10927 if (theElems.find(curElem) != theElems.end())
10929 alreadyCheckedElems.insert(curElem);
10930 double x=0, y=0, z=0;
10932 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10933 while ( nodeItr2->more() )
10935 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10936 x += anotherNode->X();
10937 y += anotherNode->Y();
10938 z += anotherNode->Z();
10942 p.SetCoord( x/nb -aNode->X(),
10944 z/nb -aNode->Z() );
10945 MESSAGE(" check " << p.X() << " " << p.Y() << " " << p.Z());
10948 MESSAGE(" --- inserted")
10949 theAffectedElems.insert( curElem );
10951 else if (curElem->GetType() == SMDSAbs_Edge)
10952 edgesToCheck.insert(curElem);
10956 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10957 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10958 for( ; eit != edgesToCheck.end(); eit++)
10960 bool onside = true;
10961 const SMDS_MeshElement* anEdge = *eit;
10962 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10963 while ( nodeItr->more() )
10965 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10966 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10974 MESSAGE(" --- edge onside inserted")
10975 theAffectedElems.insert(anEdge);
10981 const double aTol = Precision::Confusion();
10982 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10983 auto_ptr<_FaceClassifier> aFaceClassifier;
10984 if ( theShape.ShapeType() == TopAbs_SOLID )
10986 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10987 bsc3d->PerformInfinitePoint(aTol);
10989 else if (theShape.ShapeType() == TopAbs_FACE )
10991 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10994 // iterates on indicated elements and get elements by back references from their nodes
10995 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10997 for ( ielem = 1; elemItr != theElems.end(); ++elemItr )
10999 MESSAGE("element " << ielem++);
11000 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11003 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11004 while ( nodeItr->more() )
11006 MESSAGE(" noeud ");
11007 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11008 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11010 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11011 while ( backElemItr->more() )
11013 MESSAGE(" backelem ");
11014 const SMDS_MeshElement* curElem = backElemItr->next();
11015 if ( curElem && theElems.find(curElem) == theElems.end() &&
11017 isInside( curElem, *bsc3d, aTol ) :
11018 isInside( curElem, *aFaceClassifier, aTol )))
11019 theAffectedElems.insert( curElem );
11027 //================================================================================
11029 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11030 \param theElems - group of of elements (edges or faces) to be replicated
11031 \param theNodesNot - group of nodes not to replicate
11032 \param theShape - shape to detect affected elements (element which geometric center
11033 located on or inside shape).
11034 The replicated nodes should be associated to affected elements.
11035 \return TRUE if operation has been completed successfully, FALSE otherwise
11037 //================================================================================
11039 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11040 const TIDSortedElemSet& theNodesNot,
11041 const TopoDS_Shape& theShape )
11043 if ( theShape.IsNull() )
11046 const double aTol = Precision::Confusion();
11047 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11048 auto_ptr<_FaceClassifier> aFaceClassifier;
11049 if ( theShape.ShapeType() == TopAbs_SOLID )
11051 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11052 bsc3d->PerformInfinitePoint(aTol);
11054 else if (theShape.ShapeType() == TopAbs_FACE )
11056 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11059 // iterates on indicated elements and get elements by back references from their nodes
11060 TIDSortedElemSet anAffected;
11061 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11062 for ( ; elemItr != theElems.end(); ++elemItr )
11064 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11068 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11069 while ( nodeItr->more() )
11071 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11072 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11074 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11075 while ( backElemItr->more() )
11077 const SMDS_MeshElement* curElem = backElemItr->next();
11078 if ( curElem && theElems.find(curElem) == theElems.end() &&
11080 isInside( curElem, *bsc3d, aTol ) :
11081 isInside( curElem, *aFaceClassifier, aTol )))
11082 anAffected.insert( curElem );
11086 return DoubleNodes( theElems, theNodesNot, anAffected );
11090 * \brief compute an oriented angle between two planes defined by four points.
11091 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11092 * @param p0 base of the rotation axe
11093 * @param p1 extremity of the rotation axe
11094 * @param g1 belongs to the first plane
11095 * @param g2 belongs to the second plane
11097 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11099 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11100 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11101 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11102 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11103 gp_Vec vref(p0, p1);
11106 gp_Vec n1 = vref.Crossed(v1);
11107 gp_Vec n2 = vref.Crossed(v2);
11109 return n2.AngleWithRef(n1, vref);
11111 catch ( Standard_Failure ) {
11113 return Max( v1.Magnitude(), v2.Magnitude() );
11117 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11118 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11119 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11120 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11121 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11122 * 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.
11123 * 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.
11124 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11125 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11126 * \param theElems - list of groups of volumes, where a group of volume is a set of
11127 * SMDS_MeshElements sorted by Id.
11128 * \param createJointElems - if TRUE, create the elements
11129 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11130 * the boundary between \a theDomains and the rest mesh
11131 * \return TRUE if operation has been completed successfully, FALSE otherwise
11133 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11134 bool createJointElems,
11135 bool onAllBoundaries)
11137 MESSAGE("----------------------------------------------");
11138 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11139 MESSAGE("----------------------------------------------");
11141 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11142 meshDS->BuildDownWardConnectivity(true);
11144 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11146 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11147 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11148 // build the list of nodes shared by 2 or more domains, with their domain indexes
11150 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11151 std::map<int,int>celldom; // cell vtkId --> domain
11152 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11153 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11154 faceDomains.clear();
11156 cellDomains.clear();
11157 nodeDomains.clear();
11158 std::map<int,int> emptyMap;
11159 std::set<int> emptySet;
11162 MESSAGE(".. Number of domains :"<<theElems.size());
11164 TIDSortedElemSet theRestDomElems;
11165 const int iRestDom = -1;
11166 const int idom0 = onAllBoundaries ? iRestDom : 0;
11167 const int nbDomains = theElems.size();
11169 // Check if the domains do not share an element
11170 for (int idom = 0; idom < nbDomains-1; idom++)
11172 // MESSAGE("... Check of domain #" << idom);
11173 const TIDSortedElemSet& domain = theElems[idom];
11174 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11175 for (; elemItr != domain.end(); ++elemItr)
11177 const SMDS_MeshElement* anElem = *elemItr;
11178 int idombisdeb = idom + 1 ;
11179 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
11181 const TIDSortedElemSet& domainbis = theElems[idombis];
11182 if ( domainbis.count(anElem) )
11184 MESSAGE(".... Domain #" << idom);
11185 MESSAGE(".... Domain #" << idombis);
11186 throw SALOME_Exception("The domains are not disjoint.");
11193 for (int idom = 0; idom < nbDomains; idom++)
11196 // --- build a map (face to duplicate --> volume to modify)
11197 // with all the faces shared by 2 domains (group of elements)
11198 // and corresponding volume of this domain, for each shared face.
11199 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11201 MESSAGE("... Neighbors of domain #" << idom);
11202 const TIDSortedElemSet& domain = theElems[idom];
11203 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11204 for (; elemItr != domain.end(); ++elemItr)
11206 const SMDS_MeshElement* anElem = *elemItr;
11209 int vtkId = anElem->getVtkId();
11210 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11211 int neighborsVtkIds[NBMAXNEIGHBORS];
11212 int downIds[NBMAXNEIGHBORS];
11213 unsigned char downTypes[NBMAXNEIGHBORS];
11214 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11215 for (int n = 0; n < nbNeighbors; n++)
11217 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11218 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11219 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11222 for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11224 // MESSAGE("Domain " << idombis);
11225 const TIDSortedElemSet& domainbis = theElems[idombis];
11226 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11228 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11230 DownIdType face(downIds[n], downTypes[n]);
11231 if (!faceDomains[face].count(idom))
11233 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11234 celldom[vtkId] = idom;
11235 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11239 theRestDomElems.insert( elem );
11240 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11241 celldom[neighborsVtkIds[n]] = iRestDom;
11249 //MESSAGE("Number of shared faces " << faceDomains.size());
11250 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11252 // --- explore the shared faces domain by domain,
11253 // explore the nodes of the face and see if they belong to a cell in the domain,
11254 // which has only a node or an edge on the border (not a shared face)
11256 for (int idomain = idom0; idomain < nbDomains; idomain++)
11258 //MESSAGE("Domain " << idomain);
11259 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11260 itface = faceDomains.begin();
11261 for (; itface != faceDomains.end(); ++itface)
11263 const std::map<int, int>& domvol = itface->second;
11264 if (!domvol.count(idomain))
11266 DownIdType face = itface->first;
11267 //MESSAGE(" --- face " << face.cellId);
11268 std::set<int> oldNodes;
11270 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11271 std::set<int>::iterator itn = oldNodes.begin();
11272 for (; itn != oldNodes.end(); ++itn)
11275 //MESSAGE(" node " << oldId);
11276 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11277 for (int i=0; i<l.ncells; i++)
11279 int vtkId = l.cells[i];
11280 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11281 if (!domain.count(anElem))
11283 int vtkType = grid->GetCellType(vtkId);
11284 int downId = grid->CellIdToDownId(vtkId);
11287 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11288 continue; // not OK at this stage of the algorithm:
11289 //no cells created after BuildDownWardConnectivity
11291 DownIdType aCell(downId, vtkType);
11292 cellDomains[aCell][idomain] = vtkId;
11293 celldom[vtkId] = idomain;
11294 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11300 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11301 // for each shared face, get the nodes
11302 // for each node, for each domain of the face, create a clone of the node
11304 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11305 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11306 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11308 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11309 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11310 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11312 MESSAGE(".. Duplication of the nodes");
11313 for (int idomain = idom0; idomain < nbDomains; idomain++)
11315 itface = faceDomains.begin();
11316 for (; itface != faceDomains.end(); ++itface)
11318 const std::map<int, int>& domvol = itface->second;
11319 if (!domvol.count(idomain))
11321 DownIdType face = itface->first;
11322 //MESSAGE(" --- face " << face.cellId);
11323 std::set<int> oldNodes;
11325 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11326 std::set<int>::iterator itn = oldNodes.begin();
11327 for (; itn != oldNodes.end(); ++itn)
11330 if (nodeDomains[oldId].empty())
11332 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11333 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11335 std::map<int, int>::const_iterator itdom = domvol.begin();
11336 for (; itdom != domvol.end(); ++itdom)
11338 int idom = itdom->first;
11339 //MESSAGE(" domain " << idom);
11340 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11342 if (nodeDomains[oldId].size() >= 2) // a multiple node
11344 vector<int> orderedDoms;
11345 //MESSAGE("multiple node " << oldId);
11346 if (mutipleNodes.count(oldId))
11347 orderedDoms = mutipleNodes[oldId];
11350 map<int,int>::iterator it = nodeDomains[oldId].begin();
11351 for (; it != nodeDomains[oldId].end(); ++it)
11352 orderedDoms.push_back(it->first);
11354 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11355 //stringstream txt;
11356 //for (int i=0; i<orderedDoms.size(); i++)
11357 // txt << orderedDoms[i] << " ";
11358 //MESSAGE("orderedDoms " << txt.str());
11359 mutipleNodes[oldId] = orderedDoms;
11361 double *coords = grid->GetPoint(oldId);
11362 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11363 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11364 int newId = newNode->getVtkId();
11365 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11366 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11373 MESSAGE(".. Creation of elements");
11374 for (int idomain = idom0; idomain < nbDomains; idomain++)
11376 itface = faceDomains.begin();
11377 for (; itface != faceDomains.end(); ++itface)
11379 std::map<int, int> domvol = itface->second;
11380 if (!domvol.count(idomain))
11382 DownIdType face = itface->first;
11383 //MESSAGE(" --- face " << face.cellId);
11384 std::set<int> oldNodes;
11386 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11387 int nbMultipleNodes = 0;
11388 std::set<int>::iterator itn = oldNodes.begin();
11389 for (; itn != oldNodes.end(); ++itn)
11392 if (mutipleNodes.count(oldId))
11395 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11397 //MESSAGE("multiple Nodes detected on a shared face");
11398 int downId = itface->first.cellId;
11399 unsigned char cellType = itface->first.cellType;
11400 // --- shared edge or shared face ?
11401 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11404 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11405 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11406 if (mutipleNodes.count(nodes[i]))
11407 if (!mutipleNodesToFace.count(nodes[i]))
11408 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11410 else // shared face (between two volumes)
11412 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11413 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11414 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11415 for (int ie =0; ie < nbEdges; ie++)
11418 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11419 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11421 vector<int> vn0 = mutipleNodes[nodes[0]];
11422 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11424 for (int i0 = 0; i0 < vn0.size(); i0++)
11425 for (int i1 = 0; i1 < vn1.size(); i1++)
11426 if (vn0[i0] == vn1[i1])
11427 doms.push_back(vn0[i0]);
11428 if (doms.size() >2)
11430 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11431 double *coords = grid->GetPoint(nodes[0]);
11432 gp_Pnt p0(coords[0], coords[1], coords[2]);
11433 coords = grid->GetPoint(nodes[nbNodes - 1]);
11434 gp_Pnt p1(coords[0], coords[1], coords[2]);
11436 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11437 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11438 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11439 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11440 for (int id=0; id < doms.size(); id++)
11442 int idom = doms[id];
11443 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11444 for (int ivol=0; ivol<nbvol; ivol++)
11446 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11447 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11448 if (domain.count(elem))
11450 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11451 domvol[idom] = svol;
11452 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11454 vtkIdType npts = 0;
11455 vtkIdType* pts = 0;
11456 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11457 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11460 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11461 angleDom[idom] = 0;
11465 gp_Pnt g(values[0], values[1], values[2]);
11466 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11467 //MESSAGE(" angle=" << angleDom[idom]);
11473 map<double, int> sortedDom; // sort domains by angle
11474 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11475 sortedDom[ia->second] = ia->first;
11476 vector<int> vnodes;
11478 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11480 vdom.push_back(ib->second);
11481 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11483 for (int ino = 0; ino < nbNodes; ino++)
11484 vnodes.push_back(nodes[ino]);
11485 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11494 // --- iterate on shared faces (volumes to modify, face to extrude)
11495 // get node id's of the face (id SMDS = id VTK)
11496 // create flat element with old and new nodes if requested
11498 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11499 // (domain1 X domain2) = domain1 + MAXINT*domain2
11501 std::map<int, std::map<long,int> > nodeQuadDomains;
11502 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11504 MESSAGE(".. Creation of elements: simple junction");
11505 if (createJointElems)
11508 string joints2DName = "joints2D";
11509 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11510 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11511 string joints3DName = "joints3D";
11512 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11513 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11515 itface = faceDomains.begin();
11516 for (; itface != faceDomains.end(); ++itface)
11518 DownIdType face = itface->first;
11519 std::set<int> oldNodes;
11520 std::set<int>::iterator itn;
11522 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11524 std::map<int, int> domvol = itface->second;
11525 std::map<int, int>::iterator itdom = domvol.begin();
11526 int dom1 = itdom->first;
11527 int vtkVolId = itdom->second;
11529 int dom2 = itdom->first;
11530 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11532 stringstream grpname;
11535 grpname << dom1 << "_" << dom2;
11537 grpname << dom2 << "_" << dom1;
11538 string namegrp = grpname.str();
11539 if (!mapOfJunctionGroups.count(namegrp))
11540 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11541 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11543 sgrp->Add(vol->GetID());
11544 if (vol->GetType() == SMDSAbs_Volume)
11545 joints3DGrp->Add(vol->GetID());
11546 else if (vol->GetType() == SMDSAbs_Face)
11547 joints2DGrp->Add(vol->GetID());
11551 // --- create volumes on multiple domain intersection if requested
11552 // iterate on mutipleNodesToFace
11553 // iterate on edgesMultiDomains
11555 MESSAGE(".. Creation of elements: multiple junction");
11556 if (createJointElems)
11558 // --- iterate on mutipleNodesToFace
11560 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11561 for (; itn != mutipleNodesToFace.end(); ++itn)
11563 int node = itn->first;
11564 vector<int> orderDom = itn->second;
11565 vector<vtkIdType> orderedNodes;
11566 for (int idom = 0; idom <orderDom.size(); idom++)
11567 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11568 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11570 stringstream grpname;
11572 grpname << 0 << "_" << 0;
11574 string namegrp = grpname.str();
11575 if (!mapOfJunctionGroups.count(namegrp))
11576 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11577 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11579 sgrp->Add(face->GetID());
11582 // --- iterate on edgesMultiDomains
11584 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11585 for (; ite != edgesMultiDomains.end(); ++ite)
11587 vector<int> nodes = ite->first;
11588 vector<int> orderDom = ite->second;
11589 vector<vtkIdType> orderedNodes;
11590 if (nodes.size() == 2)
11592 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11593 for (int ino=0; ino < nodes.size(); ino++)
11594 if (orderDom.size() == 3)
11595 for (int idom = 0; idom <orderDom.size(); idom++)
11596 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11598 for (int idom = orderDom.size()-1; idom >=0; idom--)
11599 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11600 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11603 string namegrp = "jointsMultiples";
11604 if (!mapOfJunctionGroups.count(namegrp))
11605 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11606 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11608 sgrp->Add(vol->GetID());
11612 INFOS("Quadratic multiple joints not implemented");
11613 // TODO quadratic nodes
11618 // --- list the explicit faces and edges of the mesh that need to be modified,
11619 // i.e. faces and edges built with one or more duplicated nodes.
11620 // associate these faces or edges to their corresponding domain.
11621 // only the first domain found is kept when a face or edge is shared
11623 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11624 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11625 faceOrEdgeDom.clear();
11628 MESSAGE(".. Modification of elements");
11629 for (int idomain = idom0; idomain < nbDomains; idomain++)
11631 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11632 for (; itnod != nodeDomains.end(); ++itnod)
11634 int oldId = itnod->first;
11635 //MESSAGE(" node " << oldId);
11636 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11637 for (int i = 0; i < l.ncells; i++)
11639 int vtkId = l.cells[i];
11640 int vtkType = grid->GetCellType(vtkId);
11641 int downId = grid->CellIdToDownId(vtkId);
11643 continue; // new cells: not to be modified
11644 DownIdType aCell(downId, vtkType);
11645 int volParents[1000];
11646 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11647 for (int j = 0; j < nbvol; j++)
11648 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11649 if (!feDom.count(vtkId))
11651 feDom[vtkId] = idomain;
11652 faceOrEdgeDom[aCell] = emptyMap;
11653 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11654 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11655 // << " type " << vtkType << " downId " << downId);
11661 // --- iterate on shared faces (volumes to modify, face to extrude)
11662 // get node id's of the face
11663 // replace old nodes by new nodes in volumes, and update inverse connectivity
11665 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11666 for (int m=0; m<3; m++)
11668 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11669 itface = (*amap).begin();
11670 for (; itface != (*amap).end(); ++itface)
11672 DownIdType face = itface->first;
11673 std::set<int> oldNodes;
11674 std::set<int>::iterator itn;
11676 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11677 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11678 std::map<int, int> localClonedNodeIds;
11680 std::map<int, int> domvol = itface->second;
11681 std::map<int, int>::iterator itdom = domvol.begin();
11682 for (; itdom != domvol.end(); ++itdom)
11684 int idom = itdom->first;
11685 int vtkVolId = itdom->second;
11686 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11687 localClonedNodeIds.clear();
11688 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11691 if (nodeDomains[oldId].count(idom))
11693 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11694 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11697 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11702 // Remove empty groups (issue 0022812)
11703 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11704 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11706 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11707 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11710 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11711 grid->BuildLinks();
11719 * \brief Double nodes on some external faces and create flat elements.
11720 * Flat elements are mainly used by some types of mechanic calculations.
11722 * Each group of the list must be constituted of faces.
11723 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11724 * @param theElems - list of groups of faces, where a group of faces is a set of
11725 * SMDS_MeshElements sorted by Id.
11726 * @return TRUE if operation has been completed successfully, FALSE otherwise
11728 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11730 MESSAGE("-------------------------------------------------");
11731 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11732 MESSAGE("-------------------------------------------------");
11734 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11736 // --- For each group of faces
11737 // duplicate the nodes, create a flat element based on the face
11738 // replace the nodes of the faces by their clones
11740 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11741 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11742 clonedNodes.clear();
11743 intermediateNodes.clear();
11744 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11745 mapOfJunctionGroups.clear();
11747 for (int idom = 0; idom < theElems.size(); idom++)
11749 const TIDSortedElemSet& domain = theElems[idom];
11750 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11751 for (; elemItr != domain.end(); ++elemItr)
11753 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11754 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11757 // MESSAGE("aFace=" << aFace->GetID());
11758 bool isQuad = aFace->IsQuadratic();
11759 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11761 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11763 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11764 while (nodeIt->more())
11766 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11767 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11769 ln2.push_back(node);
11771 ln0.push_back(node);
11773 const SMDS_MeshNode* clone = 0;
11774 if (!clonedNodes.count(node))
11776 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11777 copyPosition( node, clone );
11778 clonedNodes[node] = clone;
11781 clone = clonedNodes[node];
11784 ln3.push_back(clone);
11786 ln1.push_back(clone);
11788 const SMDS_MeshNode* inter = 0;
11789 if (isQuad && (!isMedium))
11791 if (!intermediateNodes.count(node))
11793 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11794 copyPosition( node, inter );
11795 intermediateNodes[node] = inter;
11798 inter = intermediateNodes[node];
11799 ln4.push_back(inter);
11803 // --- extrude the face
11805 vector<const SMDS_MeshNode*> ln;
11806 SMDS_MeshVolume* vol = 0;
11807 vtkIdType aType = aFace->GetVtkType();
11811 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11812 // MESSAGE("vol prism " << vol->GetID());
11813 ln.push_back(ln1[0]);
11814 ln.push_back(ln1[1]);
11815 ln.push_back(ln1[2]);
11818 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11819 // MESSAGE("vol hexa " << vol->GetID());
11820 ln.push_back(ln1[0]);
11821 ln.push_back(ln1[1]);
11822 ln.push_back(ln1[2]);
11823 ln.push_back(ln1[3]);
11825 case VTK_QUADRATIC_TRIANGLE:
11826 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11827 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11828 // MESSAGE("vol quad prism " << vol->GetID());
11829 ln.push_back(ln1[0]);
11830 ln.push_back(ln1[1]);
11831 ln.push_back(ln1[2]);
11832 ln.push_back(ln3[0]);
11833 ln.push_back(ln3[1]);
11834 ln.push_back(ln3[2]);
11836 case VTK_QUADRATIC_QUAD:
11837 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11838 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11839 // ln4[0], ln4[1], ln4[2], ln4[3]);
11840 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11841 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11842 ln4[0], ln4[1], ln4[2], ln4[3]);
11843 // MESSAGE("vol quad hexa " << vol->GetID());
11844 ln.push_back(ln1[0]);
11845 ln.push_back(ln1[1]);
11846 ln.push_back(ln1[2]);
11847 ln.push_back(ln1[3]);
11848 ln.push_back(ln3[0]);
11849 ln.push_back(ln3[1]);
11850 ln.push_back(ln3[2]);
11851 ln.push_back(ln3[3]);
11861 stringstream grpname;
11865 string namegrp = grpname.str();
11866 if (!mapOfJunctionGroups.count(namegrp))
11867 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11868 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11870 sgrp->Add(vol->GetID());
11873 // --- modify the face
11875 aFace->ChangeNodes(&ln[0], ln.size());
11882 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11883 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11884 * groups of faces to remove inside the object, (idem edges).
11885 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11887 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11888 const TopoDS_Shape& theShape,
11889 SMESH_NodeSearcher* theNodeSearcher,
11890 const char* groupName,
11891 std::vector<double>& nodesCoords,
11892 std::vector<std::vector<int> >& listOfListOfNodes)
11894 MESSAGE("--------------------------------");
11895 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11896 MESSAGE("--------------------------------");
11898 // --- zone of volumes to remove is given :
11899 // 1 either by a geom shape (one or more vertices) and a radius,
11900 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11901 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11902 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11903 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11904 // defined by it's name.
11906 SMESHDS_GroupBase* groupDS = 0;
11907 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11908 while ( groupIt->more() )
11911 SMESH_Group * group = groupIt->next();
11912 if ( !group ) continue;
11913 groupDS = group->GetGroupDS();
11914 if ( !groupDS || groupDS->IsEmpty() ) continue;
11915 std::string grpName = group->GetName();
11916 //MESSAGE("grpName=" << grpName);
11917 if (grpName == groupName)
11923 bool isNodeGroup = false;
11924 bool isNodeCoords = false;
11927 if (groupDS->GetType() != SMDSAbs_Node)
11929 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11932 if (nodesCoords.size() > 0)
11933 isNodeCoords = true; // a list o nodes given by their coordinates
11934 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11936 // --- define groups to build
11938 int idg; // --- group of SMDS volumes
11939 string grpvName = groupName;
11940 grpvName += "_vol";
11941 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11944 MESSAGE("group not created " << grpvName);
11947 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11949 int idgs; // --- group of SMDS faces on the skin
11950 string grpsName = groupName;
11951 grpsName += "_skin";
11952 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11955 MESSAGE("group not created " << grpsName);
11958 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11960 int idgi; // --- group of SMDS faces internal (several shapes)
11961 string grpiName = groupName;
11962 grpiName += "_internalFaces";
11963 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11966 MESSAGE("group not created " << grpiName);
11969 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11971 int idgei; // --- group of SMDS faces internal (several shapes)
11972 string grpeiName = groupName;
11973 grpeiName += "_internalEdges";
11974 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11977 MESSAGE("group not created " << grpeiName);
11980 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11982 // --- build downward connectivity
11984 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11985 meshDS->BuildDownWardConnectivity(true);
11986 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11988 // --- set of volumes detected inside
11990 std::set<int> setOfInsideVol;
11991 std::set<int> setOfVolToCheck;
11993 std::vector<gp_Pnt> gpnts;
11996 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11998 MESSAGE("group of nodes provided");
11999 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12000 while ( elemIt->more() )
12002 const SMDS_MeshElement* elem = elemIt->next();
12005 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12008 SMDS_MeshElement* vol = 0;
12009 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12010 while (volItr->more())
12012 vol = (SMDS_MeshElement*)volItr->next();
12013 setOfInsideVol.insert(vol->getVtkId());
12014 sgrp->Add(vol->GetID());
12018 else if (isNodeCoords)
12020 MESSAGE("list of nodes coordinates provided");
12023 while (i < nodesCoords.size()-2)
12025 double x = nodesCoords[i++];
12026 double y = nodesCoords[i++];
12027 double z = nodesCoords[i++];
12028 gp_Pnt p = gp_Pnt(x, y ,z);
12029 gpnts.push_back(p);
12030 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12034 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12036 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12037 TopTools_IndexedMapOfShape vertexMap;
12038 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12039 gp_Pnt p = gp_Pnt(0,0,0);
12040 if (vertexMap.Extent() < 1)
12043 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12045 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12046 p = BRep_Tool::Pnt(vertex);
12047 gpnts.push_back(p);
12048 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12052 if (gpnts.size() > 0)
12055 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12057 nodeId = startNode->GetID();
12058 MESSAGE("nodeId " << nodeId);
12060 double radius2 = radius*radius;
12061 MESSAGE("radius2 " << radius2);
12063 // --- volumes on start node
12065 setOfVolToCheck.clear();
12066 SMDS_MeshElement* startVol = 0;
12067 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12068 while (volItr->more())
12070 startVol = (SMDS_MeshElement*)volItr->next();
12071 setOfVolToCheck.insert(startVol->getVtkId());
12073 if (setOfVolToCheck.empty())
12075 MESSAGE("No volumes found");
12079 // --- starting with central volumes then their neighbors, check if they are inside
12080 // or outside the domain, until no more new neighbor volume is inside.
12081 // Fill the group of inside volumes
12083 std::map<int, double> mapOfNodeDistance2;
12084 mapOfNodeDistance2.clear();
12085 std::set<int> setOfOutsideVol;
12086 while (!setOfVolToCheck.empty())
12088 std::set<int>::iterator it = setOfVolToCheck.begin();
12090 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12091 bool volInside = false;
12092 vtkIdType npts = 0;
12093 vtkIdType* pts = 0;
12094 grid->GetCellPoints(vtkId, npts, pts);
12095 for (int i=0; i<npts; i++)
12097 double distance2 = 0;
12098 if (mapOfNodeDistance2.count(pts[i]))
12100 distance2 = mapOfNodeDistance2[pts[i]];
12101 MESSAGE("point " << pts[i] << " distance2 " << distance2);
12105 double *coords = grid->GetPoint(pts[i]);
12106 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12108 for (int j=0; j<gpnts.size(); j++)
12110 double d2 = aPoint.SquareDistance(gpnts[j]);
12111 if (d2 < distance2)
12114 if (distance2 < radius2)
12118 mapOfNodeDistance2[pts[i]] = distance2;
12119 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12121 if (distance2 < radius2)
12123 volInside = true; // one or more nodes inside the domain
12124 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12130 setOfInsideVol.insert(vtkId);
12131 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12132 int neighborsVtkIds[NBMAXNEIGHBORS];
12133 int downIds[NBMAXNEIGHBORS];
12134 unsigned char downTypes[NBMAXNEIGHBORS];
12135 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12136 for (int n = 0; n < nbNeighbors; n++)
12137 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12138 setOfVolToCheck.insert(neighborsVtkIds[n]);
12142 setOfOutsideVol.insert(vtkId);
12143 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12145 setOfVolToCheck.erase(vtkId);
12149 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12150 // If yes, add the volume to the inside set
12152 bool addedInside = true;
12153 std::set<int> setOfVolToReCheck;
12154 while (addedInside)
12156 MESSAGE(" --------------------------- re check");
12157 addedInside = false;
12158 std::set<int>::iterator itv = setOfInsideVol.begin();
12159 for (; itv != setOfInsideVol.end(); ++itv)
12162 int neighborsVtkIds[NBMAXNEIGHBORS];
12163 int downIds[NBMAXNEIGHBORS];
12164 unsigned char downTypes[NBMAXNEIGHBORS];
12165 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12166 for (int n = 0; n < nbNeighbors; n++)
12167 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12168 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12170 setOfVolToCheck = setOfVolToReCheck;
12171 setOfVolToReCheck.clear();
12172 while (!setOfVolToCheck.empty())
12174 std::set<int>::iterator it = setOfVolToCheck.begin();
12176 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12178 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12179 int countInside = 0;
12180 int neighborsVtkIds[NBMAXNEIGHBORS];
12181 int downIds[NBMAXNEIGHBORS];
12182 unsigned char downTypes[NBMAXNEIGHBORS];
12183 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12184 for (int n = 0; n < nbNeighbors; n++)
12185 if (setOfInsideVol.count(neighborsVtkIds[n]))
12187 MESSAGE("countInside " << countInside);
12188 if (countInside > 1)
12190 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12191 setOfInsideVol.insert(vtkId);
12192 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12193 addedInside = true;
12196 setOfVolToReCheck.insert(vtkId);
12198 setOfVolToCheck.erase(vtkId);
12202 // --- map of Downward faces at the boundary, inside the global volume
12203 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12204 // fill group of SMDS faces inside the volume (when several volume shapes)
12205 // fill group of SMDS faces on the skin of the global volume (if skin)
12207 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12208 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12209 std::set<int>::iterator it = setOfInsideVol.begin();
12210 for (; it != setOfInsideVol.end(); ++it)
12213 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12214 int neighborsVtkIds[NBMAXNEIGHBORS];
12215 int downIds[NBMAXNEIGHBORS];
12216 unsigned char downTypes[NBMAXNEIGHBORS];
12217 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12218 for (int n = 0; n < nbNeighbors; n++)
12220 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12221 if (neighborDim == 3)
12223 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12225 DownIdType face(downIds[n], downTypes[n]);
12226 boundaryFaces[face] = vtkId;
12228 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12229 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12230 if (vtkFaceId >= 0)
12232 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12233 // find also the smds edges on this face
12234 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12235 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12236 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12237 for (int i = 0; i < nbEdges; i++)
12239 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12240 if (vtkEdgeId >= 0)
12241 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12245 else if (neighborDim == 2) // skin of the volume
12247 DownIdType face(downIds[n], downTypes[n]);
12248 skinFaces[face] = vtkId;
12249 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12250 if (vtkFaceId >= 0)
12251 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12256 // --- identify the edges constituting the wire of each subshape on the skin
12257 // define polylines with the nodes of edges, equivalent to wires
12258 // project polylines on subshapes, and partition, to get geom faces
12260 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12261 std::set<int> emptySet;
12263 std::set<int> shapeIds;
12265 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12266 while (itelem->more())
12268 const SMDS_MeshElement *elem = itelem->next();
12269 int shapeId = elem->getshapeId();
12270 int vtkId = elem->getVtkId();
12271 if (!shapeIdToVtkIdSet.count(shapeId))
12273 shapeIdToVtkIdSet[shapeId] = emptySet;
12274 shapeIds.insert(shapeId);
12276 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12279 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12280 std::set<DownIdType, DownIdCompare> emptyEdges;
12281 emptyEdges.clear();
12283 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12284 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12286 int shapeId = itShape->first;
12287 MESSAGE(" --- Shape ID --- "<< shapeId);
12288 shapeIdToEdges[shapeId] = emptyEdges;
12290 std::vector<int> nodesEdges;
12292 std::set<int>::iterator its = itShape->second.begin();
12293 for (; its != itShape->second.end(); ++its)
12296 MESSAGE(" " << vtkId);
12297 int neighborsVtkIds[NBMAXNEIGHBORS];
12298 int downIds[NBMAXNEIGHBORS];
12299 unsigned char downTypes[NBMAXNEIGHBORS];
12300 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12301 for (int n = 0; n < nbNeighbors; n++)
12303 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12305 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12306 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12307 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12309 DownIdType edge(downIds[n], downTypes[n]);
12310 if (!shapeIdToEdges[shapeId].count(edge))
12312 shapeIdToEdges[shapeId].insert(edge);
12314 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12315 nodesEdges.push_back(vtkNodeId[0]);
12316 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12317 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12323 std::list<int> order;
12325 if (nodesEdges.size() > 0)
12327 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12328 nodesEdges[0] = -1;
12329 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12330 nodesEdges[1] = -1; // do not reuse this edge
12334 int nodeTofind = order.back(); // try first to push back
12336 for (i = 0; i<nodesEdges.size(); i++)
12337 if (nodesEdges[i] == nodeTofind)
12339 if (i == nodesEdges.size())
12340 found = false; // no follower found on back
12343 if (i%2) // odd ==> use the previous one
12344 if (nodesEdges[i-1] < 0)
12348 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12349 nodesEdges[i-1] = -1;
12351 else // even ==> use the next one
12352 if (nodesEdges[i+1] < 0)
12356 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12357 nodesEdges[i+1] = -1;
12362 // try to push front
12364 nodeTofind = order.front(); // try to push front
12365 for (i = 0; i<nodesEdges.size(); i++)
12366 if (nodesEdges[i] == nodeTofind)
12368 if (i == nodesEdges.size())
12370 found = false; // no predecessor found on front
12373 if (i%2) // odd ==> use the previous one
12374 if (nodesEdges[i-1] < 0)
12378 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12379 nodesEdges[i-1] = -1;
12381 else // even ==> use the next one
12382 if (nodesEdges[i+1] < 0)
12386 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12387 nodesEdges[i+1] = -1;
12393 std::vector<int> nodes;
12394 nodes.push_back(shapeId);
12395 std::list<int>::iterator itl = order.begin();
12396 for (; itl != order.end(); itl++)
12398 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12399 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12401 listOfListOfNodes.push_back(nodes);
12404 // partition geom faces with blocFissure
12405 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12406 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12412 //================================================================================
12414 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12415 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12416 * \return TRUE if operation has been completed successfully, FALSE otherwise
12418 //================================================================================
12420 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12422 // iterates on volume elements and detect all free faces on them
12423 SMESHDS_Mesh* aMesh = GetMeshDS();
12427 ElemFeatures faceType( SMDSAbs_Face );
12428 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12429 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12432 const SMDS_MeshVolume* volume = vIt->next();
12433 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12434 vTool.SetExternalNormal();
12435 const int iQuad = volume->IsQuadratic();
12436 faceType.SetQuad( iQuad );
12437 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12439 if (!vTool.IsFreeFace(iface))
12442 vector<const SMDS_MeshNode *> nodes;
12443 int nbFaceNodes = vTool.NbFaceNodes(iface);
12444 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12446 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12447 nodes.push_back(faceNodes[inode]);
12449 if (iQuad) // add medium nodes
12451 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12452 nodes.push_back(faceNodes[inode]);
12453 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12454 nodes.push_back(faceNodes[8]);
12456 // add new face based on volume nodes
12457 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12459 nbExisted++; // face already exsist
12463 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12468 return ( nbFree == ( nbExisted + nbCreated ));
12473 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12475 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12477 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12480 //================================================================================
12482 * \brief Creates missing boundary elements
12483 * \param elements - elements whose boundary is to be checked
12484 * \param dimension - defines type of boundary elements to create
12485 * \param group - a group to store created boundary elements in
12486 * \param targetMesh - a mesh to store created boundary elements in
12487 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12488 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12489 * boundary elements will be copied into the targetMesh
12490 * \param toAddExistingBondary - if true, not only new but also pre-existing
12491 * boundary elements will be added into the new group
12492 * \param aroundElements - if true, elements will be created on boundary of given
12493 * elements else, on boundary of the whole mesh.
12494 * \return nb of added boundary elements
12496 //================================================================================
12498 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12499 Bnd_Dimension dimension,
12500 SMESH_Group* group/*=0*/,
12501 SMESH_Mesh* targetMesh/*=0*/,
12502 bool toCopyElements/*=false*/,
12503 bool toCopyExistingBoundary/*=false*/,
12504 bool toAddExistingBondary/*= false*/,
12505 bool aroundElements/*= false*/)
12507 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12508 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12509 // hope that all elements are of the same type, do not check them all
12510 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12511 throw SALOME_Exception(LOCALIZED("wrong element type"));
12514 toCopyElements = toCopyExistingBoundary = false;
12516 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12517 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12518 int nbAddedBnd = 0;
12520 // editor adding present bnd elements and optionally holding elements to add to the group
12521 SMESH_MeshEditor* presentEditor;
12522 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12523 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12525 SMESH_MesherHelper helper( *myMesh );
12526 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12527 SMDS_VolumeTool vTool;
12528 TIDSortedElemSet avoidSet;
12529 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12532 typedef vector<const SMDS_MeshNode*> TConnectivity;
12533 TConnectivity tgtNodes;
12534 ElemFeatures elemKind( missType );
12536 SMDS_ElemIteratorPtr eIt;
12537 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12538 else eIt = elemSetIterator( elements );
12540 while (eIt->more())
12542 const SMDS_MeshElement* elem = eIt->next();
12543 const int iQuad = elem->IsQuadratic();
12544 elemKind.SetQuad( iQuad );
12546 // ------------------------------------------------------------------------------------
12547 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12548 // ------------------------------------------------------------------------------------
12549 vector<const SMDS_MeshElement*> presentBndElems;
12550 vector<TConnectivity> missingBndElems;
12551 TConnectivity nodes, elemNodes;
12552 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12554 vTool.SetExternalNormal();
12555 const SMDS_MeshElement* otherVol = 0;
12556 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12558 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12559 ( !aroundElements || elements.count( otherVol )))
12561 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12562 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12563 if ( missType == SMDSAbs_Edge ) // boundary edges
12565 nodes.resize( 2+iQuad );
12566 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12568 for ( int j = 0; j < nodes.size(); ++j )
12570 if ( const SMDS_MeshElement* edge =
12571 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12572 presentBndElems.push_back( edge );
12574 missingBndElems.push_back( nodes );
12577 else // boundary face
12580 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12581 nodes.push_back( nn[inode] ); // add corner nodes
12583 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12584 nodes.push_back( nn[inode] ); // add medium nodes
12585 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12587 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12589 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12590 SMDSAbs_Face, /*noMedium=*/false ))
12591 presentBndElems.push_back( f );
12593 missingBndElems.push_back( nodes );
12595 if ( targetMesh != myMesh )
12597 // add 1D elements on face boundary to be added to a new mesh
12598 const SMDS_MeshElement* edge;
12599 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12602 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12604 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12605 if ( edge && avoidSet.insert( edge ).second )
12606 presentBndElems.push_back( edge );
12612 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12614 avoidSet.clear(), avoidSet.insert( elem );
12615 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12616 SMDS_MeshElement::iterator() );
12617 elemNodes.push_back( elemNodes[0] );
12618 nodes.resize( 2 + iQuad );
12619 const int nbLinks = elem->NbCornerNodes();
12620 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12622 nodes[0] = elemNodes[iN];
12623 nodes[1] = elemNodes[iN+1+iQuad];
12624 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12625 continue; // not free link
12627 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12628 if ( const SMDS_MeshElement* edge =
12629 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12630 presentBndElems.push_back( edge );
12632 missingBndElems.push_back( nodes );
12636 // ---------------------------------
12637 // 2. Add missing boundary elements
12638 // ---------------------------------
12639 if ( targetMesh != myMesh )
12640 // instead of making a map of nodes in this mesh and targetMesh,
12641 // we create nodes with same IDs.
12642 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12644 TConnectivity& srcNodes = missingBndElems[i];
12645 tgtNodes.resize( srcNodes.size() );
12646 for ( inode = 0; inode < srcNodes.size(); ++inode )
12647 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12648 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12650 /*noMedium=*/false))
12652 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12656 for ( int i = 0; i < missingBndElems.size(); ++i )
12658 TConnectivity& nodes = missingBndElems[i];
12659 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12661 /*noMedium=*/false))
12663 SMDS_MeshElement* newElem =
12664 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12665 nbAddedBnd += bool( newElem );
12667 // try to set a new element to a shape
12668 if ( myMesh->HasShapeToMesh() )
12671 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12672 const size_t nbN = nodes.size() / (iQuad+1 );
12673 for ( inode = 0; inode < nbN && ok; ++inode )
12675 pair<int, TopAbs_ShapeEnum> i_stype =
12676 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12677 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12678 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12680 if ( ok && mediumShapes.size() > 1 )
12682 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12683 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12684 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12686 if (( ok = ( stype_i->first != stype_i_0.first )))
12687 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12688 aMesh->IndexToShape( stype_i_0.second ));
12691 if ( ok && mediumShapes.begin()->first == missShapeType )
12692 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12696 // ----------------------------------
12697 // 3. Copy present boundary elements
12698 // ----------------------------------
12699 if ( toCopyExistingBoundary )
12700 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12702 const SMDS_MeshElement* e = presentBndElems[i];
12703 tgtNodes.resize( e->NbNodes() );
12704 for ( inode = 0; inode < nodes.size(); ++inode )
12705 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12706 presentEditor->AddElement( tgtNodes, elemKind.Init( e ));
12708 else // store present elements to add them to a group
12709 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12711 presentEditor->myLastCreatedElems.Append( presentBndElems[i] );
12714 } // loop on given elements
12716 // ---------------------------------------------
12717 // 4. Fill group with boundary elements
12718 // ---------------------------------------------
12721 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12722 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12723 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12725 tgtEditor.myLastCreatedElems.Clear();
12726 tgtEditor2.myLastCreatedElems.Clear();
12728 // -----------------------
12729 // 5. Copy given elements
12730 // -----------------------
12731 if ( toCopyElements && targetMesh != myMesh )
12733 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12734 else eIt = elemSetIterator( elements );
12735 while (eIt->more())
12737 const SMDS_MeshElement* elem = eIt->next();
12738 tgtNodes.resize( elem->NbNodes() );
12739 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12740 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12741 tgtEditor.AddElement( tgtNodes, elemKind.Init( elem ));
12743 tgtEditor.myLastCreatedElems.Clear();
12749 //================================================================================
12751 * \brief Copy node position and set \a to node on the same geometry
12753 //================================================================================
12755 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12756 const SMDS_MeshNode* to )
12758 if ( !from || !to ) return;
12760 SMDS_PositionPtr pos = from->GetPosition();
12761 if ( !pos || from->getshapeId() < 1 ) return;
12763 switch ( pos->GetTypeOfPosition() )
12765 case SMDS_TOP_3DSPACE: break;
12767 case SMDS_TOP_FACE:
12769 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12770 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12771 fPos->GetUParameter(), fPos->GetVParameter() );
12774 case SMDS_TOP_EDGE:
12776 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12777 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12778 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12781 case SMDS_TOP_VERTEX:
12783 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12786 case SMDS_TOP_UNSPEC: