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 AddElement( nodeVec, anyFace.Init( elem ));
5361 while ( srcElements.Length() < myLastCreatedElems.Length() )
5362 srcElements.Append( elem );
5365 } // loop on swept elements
5368 //=======================================================================
5369 //function : RotationSweep
5371 //=======================================================================
5373 SMESH_MeshEditor::PGroupIDs
5374 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5375 const gp_Ax1& theAxis,
5376 const double theAngle,
5377 const int theNbSteps,
5378 const double theTol,
5379 const bool theMakeGroups,
5380 const bool theMakeWalls)
5382 myLastCreatedElems.Clear();
5383 myLastCreatedNodes.Clear();
5385 // source elements for each generated one
5386 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5388 MESSAGE( "RotationSweep()");
5390 aTrsf.SetRotation( theAxis, theAngle );
5392 aTrsf2.SetRotation( theAxis, theAngle/2. );
5394 gp_Lin aLine( theAxis );
5395 double aSqTol = theTol * theTol;
5397 SMESHDS_Mesh* aMesh = GetMeshDS();
5399 TNodeOfNodeListMap mapNewNodes;
5400 TElemOfVecOfNnlmiMap mapElemNewNodes;
5401 TTElemOfElemListMap newElemsMap;
5403 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5404 myMesh->NbFaces(ORDER_QUADRATIC) +
5405 myMesh->NbVolumes(ORDER_QUADRATIC) );
5406 // loop on theElemSets
5407 setElemsFirst( theElemSets );
5408 TIDSortedElemSet::iterator itElem;
5409 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5411 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5412 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5413 const SMDS_MeshElement* elem = *itElem;
5414 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5416 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5417 newNodesItVec.reserve( elem->NbNodes() );
5419 // loop on elem nodes
5420 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5421 while ( itN->more() )
5423 const SMDS_MeshNode* node = cast2Node( itN->next() );
5425 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5427 aXYZ.Coord( coord[0], coord[1], coord[2] );
5428 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5430 // check if a node has been already sweeped
5431 TNodeOfNodeListMapItr nIt =
5432 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5433 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5434 if ( listNewNodes.empty() )
5436 // check if we are to create medium nodes between corner ones
5437 bool needMediumNodes = false;
5438 if ( isQuadraticMesh )
5440 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5441 while (it->more() && !needMediumNodes )
5443 const SMDS_MeshElement* invElem = it->next();
5444 if ( invElem != elem && !theElems.count( invElem )) continue;
5445 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5446 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5447 needMediumNodes = true;
5452 const SMDS_MeshNode * newNode = node;
5453 for ( int i = 0; i < theNbSteps; i++ ) {
5455 if ( needMediumNodes ) // create a medium node
5457 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5458 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5459 myLastCreatedNodes.Append(newNode);
5460 srcNodes.Append( node );
5461 listNewNodes.push_back( newNode );
5462 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5465 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5467 // create a corner node
5468 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5469 myLastCreatedNodes.Append(newNode);
5470 srcNodes.Append( node );
5471 listNewNodes.push_back( newNode );
5474 listNewNodes.push_back( newNode );
5475 // if ( needMediumNodes )
5476 // listNewNodes.push_back( newNode );
5480 newNodesItVec.push_back( nIt );
5482 // make new elements
5483 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5488 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5490 PGroupIDs newGroupIDs;
5491 if ( theMakeGroups )
5492 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5497 //=======================================================================
5498 //function : ExtrusParam
5499 //purpose : standard construction
5500 //=======================================================================
5502 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5503 const int theNbSteps,
5505 const double theTolerance):
5507 myFlags( theFlags ),
5508 myTolerance( theTolerance ),
5509 myElemsToUse( NULL )
5511 mySteps = new TColStd_HSequenceOfReal;
5512 const double stepSize = theStep.Magnitude();
5513 for (int i=1; i<=theNbSteps; i++ )
5514 mySteps->Append( stepSize );
5516 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5517 ( theTolerance > 0 ))
5519 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5523 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5527 //=======================================================================
5528 //function : ExtrusParam
5529 //purpose : steps are given explicitly
5530 //=======================================================================
5532 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5533 Handle(TColStd_HSequenceOfReal) theSteps,
5535 const double theTolerance):
5537 mySteps( theSteps ),
5538 myFlags( theFlags ),
5539 myTolerance( theTolerance ),
5540 myElemsToUse( NULL )
5542 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5543 ( theTolerance > 0 ))
5545 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5549 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5553 //=======================================================================
5554 //function : ExtrusParam
5555 //purpose : for extrusion by normal
5556 //=======================================================================
5558 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5559 const int theNbSteps,
5563 mySteps( new TColStd_HSequenceOfReal ),
5564 myFlags( theFlags ),
5566 myElemsToUse( NULL )
5568 for (int i = 0; i < theNbSteps; i++ )
5569 mySteps->Append( theStepSize );
5573 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5577 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5581 //=======================================================================
5582 //function : ExtrusParam::SetElementsToUse
5583 //purpose : stores elements to use for extrusion by normal, depending on
5584 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5585 //=======================================================================
5587 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5589 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5592 //=======================================================================
5593 //function : ExtrusParam::beginStepIter
5594 //purpose : prepare iteration on steps
5595 //=======================================================================
5597 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5599 myWithMediumNodes = withMediumNodes;
5603 //=======================================================================
5604 //function : ExtrusParam::moreSteps
5605 //purpose : are there more steps?
5606 //=======================================================================
5608 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5610 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5612 //=======================================================================
5613 //function : ExtrusParam::nextStep
5614 //purpose : returns the next step
5615 //=======================================================================
5617 double SMESH_MeshEditor::ExtrusParam::nextStep()
5620 if ( !myCurSteps.empty() )
5622 res = myCurSteps.back();
5623 myCurSteps.pop_back();
5625 else if ( myNextStep <= mySteps->Length() )
5627 myCurSteps.push_back( mySteps->Value( myNextStep ));
5629 if ( myWithMediumNodes )
5631 myCurSteps.back() /= 2.;
5632 myCurSteps.push_back( myCurSteps.back() );
5639 //=======================================================================
5640 //function : ExtrusParam::makeNodesByDir
5641 //purpose : create nodes for standard extrusion
5642 //=======================================================================
5644 int SMESH_MeshEditor::ExtrusParam::
5645 makeNodesByDir( SMESHDS_Mesh* mesh,
5646 const SMDS_MeshNode* srcNode,
5647 std::list<const SMDS_MeshNode*> & newNodes,
5648 const bool makeMediumNodes)
5650 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5653 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5655 p += myDir.XYZ() * nextStep();
5656 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5657 newNodes.push_back( newNode );
5662 //=======================================================================
5663 //function : ExtrusParam::makeNodesByDirAndSew
5664 //purpose : create nodes for standard extrusion with sewing
5665 //=======================================================================
5667 int SMESH_MeshEditor::ExtrusParam::
5668 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5669 const SMDS_MeshNode* srcNode,
5670 std::list<const SMDS_MeshNode*> & newNodes,
5671 const bool makeMediumNodes)
5673 gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5676 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5678 P1 += myDir.XYZ() * nextStep();
5680 // try to search in sequence of existing nodes
5681 // if myNodes.Length()>0 we 'nave to use given sequence
5682 // else - use all nodes of mesh
5683 const SMDS_MeshNode * node = 0;
5684 if ( myNodes.Length() > 0 ) {
5686 for(i=1; i<=myNodes.Length(); i++) {
5687 gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5688 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5690 node = myNodes.Value(i);
5696 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5697 while(itn->more()) {
5698 SMESH_TNodeXYZ P2( itn->next() );
5699 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5708 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5710 newNodes.push_back( node );
5717 //=======================================================================
5718 //function : ExtrusParam::makeNodesByNormal2D
5719 //purpose : create nodes for extrusion using normals of faces
5720 //=======================================================================
5722 int SMESH_MeshEditor::ExtrusParam::
5723 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5724 const SMDS_MeshNode* srcNode,
5725 std::list<const SMDS_MeshNode*> & newNodes,
5726 const bool makeMediumNodes)
5728 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5730 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5732 // get normals to faces sharing srcNode
5733 vector< gp_XYZ > norms, baryCenters;
5734 gp_XYZ norm, avgNorm( 0,0,0 );
5735 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5736 while ( faceIt->more() )
5738 const SMDS_MeshElement* face = faceIt->next();
5739 if ( myElemsToUse && !myElemsToUse->count( face ))
5741 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5743 norms.push_back( norm );
5745 if ( !alongAvgNorm )
5749 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5750 bc += SMESH_TNodeXYZ( nIt->next() );
5751 baryCenters.push_back( bc / nbN );
5756 if ( norms.empty() ) return 0;
5758 double normSize = avgNorm.Modulus();
5759 if ( normSize < std::numeric_limits<double>::min() )
5762 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5765 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5768 avgNorm /= normSize;
5771 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5774 double stepSize = nextStep();
5776 if ( norms.size() > 1 )
5778 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5780 // translate plane of a face
5781 baryCenters[ iF ] += norms[ iF ] * stepSize;
5783 // find point of intersection of the face plane located at baryCenters[ iF ]
5784 // and avgNorm located at pNew
5785 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5786 double dot = ( norms[ iF ] * avgNorm );
5787 if ( dot < std::numeric_limits<double>::min() )
5788 dot = stepSize * 1e-3;
5789 double step = -( norms[ iF ] * pNew + d ) / dot;
5790 pNew += step * avgNorm;
5795 pNew += stepSize * avgNorm;
5799 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5800 newNodes.push_back( newNode );
5805 //=======================================================================
5806 //function : ExtrusParam::makeNodesByNormal1D
5807 //purpose : create nodes for extrusion using normals of edges
5808 //=======================================================================
5810 int SMESH_MeshEditor::ExtrusParam::
5811 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5812 const SMDS_MeshNode* srcNode,
5813 std::list<const SMDS_MeshNode*> & newNodes,
5814 const bool makeMediumNodes)
5816 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5820 //=======================================================================
5821 //function : ExtrusionSweep
5823 //=======================================================================
5825 SMESH_MeshEditor::PGroupIDs
5826 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5827 const gp_Vec& theStep,
5828 const int theNbSteps,
5829 TTElemOfElemListMap& newElemsMap,
5831 const double theTolerance)
5833 ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5834 return ExtrusionSweep( theElems, aParams, newElemsMap );
5838 //=======================================================================
5839 //function : ExtrusionSweep
5841 //=======================================================================
5843 SMESH_MeshEditor::PGroupIDs
5844 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5845 ExtrusParam& theParams,
5846 TTElemOfElemListMap& newElemsMap)
5848 myLastCreatedElems.Clear();
5849 myLastCreatedNodes.Clear();
5851 // source elements for each generated one
5852 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5854 SMESHDS_Mesh* aMesh = GetMeshDS();
5856 setElemsFirst( theElemSets );
5857 const int nbSteps = theParams.NbSteps();
5858 theParams.SetElementsToUse( theElemSets[0] );
5860 TNodeOfNodeListMap mapNewNodes;
5861 //TNodeOfNodeVecMap mapNewNodes;
5862 TElemOfVecOfNnlmiMap mapElemNewNodes;
5863 //TElemOfVecOfMapNodesMap mapElemNewNodes;
5865 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5866 myMesh->NbFaces(ORDER_QUADRATIC) +
5867 myMesh->NbVolumes(ORDER_QUADRATIC) );
5869 TIDSortedElemSet::iterator itElem;
5870 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5872 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5873 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5875 // check element type
5876 const SMDS_MeshElement* elem = *itElem;
5877 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5880 const size_t nbNodes = elem->NbNodes();
5881 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5882 newNodesItVec.reserve( nbNodes );
5884 // loop on elem nodes
5885 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5886 while ( itN->more() )
5888 // check if a node has been already sweeped
5889 const SMDS_MeshNode* node = cast2Node( itN->next() );
5890 TNodeOfNodeListMap::iterator nIt =
5891 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5892 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5893 if ( listNewNodes.empty() )
5897 // check if we are to create medium nodes between corner ones
5898 bool needMediumNodes = false;
5899 if ( isQuadraticMesh )
5901 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5902 while (it->more() && !needMediumNodes )
5904 const SMDS_MeshElement* invElem = it->next();
5905 if ( invElem != elem && !theElems.count( invElem )) continue;
5906 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5907 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5908 needMediumNodes = true;
5911 // create nodes for all steps
5912 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5914 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5915 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5917 myLastCreatedNodes.Append( *newNodesIt );
5918 srcNodes.Append( node );
5923 break; // newNodesItVec will be shorter than nbNodes
5926 newNodesItVec.push_back( nIt );
5928 // make new elements
5929 if ( newNodesItVec.size() == nbNodes )
5930 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5934 if ( theParams.ToMakeBoundary() ) {
5935 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5937 PGroupIDs newGroupIDs;
5938 if ( theParams.ToMakeGroups() )
5939 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5944 //=======================================================================
5945 //function : ExtrusionAlongTrack
5947 //=======================================================================
5948 SMESH_MeshEditor::Extrusion_Error
5949 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5950 SMESH_subMesh* theTrack,
5951 const SMDS_MeshNode* theN1,
5952 const bool theHasAngles,
5953 list<double>& theAngles,
5954 const bool theLinearVariation,
5955 const bool theHasRefPoint,
5956 const gp_Pnt& theRefPoint,
5957 const bool theMakeGroups)
5959 MESSAGE("ExtrusionAlongTrack");
5960 myLastCreatedElems.Clear();
5961 myLastCreatedNodes.Clear();
5964 std::list<double> aPrms;
5965 TIDSortedElemSet::iterator itElem;
5968 TopoDS_Edge aTrackEdge;
5969 TopoDS_Vertex aV1, aV2;
5971 SMDS_ElemIteratorPtr aItE;
5972 SMDS_NodeIteratorPtr aItN;
5973 SMDSAbs_ElementType aTypeE;
5975 TNodeOfNodeListMap mapNewNodes;
5978 aNbE = theElements[0].size() + theElements[1].size();
5981 return EXTR_NO_ELEMENTS;
5983 // 1.1 Track Pattern
5986 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5988 aItE = pSubMeshDS->GetElements();
5989 while ( aItE->more() ) {
5990 const SMDS_MeshElement* pE = aItE->next();
5991 aTypeE = pE->GetType();
5992 // Pattern must contain links only
5993 if ( aTypeE != SMDSAbs_Edge )
5994 return EXTR_PATH_NOT_EDGE;
5997 list<SMESH_MeshEditor_PathPoint> fullList;
5999 const TopoDS_Shape& aS = theTrack->GetSubShape();
6000 // Sub-shape for the Pattern must be an Edge or Wire
6001 if( aS.ShapeType() == TopAbs_EDGE ) {
6002 aTrackEdge = TopoDS::Edge( aS );
6003 // the Edge must not be degenerated
6004 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6005 return EXTR_BAD_PATH_SHAPE;
6006 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6007 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
6008 const SMDS_MeshNode* aN1 = aItN->next();
6009 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
6010 const SMDS_MeshNode* aN2 = aItN->next();
6011 // starting node must be aN1 or aN2
6012 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6013 return EXTR_BAD_STARTING_NODE;
6014 aItN = pSubMeshDS->GetNodes();
6015 while ( aItN->more() ) {
6016 const SMDS_MeshNode* pNode = aItN->next();
6017 const SMDS_EdgePosition* pEPos =
6018 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6019 double aT = pEPos->GetUParameter();
6020 aPrms.push_back( aT );
6022 //Extrusion_Error err =
6023 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6024 } else if( aS.ShapeType() == TopAbs_WIRE ) {
6025 list< SMESH_subMesh* > LSM;
6026 TopTools_SequenceOfShape Edges;
6027 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6028 while(itSM->more()) {
6029 SMESH_subMesh* SM = itSM->next();
6031 const TopoDS_Shape& aS = SM->GetSubShape();
6034 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6035 int startNid = theN1->GetID();
6036 TColStd_MapOfInteger UsedNums;
6038 int NbEdges = Edges.Length();
6040 for(; i<=NbEdges; i++) {
6042 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6043 for(; itLSM!=LSM.end(); itLSM++) {
6045 if(UsedNums.Contains(k)) continue;
6046 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6047 SMESH_subMesh* locTrack = *itLSM;
6048 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6049 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6050 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6051 const SMDS_MeshNode* aN1 = aItN->next();
6052 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6053 const SMDS_MeshNode* aN2 = aItN->next();
6054 // starting node must be aN1 or aN2
6055 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6056 // 2. Collect parameters on the track edge
6058 aItN = locMeshDS->GetNodes();
6059 while ( aItN->more() ) {
6060 const SMDS_MeshNode* pNode = aItN->next();
6061 const SMDS_EdgePosition* pEPos =
6062 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6063 double aT = pEPos->GetUParameter();
6064 aPrms.push_back( aT );
6066 list<SMESH_MeshEditor_PathPoint> LPP;
6067 //Extrusion_Error err =
6068 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6069 LLPPs.push_back(LPP);
6071 // update startN for search following egde
6072 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6073 else startNid = aN1->GetID();
6077 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6078 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6079 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6080 for(; itPP!=firstList.end(); itPP++) {
6081 fullList.push_back( *itPP );
6083 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6084 fullList.pop_back();
6086 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6087 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6088 itPP = currList.begin();
6089 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6090 gp_Dir D1 = PP1.Tangent();
6091 gp_Dir D2 = PP2.Tangent();
6092 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6093 (D1.Z()+D2.Z())/2 ) );
6094 PP1.SetTangent(Dnew);
6095 fullList.push_back(PP1);
6097 for(; itPP!=firstList.end(); itPP++) {
6098 fullList.push_back( *itPP );
6100 PP1 = fullList.back();
6101 fullList.pop_back();
6103 // if wire not closed
6104 fullList.push_back(PP1);
6108 return EXTR_BAD_PATH_SHAPE;
6111 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6112 theHasRefPoint, theRefPoint, theMakeGroups);
6116 //=======================================================================
6117 //function : ExtrusionAlongTrack
6119 //=======================================================================
6120 SMESH_MeshEditor::Extrusion_Error
6121 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6122 SMESH_Mesh* theTrack,
6123 const SMDS_MeshNode* theN1,
6124 const bool theHasAngles,
6125 list<double>& theAngles,
6126 const bool theLinearVariation,
6127 const bool theHasRefPoint,
6128 const gp_Pnt& theRefPoint,
6129 const bool theMakeGroups)
6131 myLastCreatedElems.Clear();
6132 myLastCreatedNodes.Clear();
6135 std::list<double> aPrms;
6136 TIDSortedElemSet::iterator itElem;
6139 TopoDS_Edge aTrackEdge;
6140 TopoDS_Vertex aV1, aV2;
6142 SMDS_ElemIteratorPtr aItE;
6143 SMDS_NodeIteratorPtr aItN;
6144 SMDSAbs_ElementType aTypeE;
6146 TNodeOfNodeListMap mapNewNodes;
6149 aNbE = theElements[0].size() + theElements[1].size();
6152 return EXTR_NO_ELEMENTS;
6154 // 1.1 Track Pattern
6157 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6159 aItE = pMeshDS->elementsIterator();
6160 while ( aItE->more() ) {
6161 const SMDS_MeshElement* pE = aItE->next();
6162 aTypeE = pE->GetType();
6163 // Pattern must contain links only
6164 if ( aTypeE != SMDSAbs_Edge )
6165 return EXTR_PATH_NOT_EDGE;
6168 list<SMESH_MeshEditor_PathPoint> fullList;
6170 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6172 if ( !theTrack->HasShapeToMesh() ) {
6173 //Mesh without shape
6174 const SMDS_MeshNode* currentNode = NULL;
6175 const SMDS_MeshNode* prevNode = theN1;
6176 std::vector<const SMDS_MeshNode*> aNodesList;
6177 aNodesList.push_back(theN1);
6178 int nbEdges = 0, conn=0;
6179 const SMDS_MeshElement* prevElem = NULL;
6180 const SMDS_MeshElement* currentElem = NULL;
6181 int totalNbEdges = theTrack->NbEdges();
6182 SMDS_ElemIteratorPtr nIt;
6185 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6186 return EXTR_BAD_STARTING_NODE;
6189 conn = nbEdgeConnectivity(theN1);
6191 return EXTR_PATH_NOT_EDGE;
6193 aItE = theN1->GetInverseElementIterator();
6194 prevElem = aItE->next();
6195 currentElem = prevElem;
6197 if(totalNbEdges == 1 ) {
6198 nIt = currentElem->nodesIterator();
6199 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6200 if(currentNode == prevNode)
6201 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6202 aNodesList.push_back(currentNode);
6204 nIt = currentElem->nodesIterator();
6205 while( nIt->more() ) {
6206 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6207 if(currentNode == prevNode)
6208 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6209 aNodesList.push_back(currentNode);
6211 //case of the closed mesh
6212 if(currentNode == theN1) {
6217 conn = nbEdgeConnectivity(currentNode);
6219 return EXTR_PATH_NOT_EDGE;
6220 }else if( conn == 1 && nbEdges > 0 ) {
6225 prevNode = currentNode;
6226 aItE = currentNode->GetInverseElementIterator();
6227 currentElem = aItE->next();
6228 if( currentElem == prevElem)
6229 currentElem = aItE->next();
6230 nIt = currentElem->nodesIterator();
6231 prevElem = currentElem;
6237 if(nbEdges != totalNbEdges)
6238 return EXTR_PATH_NOT_EDGE;
6240 TopTools_SequenceOfShape Edges;
6241 double x1,x2,y1,y2,z1,z2;
6242 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6243 int startNid = theN1->GetID();
6244 for(int i = 1; i < aNodesList.size(); i++) {
6245 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
6246 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
6247 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
6248 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
6249 list<SMESH_MeshEditor_PathPoint> LPP;
6251 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6252 LLPPs.push_back(LPP);
6253 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
6254 else startNid = aNodesList[i-1]->GetID();
6258 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6259 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6260 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6261 for(; itPP!=firstList.end(); itPP++) {
6262 fullList.push_back( *itPP );
6265 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6266 SMESH_MeshEditor_PathPoint PP2;
6267 fullList.pop_back();
6269 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6270 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6271 itPP = currList.begin();
6272 PP2 = currList.front();
6273 gp_Dir D1 = PP1.Tangent();
6274 gp_Dir D2 = PP2.Tangent();
6275 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6276 (D1.Z()+D2.Z())/2 ) );
6277 PP1.SetTangent(Dnew);
6278 fullList.push_back(PP1);
6280 for(; itPP!=currList.end(); itPP++) {
6281 fullList.push_back( *itPP );
6283 PP1 = fullList.back();
6284 fullList.pop_back();
6286 fullList.push_back(PP1);
6288 } // Sub-shape for the Pattern must be an Edge or Wire
6289 else if( aS.ShapeType() == TopAbs_EDGE ) {
6290 aTrackEdge = TopoDS::Edge( aS );
6291 // the Edge must not be degenerated
6292 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6293 return EXTR_BAD_PATH_SHAPE;
6294 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6295 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6296 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6297 // starting node must be aN1 or aN2
6298 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6299 return EXTR_BAD_STARTING_NODE;
6300 aItN = pMeshDS->nodesIterator();
6301 while ( aItN->more() ) {
6302 const SMDS_MeshNode* pNode = aItN->next();
6303 if( pNode==aN1 || pNode==aN2 ) continue;
6304 const SMDS_EdgePosition* pEPos =
6305 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6306 double aT = pEPos->GetUParameter();
6307 aPrms.push_back( aT );
6309 //Extrusion_Error err =
6310 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6312 else if( aS.ShapeType() == TopAbs_WIRE ) {
6313 list< SMESH_subMesh* > LSM;
6314 TopTools_SequenceOfShape Edges;
6315 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6316 for(; eExp.More(); eExp.Next()) {
6317 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6318 if( SMESH_Algo::isDegenerated(E) ) continue;
6319 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6325 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6326 TopoDS_Vertex aVprev;
6327 TColStd_MapOfInteger UsedNums;
6328 int NbEdges = Edges.Length();
6330 for(; i<=NbEdges; i++) {
6332 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6333 for(; itLSM!=LSM.end(); itLSM++) {
6335 if(UsedNums.Contains(k)) continue;
6336 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6337 SMESH_subMesh* locTrack = *itLSM;
6338 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6339 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6340 bool aN1isOK = false, aN2isOK = false;
6341 if ( aVprev.IsNull() ) {
6342 // if previous vertex is not yet defined, it means that we in the beginning of wire
6343 // and we have to find initial vertex corresponding to starting node theN1
6344 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6345 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6346 // starting node must be aN1 or aN2
6347 aN1isOK = ( aN1 && aN1 == theN1 );
6348 aN2isOK = ( aN2 && aN2 == theN1 );
6351 // we have specified ending vertex of the previous edge on the previous iteration
6352 // and we have just to check that it corresponds to any vertex in current segment
6353 aN1isOK = aVprev.IsSame( aV1 );
6354 aN2isOK = aVprev.IsSame( aV2 );
6356 if ( !aN1isOK && !aN2isOK ) continue;
6357 // 2. Collect parameters on the track edge
6359 aItN = locMeshDS->GetNodes();
6360 while ( aItN->more() ) {
6361 const SMDS_MeshNode* pNode = aItN->next();
6362 const SMDS_EdgePosition* pEPos =
6363 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6364 double aT = pEPos->GetUParameter();
6365 aPrms.push_back( aT );
6367 list<SMESH_MeshEditor_PathPoint> LPP;
6368 //Extrusion_Error err =
6369 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6370 LLPPs.push_back(LPP);
6372 // update startN for search following egde
6373 if ( aN1isOK ) aVprev = aV2;
6378 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6379 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6380 fullList.splice( fullList.end(), firstList );
6382 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6383 fullList.pop_back();
6385 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6386 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6387 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6388 gp_Dir D1 = PP1.Tangent();
6389 gp_Dir D2 = PP2.Tangent();
6390 gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
6391 PP1.SetTangent(Dnew);
6392 fullList.push_back(PP1);
6393 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6394 PP1 = fullList.back();
6395 fullList.pop_back();
6397 // if wire not closed
6398 fullList.push_back(PP1);
6402 return EXTR_BAD_PATH_SHAPE;
6405 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6406 theHasRefPoint, theRefPoint, theMakeGroups);
6410 //=======================================================================
6411 //function : MakeEdgePathPoints
6412 //purpose : auxilary for ExtrusionAlongTrack
6413 //=======================================================================
6414 SMESH_MeshEditor::Extrusion_Error
6415 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
6416 const TopoDS_Edge& aTrackEdge,
6418 list<SMESH_MeshEditor_PathPoint>& LPP)
6420 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6422 aTolVec2=aTolVec*aTolVec;
6424 TopoDS_Vertex aV1, aV2;
6425 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6426 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6427 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6428 // 2. Collect parameters on the track edge
6429 aPrms.push_front( aT1 );
6430 aPrms.push_back( aT2 );
6433 if( FirstIsStart ) {
6444 SMESH_MeshEditor_PathPoint aPP;
6445 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6446 std::list<double>::iterator aItD = aPrms.begin();
6447 for(; aItD != aPrms.end(); ++aItD) {
6451 aC3D->D1( aT, aP3D, aVec );
6452 aL2 = aVec.SquareMagnitude();
6453 if ( aL2 < aTolVec2 )
6454 return EXTR_CANT_GET_TANGENT;
6455 gp_Dir aTgt( aVec );
6457 aPP.SetTangent( aTgt );
6458 aPP.SetParameter( aT );
6465 //=======================================================================
6466 //function : MakeExtrElements
6467 //purpose : auxilary for ExtrusionAlongTrack
6468 //=======================================================================
6469 SMESH_MeshEditor::Extrusion_Error
6470 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet theElemSets[2],
6471 list<SMESH_MeshEditor_PathPoint>& fullList,
6472 const bool theHasAngles,
6473 list<double>& theAngles,
6474 const bool theLinearVariation,
6475 const bool theHasRefPoint,
6476 const gp_Pnt& theRefPoint,
6477 const bool theMakeGroups)
6479 const int aNbTP = fullList.size();
6481 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6482 LinearAngleVariation(aNbTP-1, theAngles);
6483 // fill vector of path points with angles
6484 vector<SMESH_MeshEditor_PathPoint> aPPs;
6485 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6486 list<double>::iterator itAngles = theAngles.begin();
6487 aPPs.push_back( *itPP++ );
6488 for( ; itPP != fullList.end(); itPP++) {
6489 aPPs.push_back( *itPP );
6490 if ( theHasAngles && itAngles != theAngles.end() )
6491 aPPs.back().SetAngle( *itAngles++ );
6494 TNodeOfNodeListMap mapNewNodes;
6495 TElemOfVecOfNnlmiMap mapElemNewNodes;
6496 TTElemOfElemListMap newElemsMap;
6497 TIDSortedElemSet::iterator itElem;
6498 // source elements for each generated one
6499 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6501 // 3. Center of rotation aV0
6502 gp_Pnt aV0 = theRefPoint;
6503 if ( !theHasRefPoint )
6505 gp_XYZ aGC( 0.,0.,0. );
6506 TIDSortedElemSet newNodes;
6508 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6510 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6511 itElem = theElements.begin();
6512 for ( ; itElem != theElements.end(); itElem++ ) {
6513 const SMDS_MeshElement* elem = *itElem;
6515 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6516 while ( itN->more() ) {
6517 const SMDS_MeshElement* node = itN->next();
6518 if ( newNodes.insert( node ).second )
6519 aGC += SMESH_TNodeXYZ( node );
6523 aGC /= newNodes.size();
6525 } // if (!theHasRefPoint) {
6527 // 4. Processing the elements
6528 SMESHDS_Mesh* aMesh = GetMeshDS();
6530 setElemsFirst( theElemSets );
6531 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6533 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6534 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
6535 // check element type
6536 const SMDS_MeshElement* elem = *itElem;
6539 // SMDSAbs_ElementType aTypeE = elem->GetType();
6540 // if ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge )
6543 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6544 newNodesItVec.reserve( elem->NbNodes() );
6546 // loop on elem nodes
6548 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6549 while ( itN->more() )
6552 // check if a node has been already processed
6553 const SMDS_MeshNode* node =
6554 static_cast<const SMDS_MeshNode*>( itN->next() );
6555 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
6556 if ( nIt == mapNewNodes.end() ) {
6557 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6558 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6561 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6562 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6563 gp_Ax1 anAx1, anAxT1T0;
6564 gp_Dir aDT1x, aDT0x, aDT1T0;
6569 aPN0 = SMESH_TNodeXYZ( node );
6571 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6573 aDT0x= aPP0.Tangent();
6574 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
6576 for ( int j = 1; j < aNbTP; ++j ) {
6577 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6579 aDT1x = aPP1.Tangent();
6580 aAngle1x = aPP1.Angle();
6582 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6584 gp_Vec aV01x( aP0x, aP1x );
6585 aTrsf.SetTranslation( aV01x );
6588 aV1x = aV0x.Transformed( aTrsf );
6589 aPN1 = aPN0.Transformed( aTrsf );
6591 // rotation 1 [ T1,T0 ]
6592 aAngleT1T0=-aDT1x.Angle( aDT0x );
6593 if (fabs(aAngleT1T0) > aTolAng) {
6595 anAxT1T0.SetLocation( aV1x );
6596 anAxT1T0.SetDirection( aDT1T0 );
6597 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6599 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6603 if ( theHasAngles ) {
6604 anAx1.SetLocation( aV1x );
6605 anAx1.SetDirection( aDT1x );
6606 aTrsfRot.SetRotation( anAx1, aAngle1x );
6608 aPN1 = aPN1.Transformed( aTrsfRot );
6612 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
6613 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6614 // create additional node
6615 double x = ( aPN1.X() + aPN0.X() )/2.;
6616 double y = ( aPN1.Y() + aPN0.Y() )/2.;
6617 double z = ( aPN1.Z() + aPN0.Z() )/2.;
6618 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6619 myLastCreatedNodes.Append(newNode);
6620 srcNodes.Append( node );
6621 listNewNodes.push_back( newNode );
6623 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6624 myLastCreatedNodes.Append(newNode);
6625 srcNodes.Append( node );
6626 listNewNodes.push_back( newNode );
6636 // if current elem is quadratic and current node is not medium
6637 // we have to check - may be it is needed to insert additional nodes
6638 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6639 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6640 if(listNewNodes.size()==aNbTP-1) {
6641 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6642 gp_XYZ P(node->X(), node->Y(), node->Z());
6643 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6645 for(i=0; i<aNbTP-1; i++) {
6646 const SMDS_MeshNode* N = *it;
6647 double x = ( N->X() + P.X() )/2.;
6648 double y = ( N->Y() + P.Y() )/2.;
6649 double z = ( N->Z() + P.Z() )/2.;
6650 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6651 srcNodes.Append( node );
6652 myLastCreatedNodes.Append(newN);
6655 P = gp_XYZ(N->X(),N->Y(),N->Z());
6657 listNewNodes.clear();
6658 for(i=0; i<2*(aNbTP-1); i++) {
6659 listNewNodes.push_back(aNodes[i]);
6665 newNodesItVec.push_back( nIt );
6667 // make new elements
6668 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6669 // newNodesItVec[0]->second.size(), myLastCreatedElems );
6670 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6674 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6676 if ( theMakeGroups )
6677 generateGroups( srcNodes, srcElems, "extruded");
6683 //=======================================================================
6684 //function : LinearAngleVariation
6685 //purpose : auxilary for ExtrusionAlongTrack
6686 //=======================================================================
6687 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6688 list<double>& Angles)
6690 int nbAngles = Angles.size();
6691 if( nbSteps > nbAngles ) {
6692 vector<double> theAngles(nbAngles);
6693 list<double>::iterator it = Angles.begin();
6695 for(; it!=Angles.end(); it++) {
6697 theAngles[i] = (*it);
6700 double rAn2St = double( nbAngles ) / double( nbSteps );
6701 double angPrev = 0, angle;
6702 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6703 double angCur = rAn2St * ( iSt+1 );
6704 double angCurFloor = floor( angCur );
6705 double angPrevFloor = floor( angPrev );
6706 if ( angPrevFloor == angCurFloor )
6707 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6709 int iP = int( angPrevFloor );
6710 double angPrevCeil = ceil(angPrev);
6711 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6713 int iC = int( angCurFloor );
6714 if ( iC < nbAngles )
6715 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6717 iP = int( angPrevCeil );
6719 angle += theAngles[ iC ];
6721 res.push_back(angle);
6726 for(; it!=res.end(); it++)
6727 Angles.push_back( *it );
6732 //================================================================================
6734 * \brief Move or copy theElements applying theTrsf to their nodes
6735 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6736 * \param theTrsf - transformation to apply
6737 * \param theCopy - if true, create translated copies of theElems
6738 * \param theMakeGroups - if true and theCopy, create translated groups
6739 * \param theTargetMesh - mesh to copy translated elements into
6740 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6742 //================================================================================
6744 SMESH_MeshEditor::PGroupIDs
6745 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6746 const gp_Trsf& theTrsf,
6748 const bool theMakeGroups,
6749 SMESH_Mesh* theTargetMesh)
6751 myLastCreatedElems.Clear();
6752 myLastCreatedNodes.Clear();
6754 bool needReverse = false;
6755 string groupPostfix;
6756 switch ( theTrsf.Form() ) {
6758 MESSAGE("gp_PntMirror");
6760 groupPostfix = "mirrored";
6763 MESSAGE("gp_Ax1Mirror");
6764 groupPostfix = "mirrored";
6767 MESSAGE("gp_Ax2Mirror");
6769 groupPostfix = "mirrored";
6772 MESSAGE("gp_Rotation");
6773 groupPostfix = "rotated";
6775 case gp_Translation:
6776 MESSAGE("gp_Translation");
6777 groupPostfix = "translated";
6780 MESSAGE("gp_Scale");
6781 groupPostfix = "scaled";
6783 case gp_CompoundTrsf: // different scale by axis
6784 MESSAGE("gp_CompoundTrsf");
6785 groupPostfix = "scaled";
6789 needReverse = false;
6790 groupPostfix = "transformed";
6793 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6794 SMESHDS_Mesh* aMesh = GetMeshDS();
6796 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6797 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6798 SMESH_MeshEditor::ElemFeatures elemType;
6800 // map old node to new one
6801 TNodeNodeMap nodeMap;
6803 // elements sharing moved nodes; those of them which have all
6804 // nodes mirrored but are not in theElems are to be reversed
6805 TIDSortedElemSet inverseElemSet;
6807 // source elements for each generated one
6808 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6810 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6811 TIDSortedElemSet orphanNode;
6813 if ( theElems.empty() ) // transform the whole mesh
6816 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6817 while ( eIt->more() ) theElems.insert( eIt->next() );
6819 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6820 while ( nIt->more() )
6822 const SMDS_MeshNode* node = nIt->next();
6823 if ( node->NbInverseElements() == 0)
6824 orphanNode.insert( node );
6828 // loop on elements to transform nodes : first orphan nodes then elems
6829 TIDSortedElemSet::iterator itElem;
6830 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6831 for (int i=0; i<2; i++)
6832 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6834 const SMDS_MeshElement* elem = *itElem;
6838 // loop on elem nodes
6840 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6841 while ( itN->more() )
6843 const SMDS_MeshNode* node = cast2Node( itN->next() );
6844 // check if a node has been already transformed
6845 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6846 nodeMap.insert( make_pair ( node, node ));
6847 if ( !n2n_isnew.second )
6850 node->GetXYZ( coord );
6851 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6852 if ( theTargetMesh ) {
6853 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6854 n2n_isnew.first->second = newNode;
6855 myLastCreatedNodes.Append(newNode);
6856 srcNodes.Append( node );
6858 else if ( theCopy ) {
6859 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6860 n2n_isnew.first->second = newNode;
6861 myLastCreatedNodes.Append(newNode);
6862 srcNodes.Append( node );
6865 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6866 // node position on shape becomes invalid
6867 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6868 ( SMDS_SpacePosition::originSpacePosition() );
6871 // keep inverse elements
6872 if ( !theCopy && !theTargetMesh && needReverse ) {
6873 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6874 while ( invElemIt->more() ) {
6875 const SMDS_MeshElement* iel = invElemIt->next();
6876 inverseElemSet.insert( iel );
6880 } // loop on elems in { &orphanNode, &theElems };
6882 // either create new elements or reverse mirrored ones
6883 if ( !theCopy && !needReverse && !theTargetMesh )
6886 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6888 // Replicate or reverse elements
6890 std::vector<int> iForw;
6891 vector<const SMDS_MeshNode*> nodes;
6892 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6894 const SMDS_MeshElement* elem = *itElem;
6895 if ( !elem ) continue;
6897 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6898 int nbNodes = elem->NbNodes();
6899 if ( geomType == SMDSGeom_NONE ) continue; // node
6901 nodes.resize( nbNodes );
6903 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6905 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6909 bool allTransformed = true;
6910 int nbFaces = aPolyedre->NbFaces();
6911 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6913 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6914 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6916 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6917 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6918 if ( nodeMapIt == nodeMap.end() )
6919 allTransformed = false; // not all nodes transformed
6921 nodes.push_back((*nodeMapIt).second);
6923 if ( needReverse && allTransformed )
6924 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6926 if ( !allTransformed )
6927 continue; // not all nodes transformed
6929 else // ----------------------- the rest element types
6931 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6932 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6933 const vector<int>& i = needReverse ? iRev : iForw;
6935 // find transformed nodes
6937 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6938 while ( itN->more() ) {
6939 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6940 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6941 if ( nodeMapIt == nodeMap.end() )
6942 break; // not all nodes transformed
6943 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6945 if ( iNode != nbNodes )
6946 continue; // not all nodes transformed
6950 // copy in this or a new mesh
6951 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6952 srcElems.Append( elem );
6955 // reverse element as it was reversed by transformation
6957 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6960 } // loop on elements
6962 if ( editor && editor != this )
6963 myLastCreatedElems = editor->myLastCreatedElems;
6965 PGroupIDs newGroupIDs;
6967 if ( ( theMakeGroups && theCopy ) ||
6968 ( theMakeGroups && theTargetMesh ) )
6969 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6974 //=======================================================================
6976 * \brief Create groups of elements made during transformation
6977 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6978 * \param elemGens - elements making corresponding myLastCreatedElems
6979 * \param postfix - to append to names of new groups
6980 * \param targetMesh - mesh to create groups in
6981 * \param topPresent - is there "top" elements that are created by sweeping
6983 //=======================================================================
6985 SMESH_MeshEditor::PGroupIDs
6986 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6987 const SMESH_SequenceOfElemPtr& elemGens,
6988 const std::string& postfix,
6989 SMESH_Mesh* targetMesh,
6990 const bool topPresent)
6992 PGroupIDs newGroupIDs( new list<int> );
6993 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6995 // Sort existing groups by types and collect their names
6997 // containers to store an old group and generated new ones;
6998 // 1st new group is for result elems of different type than a source one;
6999 // 2nd new group is for same type result elems ("top" group at extrusion)
7001 using boost::make_tuple;
7002 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
7003 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
7004 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7006 set< string > groupNames;
7008 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7009 if ( !groupIt->more() ) return newGroupIDs;
7011 int newGroupID = mesh->GetGroupIds().back()+1;
7012 while ( groupIt->more() )
7014 SMESH_Group * group = groupIt->next();
7015 if ( !group ) continue;
7016 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7017 if ( !groupDS || groupDS->IsEmpty() ) continue;
7018 groupNames.insert ( group->GetName() );
7019 groupDS->SetStoreName( group->GetName() );
7020 const SMDSAbs_ElementType type = groupDS->GetType();
7021 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7022 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7023 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7024 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7027 // Loop on nodes and elements to add them in new groups
7029 vector< const SMDS_MeshElement* > resultElems;
7030 for ( int isNodes = 0; isNodes < 2; ++isNodes )
7032 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
7033 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7034 if ( gens.Length() != elems.Length() )
7035 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7037 // loop on created elements
7038 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7040 const SMDS_MeshElement* sourceElem = gens( iElem );
7041 if ( !sourceElem ) {
7042 MESSAGE("generateGroups(): NULL source element");
7045 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7046 if ( groupsOldNew.empty() ) { // no groups of this type at all
7047 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7048 ++iElem; // skip all elements made by sourceElem
7051 // collect all elements made by the iElem-th sourceElem
7052 resultElems.clear();
7053 if ( const SMDS_MeshElement* resElem = elems( iElem ))
7054 if ( resElem != sourceElem )
7055 resultElems.push_back( resElem );
7056 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7057 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7058 if ( resElem != sourceElem )
7059 resultElems.push_back( resElem );
7061 const SMDS_MeshElement* topElem = 0;
7062 if ( isNodes ) // there must be a top element
7064 topElem = resultElems.back();
7065 resultElems.pop_back();
7069 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7070 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7071 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7073 topElem = *resElemIt;
7074 *resElemIt = 0; // erase *resElemIt
7078 // add resultElems to groups originted from ones the sourceElem belongs to
7079 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7080 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7082 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7083 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7085 // fill in a new group
7086 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7087 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7088 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7090 newGroup.Add( *resElemIt );
7092 // fill a "top" group
7095 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7096 newTopGroup.Add( topElem );
7100 } // loop on created elements
7101 }// loop on nodes and elements
7103 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7105 list<int> topGrouIds;
7106 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7108 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
7109 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7110 orderedOldNewGroups[i]->get<2>() };
7111 for ( int is2nd = 0; is2nd < 2; ++is2nd )
7113 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7114 if ( newGroupDS->IsEmpty() )
7116 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7121 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7124 const bool isTop = ( topPresent &&
7125 newGroupDS->GetType() == oldGroupDS->GetType() &&
7128 string name = oldGroupDS->GetStoreName();
7129 { // remove trailing whitespaces (issue 22599)
7130 size_t size = name.size();
7131 while ( size > 1 && isspace( name[ size-1 ]))
7133 if ( size != name.size() )
7135 name.resize( size );
7136 oldGroupDS->SetStoreName( name.c_str() );
7139 if ( !targetMesh ) {
7140 string suffix = ( isTop ? "top": postfix.c_str() );
7144 while ( !groupNames.insert( name ).second ) // name exists
7145 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7150 newGroupDS->SetStoreName( name.c_str() );
7152 // make a SMESH_Groups
7153 mesh->AddGroup( newGroupDS );
7155 topGrouIds.push_back( newGroupDS->GetID() );
7157 newGroupIDs->push_back( newGroupDS->GetID() );
7161 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7166 //================================================================================
7168 * * \brief Return list of group of nodes close to each other within theTolerance
7169 * * Search among theNodes or in the whole mesh if theNodes is empty using
7170 * * an Octree algorithm
7171 * \param [in,out] theNodes - the nodes to treat
7172 * \param [in] theTolerance - the tolerance
7173 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7174 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7175 * corner and medium nodes in separate groups
7177 //================================================================================
7179 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7180 const double theTolerance,
7181 TListOfListOfNodes & theGroupsOfNodes,
7182 bool theSeparateCornersAndMedium)
7184 myLastCreatedElems.Clear();
7185 myLastCreatedNodes.Clear();
7187 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7188 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7189 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7190 theSeparateCornersAndMedium = false;
7192 TIDSortedNodeSet& corners = theNodes;
7193 TIDSortedNodeSet medium;
7195 if ( theNodes.empty() ) // get all nodes in the mesh
7197 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7198 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7199 if ( theSeparateCornersAndMedium )
7200 while ( nIt->more() )
7202 const SMDS_MeshNode* n = nIt->next();
7203 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7204 nodeSet->insert( nodeSet->end(), n );
7207 while ( nIt->more() )
7208 theNodes.insert( theNodes.end(),nIt->next() );
7210 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7212 TIDSortedNodeSet::iterator nIt = corners.begin();
7213 while ( nIt != corners.end() )
7214 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7216 medium.insert( medium.end(), *nIt );
7217 corners.erase( nIt++ );
7225 if ( !corners.empty() )
7226 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7227 if ( !medium.empty() )
7228 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7231 //=======================================================================
7232 //function : SimplifyFace
7233 //purpose : split a chain of nodes into several closed chains
7234 //=======================================================================
7236 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7237 vector<const SMDS_MeshNode *>& poly_nodes,
7238 vector<int>& quantities) const
7240 int nbNodes = faceNodes.size();
7245 set<const SMDS_MeshNode*> nodeSet;
7247 // get simple seq of nodes
7248 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7249 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7250 int iSimple = 0, nbUnique = 0;
7252 simpleNodes[iSimple++] = faceNodes[0];
7254 for (int iCur = 1; iCur < nbNodes; iCur++) {
7255 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7256 simpleNodes[iSimple++] = faceNodes[iCur];
7257 if (nodeSet.insert( faceNodes[iCur] ).second)
7261 int nbSimple = iSimple;
7262 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7272 bool foundLoop = (nbSimple > nbUnique);
7275 set<const SMDS_MeshNode*> loopSet;
7276 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7277 const SMDS_MeshNode* n = simpleNodes[iSimple];
7278 if (!loopSet.insert( n ).second) {
7282 int iC = 0, curLast = iSimple;
7283 for (; iC < curLast; iC++) {
7284 if (simpleNodes[iC] == n) break;
7286 int loopLen = curLast - iC;
7288 // create sub-element
7290 quantities.push_back(loopLen);
7291 for (; iC < curLast; iC++) {
7292 poly_nodes.push_back(simpleNodes[iC]);
7295 // shift the rest nodes (place from the first loop position)
7296 for (iC = curLast + 1; iC < nbSimple; iC++) {
7297 simpleNodes[iC - loopLen] = simpleNodes[iC];
7299 nbSimple -= loopLen;
7302 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7303 } // while (foundLoop)
7307 quantities.push_back(iSimple);
7308 for (int i = 0; i < iSimple; i++)
7309 poly_nodes.push_back(simpleNodes[i]);
7315 //=======================================================================
7316 //function : MergeNodes
7317 //purpose : In each group, the cdr of nodes are substituted by the first one
7319 //=======================================================================
7321 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7323 MESSAGE("MergeNodes");
7324 myLastCreatedElems.Clear();
7325 myLastCreatedNodes.Clear();
7327 SMESHDS_Mesh* aMesh = GetMeshDS();
7329 TNodeNodeMap nodeNodeMap; // node to replace - new node
7330 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7331 list< int > rmElemIds, rmNodeIds;
7333 // Fill nodeNodeMap and elems
7335 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7336 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7338 list<const SMDS_MeshNode*>& nodes = *grIt;
7339 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7340 const SMDS_MeshNode* nToKeep = *nIt;
7341 for ( ++nIt; nIt != nodes.end(); nIt++ )
7343 const SMDS_MeshNode* nToRemove = *nIt;
7344 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7345 if ( nToRemove != nToKeep )
7347 rmNodeIds.push_back( nToRemove->GetID() );
7348 AddToSameGroups( nToKeep, nToRemove, aMesh );
7349 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7350 // after MergeNodes() w/o creating node in place of merged ones.
7351 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7352 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7353 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7354 sm->SetIsAlwaysComputed( true );
7356 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7357 while ( invElemIt->more() ) {
7358 const SMDS_MeshElement* elem = invElemIt->next();
7363 // Change element nodes or remove an element
7365 set<const SMDS_MeshNode*> nodeSet;
7366 vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7368 ElemFeatures elemType;
7370 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7371 for ( ; eIt != elems.end(); eIt++ )
7373 const SMDS_MeshElement* elem = *eIt;
7374 int nbNodes = elem->NbNodes();
7375 int aShapeId = FindShape( elem );
7378 curNodes.resize( nbNodes );
7379 uniqueNodes.resize( nbNodes );
7380 iRepl.resize( nbNodes );
7381 int iUnique = 0, iCur = 0, nbRepl = 0;
7383 // get new seq of nodes
7384 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7385 while ( itN->more() )
7387 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7389 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7390 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7392 { ////////// BUG 0020185: begin
7393 bool stopRecur = false;
7394 set<const SMDS_MeshNode*> nodesRecur;
7395 nodesRecur.insert(n);
7396 while (!stopRecur) {
7397 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7398 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7399 n = (*nnIt_i).second;
7400 if (!nodesRecur.insert(n).second) {
7401 // error: recursive dependancy
7408 } ////////// BUG 0020185: end
7410 curNodes[ iCur ] = n;
7411 bool isUnique = nodeSet.insert( n ).second;
7413 uniqueNodes[ iUnique++ ] = n;
7415 iRepl[ nbRepl++ ] = iCur;
7419 // Analyse element topology after replacement
7422 int nbUniqueNodes = nodeSet.size();
7423 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7425 if (elem->IsPoly()) // Polygons and Polyhedral volumes
7427 if (elem->GetType() == SMDSAbs_Face) // Polygon
7429 elemType.Init( elem );
7430 const bool isQuad = elemType.myIsQuad;
7432 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7433 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7435 // a polygon can divide into several elements
7436 vector<const SMDS_MeshNode *> polygons_nodes;
7437 vector<int> quantities;
7438 int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7441 vector<const SMDS_MeshNode *> face_nodes;
7443 for (int iface = 0; iface < nbNew; iface++)
7445 int nbNewNodes = quantities[iface];
7446 face_nodes.assign( polygons_nodes.begin() + inode,
7447 polygons_nodes.begin() + inode + nbNewNodes );
7448 inode += nbNewNodes;
7449 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7451 bool isValid = ( nbNewNodes % 2 == 0 );
7452 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7453 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7454 elemType.SetQuad( isValid );
7455 if ( isValid ) // put medium nodes after corners
7456 SMDS_MeshCell::applyInterlaceRev
7457 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7458 nbNewNodes ), face_nodes );
7460 SMDS_MeshElement* newElem = AddElement( face_nodes, elemType );
7462 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7465 rmElemIds.push_back(elem->GetID());
7469 else if (elem->GetType() == SMDSAbs_Volume) // Polyhedral volume
7471 if (nbUniqueNodes < 4) {
7472 rmElemIds.push_back(elem->GetID());
7475 // each face has to be analyzed in order to check volume validity
7476 const SMDS_VtkVolume* aPolyedre =
7477 dynamic_cast<const SMDS_VtkVolume*>( elem );
7479 int nbFaces = aPolyedre->NbFaces();
7481 vector<const SMDS_MeshNode *> poly_nodes;
7482 vector<int> quantities;
7484 for (int iface = 1; iface <= nbFaces; iface++) {
7485 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7486 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7488 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7489 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7490 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7491 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7492 faceNode = (*nnIt).second;
7494 faceNodes[inode - 1] = faceNode;
7497 SimplifyFace(faceNodes, poly_nodes, quantities);
7500 if (quantities.size() > 3) {
7501 // to be done: remove coincident faces
7504 if (quantities.size() > 3)
7506 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7507 const SMDS_MeshElement* newElem =
7508 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7509 myLastCreatedElems.Append(newElem);
7510 if ( aShapeId && newElem )
7511 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7512 rmElemIds.push_back(elem->GetID());
7516 rmElemIds.push_back(elem->GetID());
7527 // TODO not all the possible cases are solved. Find something more generic?
7528 switch ( nbNodes ) {
7529 case 2: ///////////////////////////////////// EDGE
7530 isOk = false; break;
7531 case 3: ///////////////////////////////////// TRIANGLE
7532 isOk = false; break;
7534 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7536 else { //////////////////////////////////// QUADRANGLE
7537 if ( nbUniqueNodes < 3 )
7539 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7540 isOk = false; // opposite nodes stick
7541 //MESSAGE("isOk " << isOk);
7544 case 6: ///////////////////////////////////// PENTAHEDRON
7545 if ( nbUniqueNodes == 4 ) {
7546 // ---------------------------------> tetrahedron
7548 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7549 // all top nodes stick: reverse a bottom
7550 uniqueNodes[ 0 ] = curNodes [ 1 ];
7551 uniqueNodes[ 1 ] = curNodes [ 0 ];
7553 else if (nbRepl == 3 &&
7554 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7555 // all bottom nodes stick: set a top before
7556 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7557 uniqueNodes[ 0 ] = curNodes [ 3 ];
7558 uniqueNodes[ 1 ] = curNodes [ 4 ];
7559 uniqueNodes[ 2 ] = curNodes [ 5 ];
7561 else if (nbRepl == 4 &&
7562 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7563 // a lateral face turns into a line: reverse a bottom
7564 uniqueNodes[ 0 ] = curNodes [ 1 ];
7565 uniqueNodes[ 1 ] = curNodes [ 0 ];
7570 else if ( nbUniqueNodes == 5 ) {
7571 // PENTAHEDRON --------------------> 2 tetrahedrons
7572 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7573 // a bottom node sticks with a linked top one
7575 SMDS_MeshElement* newElem =
7576 aMesh->AddVolume(curNodes[ 3 ],
7579 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7580 myLastCreatedElems.Append(newElem);
7582 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7583 // 2. : reverse a bottom
7584 uniqueNodes[ 0 ] = curNodes [ 1 ];
7585 uniqueNodes[ 1 ] = curNodes [ 0 ];
7595 if(elem->IsQuadratic()) { // Quadratic quadrangle
7607 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7610 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7612 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7613 uniqueNodes[0] = curNodes[0];
7614 uniqueNodes[1] = curNodes[2];
7615 uniqueNodes[2] = curNodes[3];
7616 uniqueNodes[3] = curNodes[5];
7617 uniqueNodes[4] = curNodes[6];
7618 uniqueNodes[5] = curNodes[7];
7621 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7622 uniqueNodes[0] = curNodes[0];
7623 uniqueNodes[1] = curNodes[1];
7624 uniqueNodes[2] = curNodes[2];
7625 uniqueNodes[3] = curNodes[4];
7626 uniqueNodes[4] = curNodes[5];
7627 uniqueNodes[5] = curNodes[6];
7630 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7631 uniqueNodes[0] = curNodes[1];
7632 uniqueNodes[1] = curNodes[2];
7633 uniqueNodes[2] = curNodes[3];
7634 uniqueNodes[3] = curNodes[5];
7635 uniqueNodes[4] = curNodes[6];
7636 uniqueNodes[5] = curNodes[0];
7639 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7640 uniqueNodes[0] = curNodes[0];
7641 uniqueNodes[1] = curNodes[1];
7642 uniqueNodes[2] = curNodes[3];
7643 uniqueNodes[3] = curNodes[4];
7644 uniqueNodes[4] = curNodes[6];
7645 uniqueNodes[5] = curNodes[7];
7648 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7649 uniqueNodes[0] = curNodes[0];
7650 uniqueNodes[1] = curNodes[2];
7651 uniqueNodes[2] = curNodes[3];
7652 uniqueNodes[3] = curNodes[1];
7653 uniqueNodes[4] = curNodes[6];
7654 uniqueNodes[5] = curNodes[7];
7657 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7658 uniqueNodes[0] = curNodes[0];
7659 uniqueNodes[1] = curNodes[1];
7660 uniqueNodes[2] = curNodes[2];
7661 uniqueNodes[3] = curNodes[4];
7662 uniqueNodes[4] = curNodes[5];
7663 uniqueNodes[5] = curNodes[7];
7666 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7667 uniqueNodes[0] = curNodes[0];
7668 uniqueNodes[1] = curNodes[1];
7669 uniqueNodes[2] = curNodes[3];
7670 uniqueNodes[3] = curNodes[4];
7671 uniqueNodes[4] = curNodes[2];
7672 uniqueNodes[5] = curNodes[7];
7675 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7676 uniqueNodes[0] = curNodes[0];
7677 uniqueNodes[1] = curNodes[1];
7678 uniqueNodes[2] = curNodes[2];
7679 uniqueNodes[3] = curNodes[4];
7680 uniqueNodes[4] = curNodes[5];
7681 uniqueNodes[5] = curNodes[3];
7686 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7689 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7693 //////////////////////////////////// HEXAHEDRON
7695 SMDS_VolumeTool hexa (elem);
7696 hexa.SetExternalNormal();
7697 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7698 //////////////////////// HEX ---> 1 tetrahedron
7699 for ( int iFace = 0; iFace < 6; iFace++ ) {
7700 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7701 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7702 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7703 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7704 // one face turns into a point ...
7705 int iOppFace = hexa.GetOppFaceIndex( iFace );
7706 ind = hexa.GetFaceNodesIndices( iOppFace );
7708 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7709 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7712 if ( nbStick == 1 ) {
7713 // ... and the opposite one - into a triangle.
7715 ind = hexa.GetFaceNodesIndices( iFace );
7716 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7723 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7724 //////////////////////// HEX ---> 1 prism
7725 int nbTria = 0, iTria[3];
7726 const int *ind; // indices of face nodes
7727 // look for triangular faces
7728 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7729 ind = hexa.GetFaceNodesIndices( iFace );
7730 TIDSortedNodeSet faceNodes;
7731 for ( iCur = 0; iCur < 4; iCur++ )
7732 faceNodes.insert( curNodes[ind[iCur]] );
7733 if ( faceNodes.size() == 3 )
7734 iTria[ nbTria++ ] = iFace;
7736 // check if triangles are opposite
7737 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7740 // set nodes of the bottom triangle
7741 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7743 for ( iCur = 0; iCur < 4; iCur++ )
7744 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7745 indB.push_back( ind[iCur] );
7746 if ( !hexa.IsForward() )
7747 std::swap( indB[0], indB[2] );
7748 for ( iCur = 0; iCur < 3; iCur++ )
7749 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7750 // set nodes of the top triangle
7751 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7752 for ( iCur = 0; iCur < 3; ++iCur )
7753 for ( int j = 0; j < 4; ++j )
7754 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7756 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7762 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7763 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7764 for ( int iFace = 0; iFace < 6; iFace++ ) {
7765 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7766 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7767 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7768 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7769 // one face turns into a point ...
7770 int iOppFace = hexa.GetOppFaceIndex( iFace );
7771 ind = hexa.GetFaceNodesIndices( iOppFace );
7773 iUnique = 2; // reverse a tetrahedron 1 bottom
7774 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7775 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7777 else if ( iUnique >= 0 )
7778 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7780 if ( nbStick == 0 ) {
7781 // ... and the opposite one is a quadrangle
7783 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7784 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7787 SMDS_MeshElement* newElem =
7788 aMesh->AddVolume(curNodes[ind[ 0 ]],
7791 curNodes[indTop[ 0 ]]);
7792 myLastCreatedElems.Append(newElem);
7794 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7801 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7802 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7803 // find indices of quad and tri faces
7804 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7805 for ( iFace = 0; iFace < 6; iFace++ ) {
7806 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7808 for ( iCur = 0; iCur < 4; iCur++ )
7809 nodeSet.insert( curNodes[ind[ iCur ]] );
7810 nbUniqueNodes = nodeSet.size();
7811 if ( nbUniqueNodes == 3 )
7812 iTriFace[ nbTri++ ] = iFace;
7813 else if ( nbUniqueNodes == 4 )
7814 iQuadFace[ nbQuad++ ] = iFace;
7816 if (nbQuad == 2 && nbTri == 4 &&
7817 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7818 // 2 opposite quadrangles stuck with a diagonal;
7819 // sample groups of merged indices: (0-4)(2-6)
7820 // --------------------------------------------> 2 tetrahedrons
7821 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7822 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7823 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7824 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7825 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7826 // stuck with 0-2 diagonal
7834 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7835 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7836 // stuck with 1-3 diagonal
7848 uniqueNodes[ 0 ] = curNodes [ i0 ];
7849 uniqueNodes[ 1 ] = curNodes [ i1d ];
7850 uniqueNodes[ 2 ] = curNodes [ i3d ];
7851 uniqueNodes[ 3 ] = curNodes [ i0t ];
7854 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7858 myLastCreatedElems.Append(newElem);
7860 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7863 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7864 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7865 // --------------------------------------------> prism
7866 // find 2 opposite triangles
7868 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7869 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7870 // find indices of kept and replaced nodes
7871 // and fill unique nodes of 2 opposite triangles
7872 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7873 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7874 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7875 // fill unique nodes
7878 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7879 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7880 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7882 // iCur of a linked node of the opposite face (make normals co-directed):
7883 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7884 // check that correspondent corners of triangles are linked
7885 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7888 uniqueNodes[ iUnique ] = n;
7889 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7898 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7901 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7908 } // switch ( nbNodes )
7910 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7912 if ( isOk ) // the non-poly elem remains valid after sticking nodes
7914 elemType.Init( elem ).SetID( elem->GetID() );
7916 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7917 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7919 uniqueNodes.resize(nbUniqueNodes);
7920 SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7921 if ( sm && newElem )
7922 sm->AddElement( newElem );
7923 if ( elem != newElem )
7924 ReplaceElemInGroups( elem, newElem, aMesh );
7927 // Remove invalid regular element or invalid polygon
7928 rmElemIds.push_back( elem->GetID() );
7931 } // loop on elements
7933 // Remove bad elements, then equal nodes (order important)
7935 Remove( rmElemIds, false );
7936 Remove( rmNodeIds, true );
7942 // ========================================================
7943 // class : SortableElement
7944 // purpose : allow sorting elements basing on their nodes
7945 // ========================================================
7946 class SortableElement : public set <const SMDS_MeshElement*>
7950 SortableElement( const SMDS_MeshElement* theElem )
7953 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7954 while ( nodeIt->more() )
7955 this->insert( nodeIt->next() );
7958 const SMDS_MeshElement* Get() const
7961 void Set(const SMDS_MeshElement* e) const
7966 mutable const SMDS_MeshElement* myElem;
7969 //=======================================================================
7970 //function : FindEqualElements
7971 //purpose : Return list of group of elements built on the same nodes.
7972 // Search among theElements or in the whole mesh if theElements is empty
7973 //=======================================================================
7975 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7976 TListOfListOfElementsID & theGroupsOfElementsID)
7978 myLastCreatedElems.Clear();
7979 myLastCreatedNodes.Clear();
7981 typedef map< SortableElement, int > TMapOfNodeSet;
7982 typedef list<int> TGroupOfElems;
7984 if ( theElements.empty() )
7985 { // get all elements in the mesh
7986 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7987 while ( eIt->more() )
7988 theElements.insert( theElements.end(), eIt->next() );
7991 vector< TGroupOfElems > arrayOfGroups;
7992 TGroupOfElems groupOfElems;
7993 TMapOfNodeSet mapOfNodeSet;
7995 TIDSortedElemSet::iterator elemIt = theElements.begin();
7996 for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7998 const SMDS_MeshElement* curElem = *elemIt;
7999 SortableElement SE(curElem);
8001 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8002 if ( !pp.second ) { // one more coincident elem
8003 TMapOfNodeSet::iterator& itSE = pp.first;
8004 int ind = (*itSE).second;
8005 arrayOfGroups[ind].push_back( curElem->GetID() );
8008 arrayOfGroups.push_back( groupOfElems );
8009 arrayOfGroups.back().push_back( curElem->GetID() );
8014 groupOfElems.clear();
8015 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8016 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8018 if ( groupIt->size() > 1 ) {
8019 //groupOfElems.sort(); -- theElements is sorted already
8020 theGroupsOfElementsID.push_back( groupOfElems );
8021 theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8026 //=======================================================================
8027 //function : MergeElements
8028 //purpose : In each given group, substitute all elements by the first one.
8029 //=======================================================================
8031 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8033 myLastCreatedElems.Clear();
8034 myLastCreatedNodes.Clear();
8036 typedef list<int> TListOfIDs;
8037 TListOfIDs rmElemIds; // IDs of elems to remove
8039 SMESHDS_Mesh* aMesh = GetMeshDS();
8041 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8042 while ( groupsIt != theGroupsOfElementsID.end() ) {
8043 TListOfIDs& aGroupOfElemID = *groupsIt;
8044 aGroupOfElemID.sort();
8045 int elemIDToKeep = aGroupOfElemID.front();
8046 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8047 aGroupOfElemID.pop_front();
8048 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8049 while ( idIt != aGroupOfElemID.end() ) {
8050 int elemIDToRemove = *idIt;
8051 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8052 // add the kept element in groups of removed one (PAL15188)
8053 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8054 rmElemIds.push_back( elemIDToRemove );
8060 Remove( rmElemIds, false );
8063 //=======================================================================
8064 //function : MergeEqualElements
8065 //purpose : Remove all but one of elements built on the same nodes.
8066 //=======================================================================
8068 void SMESH_MeshEditor::MergeEqualElements()
8070 TIDSortedElemSet aMeshElements; /* empty input ==
8071 to merge equal elements in the whole mesh */
8072 TListOfListOfElementsID aGroupsOfElementsID;
8073 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8074 MergeElements(aGroupsOfElementsID);
8077 //=======================================================================
8078 //function : findAdjacentFace
8080 //=======================================================================
8082 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8083 const SMDS_MeshNode* n2,
8084 const SMDS_MeshElement* elem)
8086 TIDSortedElemSet elemSet, avoidSet;
8088 avoidSet.insert ( elem );
8089 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8092 //=======================================================================
8093 //function : FindFreeBorder
8095 //=======================================================================
8097 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8099 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8100 const SMDS_MeshNode* theSecondNode,
8101 const SMDS_MeshNode* theLastNode,
8102 list< const SMDS_MeshNode* > & theNodes,
8103 list< const SMDS_MeshElement* >& theFaces)
8105 if ( !theFirstNode || !theSecondNode )
8107 // find border face between theFirstNode and theSecondNode
8108 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8112 theFaces.push_back( curElem );
8113 theNodes.push_back( theFirstNode );
8114 theNodes.push_back( theSecondNode );
8116 //vector<const SMDS_MeshNode*> nodes;
8117 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8118 TIDSortedElemSet foundElems;
8119 bool needTheLast = ( theLastNode != 0 );
8121 while ( nStart != theLastNode ) {
8122 if ( nStart == theFirstNode )
8123 return !needTheLast;
8125 // find all free border faces sharing form nStart
8127 list< const SMDS_MeshElement* > curElemList;
8128 list< const SMDS_MeshNode* > nStartList;
8129 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8130 while ( invElemIt->more() ) {
8131 const SMDS_MeshElement* e = invElemIt->next();
8132 if ( e == curElem || foundElems.insert( e ).second ) {
8134 int iNode = 0, nbNodes = e->NbNodes();
8135 //const SMDS_MeshNode* nodes[nbNodes+1];
8136 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8138 if(e->IsQuadratic()) {
8139 const SMDS_VtkFace* F =
8140 dynamic_cast<const SMDS_VtkFace*>(e);
8141 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8142 // use special nodes iterator
8143 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8144 while( anIter->more() ) {
8145 nodes[ iNode++ ] = cast2Node(anIter->next());
8149 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8150 while ( nIt->more() )
8151 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8153 nodes[ iNode ] = nodes[ 0 ];
8155 for ( iNode = 0; iNode < nbNodes; iNode++ )
8156 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8157 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8158 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8160 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8161 curElemList.push_back( e );
8165 // analyse the found
8167 int nbNewBorders = curElemList.size();
8168 if ( nbNewBorders == 0 ) {
8169 // no free border furthermore
8170 return !needTheLast;
8172 else if ( nbNewBorders == 1 ) {
8173 // one more element found
8175 nStart = nStartList.front();
8176 curElem = curElemList.front();
8177 theFaces.push_back( curElem );
8178 theNodes.push_back( nStart );
8181 // several continuations found
8182 list< const SMDS_MeshElement* >::iterator curElemIt;
8183 list< const SMDS_MeshNode* >::iterator nStartIt;
8184 // check if one of them reached the last node
8185 if ( needTheLast ) {
8186 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8187 curElemIt!= curElemList.end();
8188 curElemIt++, nStartIt++ )
8189 if ( *nStartIt == theLastNode ) {
8190 theFaces.push_back( *curElemIt );
8191 theNodes.push_back( *nStartIt );
8195 // find the best free border by the continuations
8196 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8197 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8198 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8199 curElemIt!= curElemList.end();
8200 curElemIt++, nStartIt++ )
8202 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8203 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8204 // find one more free border
8205 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8209 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8210 // choice: clear a worse one
8211 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8212 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8213 contNodes[ iWorse ].clear();
8214 contFaces[ iWorse ].clear();
8217 if ( contNodes[0].empty() && contNodes[1].empty() )
8220 // append the best free border
8221 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8222 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8223 theNodes.pop_back(); // remove nIgnore
8224 theNodes.pop_back(); // remove nStart
8225 theFaces.pop_back(); // remove curElem
8226 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8227 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8228 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8229 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8232 } // several continuations found
8233 } // while ( nStart != theLastNode )
8238 //=======================================================================
8239 //function : CheckFreeBorderNodes
8240 //purpose : Return true if the tree nodes are on a free border
8241 //=======================================================================
8243 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8244 const SMDS_MeshNode* theNode2,
8245 const SMDS_MeshNode* theNode3)
8247 list< const SMDS_MeshNode* > nodes;
8248 list< const SMDS_MeshElement* > faces;
8249 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8252 //=======================================================================
8253 //function : SewFreeBorder
8255 //=======================================================================
8257 SMESH_MeshEditor::Sew_Error
8258 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8259 const SMDS_MeshNode* theBordSecondNode,
8260 const SMDS_MeshNode* theBordLastNode,
8261 const SMDS_MeshNode* theSideFirstNode,
8262 const SMDS_MeshNode* theSideSecondNode,
8263 const SMDS_MeshNode* theSideThirdNode,
8264 const bool theSideIsFreeBorder,
8265 const bool toCreatePolygons,
8266 const bool toCreatePolyedrs)
8268 myLastCreatedElems.Clear();
8269 myLastCreatedNodes.Clear();
8271 MESSAGE("::SewFreeBorder()");
8272 Sew_Error aResult = SEW_OK;
8274 // ====================================
8275 // find side nodes and elements
8276 // ====================================
8278 list< const SMDS_MeshNode* > nSide[ 2 ];
8279 list< const SMDS_MeshElement* > eSide[ 2 ];
8280 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8281 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8285 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8286 nSide[0], eSide[0])) {
8287 MESSAGE(" Free Border 1 not found " );
8288 aResult = SEW_BORDER1_NOT_FOUND;
8290 if (theSideIsFreeBorder) {
8293 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8294 nSide[1], eSide[1])) {
8295 MESSAGE(" Free Border 2 not found " );
8296 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8299 if ( aResult != SEW_OK )
8302 if (!theSideIsFreeBorder) {
8306 // -------------------------------------------------------------------------
8308 // 1. If nodes to merge are not coincident, move nodes of the free border
8309 // from the coord sys defined by the direction from the first to last
8310 // nodes of the border to the correspondent sys of the side 2
8311 // 2. On the side 2, find the links most co-directed with the correspondent
8312 // links of the free border
8313 // -------------------------------------------------------------------------
8315 // 1. Since sewing may break if there are volumes to split on the side 2,
8316 // we wont move nodes but just compute new coordinates for them
8317 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8318 TNodeXYZMap nBordXYZ;
8319 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8320 list< const SMDS_MeshNode* >::iterator nBordIt;
8322 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8323 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8324 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8325 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8326 double tol2 = 1.e-8;
8327 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8328 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8329 // Need node movement.
8331 // find X and Z axes to create trsf
8332 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8334 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8336 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8339 gp_Ax3 toBordAx( Pb1, Zb, X );
8340 gp_Ax3 fromSideAx( Ps1, Zs, X );
8341 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8343 gp_Trsf toBordSys, fromSide2Sys;
8344 toBordSys.SetTransformation( toBordAx );
8345 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8346 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8349 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8350 const SMDS_MeshNode* n = *nBordIt;
8351 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8352 toBordSys.Transforms( xyz );
8353 fromSide2Sys.Transforms( xyz );
8354 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8358 // just insert nodes XYZ in the nBordXYZ map
8359 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8360 const SMDS_MeshNode* n = *nBordIt;
8361 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8365 // 2. On the side 2, find the links most co-directed with the correspondent
8366 // links of the free border
8368 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8369 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8370 sideNodes.push_back( theSideFirstNode );
8372 bool hasVolumes = false;
8373 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8374 set<long> foundSideLinkIDs, checkedLinkIDs;
8375 SMDS_VolumeTool volume;
8376 //const SMDS_MeshNode* faceNodes[ 4 ];
8378 const SMDS_MeshNode* sideNode;
8379 const SMDS_MeshElement* sideElem;
8380 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8381 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8382 nBordIt = bordNodes.begin();
8384 // border node position and border link direction to compare with
8385 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8386 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8387 // choose next side node by link direction or by closeness to
8388 // the current border node:
8389 bool searchByDir = ( *nBordIt != theBordLastNode );
8391 // find the next node on the Side 2
8393 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8395 checkedLinkIDs.clear();
8396 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8398 // loop on inverse elements of current node (prevSideNode) on the Side 2
8399 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8400 while ( invElemIt->more() )
8402 const SMDS_MeshElement* elem = invElemIt->next();
8403 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8404 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8405 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8406 bool isVolume = volume.Set( elem );
8407 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8408 if ( isVolume ) // --volume
8410 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8411 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8412 if(elem->IsQuadratic()) {
8413 const SMDS_VtkFace* F =
8414 dynamic_cast<const SMDS_VtkFace*>(elem);
8415 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8416 // use special nodes iterator
8417 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8418 while( anIter->more() ) {
8419 nodes[ iNode ] = cast2Node(anIter->next());
8420 if ( nodes[ iNode++ ] == prevSideNode )
8421 iPrevNode = iNode - 1;
8425 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8426 while ( nIt->more() ) {
8427 nodes[ iNode ] = cast2Node( nIt->next() );
8428 if ( nodes[ iNode++ ] == prevSideNode )
8429 iPrevNode = iNode - 1;
8432 // there are 2 links to check
8437 // loop on links, to be precise, on the second node of links
8438 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8439 const SMDS_MeshNode* n = nodes[ iNode ];
8441 if ( !volume.IsLinked( n, prevSideNode ))
8445 if ( iNode ) // a node before prevSideNode
8446 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8447 else // a node after prevSideNode
8448 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8450 // check if this link was already used
8451 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8452 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8453 if (!isJustChecked &&
8454 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8456 // test a link geometrically
8457 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8458 bool linkIsBetter = false;
8459 double dot = 0.0, dist = 0.0;
8460 if ( searchByDir ) { // choose most co-directed link
8461 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8462 linkIsBetter = ( dot > maxDot );
8464 else { // choose link with the node closest to bordPos
8465 dist = ( nextXYZ - bordPos ).SquareModulus();
8466 linkIsBetter = ( dist < minDist );
8468 if ( linkIsBetter ) {
8477 } // loop on inverse elements of prevSideNode
8480 MESSAGE(" Cant find path by links of the Side 2 ");
8481 return SEW_BAD_SIDE_NODES;
8483 sideNodes.push_back( sideNode );
8484 sideElems.push_back( sideElem );
8485 foundSideLinkIDs.insert ( linkID );
8486 prevSideNode = sideNode;
8488 if ( *nBordIt == theBordLastNode )
8489 searchByDir = false;
8491 // find the next border link to compare with
8492 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8493 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8494 // move to next border node if sideNode is before forward border node (bordPos)
8495 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8496 prevBordNode = *nBordIt;
8498 bordPos = nBordXYZ[ *nBordIt ];
8499 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8500 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8504 while ( sideNode != theSideSecondNode );
8506 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8507 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8508 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8510 } // end nodes search on the side 2
8512 // ============================
8513 // sew the border to the side 2
8514 // ============================
8516 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8517 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8519 TListOfListOfNodes nodeGroupsToMerge;
8520 if ( nbNodes[0] == nbNodes[1] ||
8521 ( theSideIsFreeBorder && !theSideThirdNode)) {
8523 // all nodes are to be merged
8525 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8526 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8527 nIt[0]++, nIt[1]++ )
8529 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8530 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8531 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8536 // insert new nodes into the border and the side to get equal nb of segments
8538 // get normalized parameters of nodes on the borders
8539 //double param[ 2 ][ maxNbNodes ];
8541 param[0] = new double [ maxNbNodes ];
8542 param[1] = new double [ maxNbNodes ];
8544 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8545 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8546 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8547 const SMDS_MeshNode* nPrev = *nIt;
8548 double bordLength = 0;
8549 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8550 const SMDS_MeshNode* nCur = *nIt;
8551 gp_XYZ segment (nCur->X() - nPrev->X(),
8552 nCur->Y() - nPrev->Y(),
8553 nCur->Z() - nPrev->Z());
8554 double segmentLen = segment.Modulus();
8555 bordLength += segmentLen;
8556 param[ iBord ][ iNode ] = bordLength;
8559 // normalize within [0,1]
8560 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8561 param[ iBord ][ iNode ] /= bordLength;
8565 // loop on border segments
8566 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8567 int i[ 2 ] = { 0, 0 };
8568 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8569 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8571 TElemOfNodeListMap insertMap;
8572 TElemOfNodeListMap::iterator insertMapIt;
8574 // key: elem to insert nodes into
8575 // value: 2 nodes to insert between + nodes to be inserted
8577 bool next[ 2 ] = { false, false };
8579 // find min adjacent segment length after sewing
8580 double nextParam = 10., prevParam = 0;
8581 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8582 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8583 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8584 if ( i[ iBord ] > 0 )
8585 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8587 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8588 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8589 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8591 // choose to insert or to merge nodes
8592 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8593 if ( Abs( du ) <= minSegLen * 0.2 ) {
8596 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8597 const SMDS_MeshNode* n0 = *nIt[0];
8598 const SMDS_MeshNode* n1 = *nIt[1];
8599 nodeGroupsToMerge.back().push_back( n1 );
8600 nodeGroupsToMerge.back().push_back( n0 );
8601 // position of node of the border changes due to merge
8602 param[ 0 ][ i[0] ] += du;
8603 // move n1 for the sake of elem shape evaluation during insertion.
8604 // n1 will be removed by MergeNodes() anyway
8605 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8606 next[0] = next[1] = true;
8611 int intoBord = ( du < 0 ) ? 0 : 1;
8612 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8613 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8614 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8615 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8616 if ( intoBord == 1 ) {
8617 // move node of the border to be on a link of elem of the side
8618 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8619 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8620 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8621 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8622 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8624 insertMapIt = insertMap.find( elem );
8625 bool notFound = ( insertMapIt == insertMap.end() );
8626 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8628 // insert into another link of the same element:
8629 // 1. perform insertion into the other link of the elem
8630 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8631 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8632 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8633 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8634 // 2. perform insertion into the link of adjacent faces
8636 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8638 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8642 if (toCreatePolyedrs) {
8643 // perform insertion into the links of adjacent volumes
8644 UpdateVolumes(n12, n22, nodeList);
8646 // 3. find an element appeared on n1 and n2 after the insertion
8647 insertMap.erase( elem );
8648 elem = findAdjacentFace( n1, n2, 0 );
8650 if ( notFound || otherLink ) {
8651 // add element and nodes of the side into the insertMap
8652 insertMapIt = insertMap.insert
8653 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8654 (*insertMapIt).second.push_back( n1 );
8655 (*insertMapIt).second.push_back( n2 );
8657 // add node to be inserted into elem
8658 (*insertMapIt).second.push_back( nIns );
8659 next[ 1 - intoBord ] = true;
8662 // go to the next segment
8663 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8664 if ( next[ iBord ] ) {
8665 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8667 nPrev[ iBord ] = *nIt[ iBord ];
8668 nIt[ iBord ]++; i[ iBord ]++;
8672 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8674 // perform insertion of nodes into elements
8676 for (insertMapIt = insertMap.begin();
8677 insertMapIt != insertMap.end();
8680 const SMDS_MeshElement* elem = (*insertMapIt).first;
8681 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8682 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8683 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8685 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8687 if ( !theSideIsFreeBorder ) {
8688 // look for and insert nodes into the faces adjacent to elem
8690 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8692 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8697 if (toCreatePolyedrs) {
8698 // perform insertion into the links of adjacent volumes
8699 UpdateVolumes(n1, n2, nodeList);
8705 } // end: insert new nodes
8707 MergeNodes ( nodeGroupsToMerge );
8712 //=======================================================================
8713 //function : InsertNodesIntoLink
8714 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
8715 // and theBetweenNode2 and split theElement
8716 //=======================================================================
8718 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
8719 const SMDS_MeshNode* theBetweenNode1,
8720 const SMDS_MeshNode* theBetweenNode2,
8721 list<const SMDS_MeshNode*>& theNodesToInsert,
8722 const bool toCreatePoly)
8724 if ( theFace->GetType() != SMDSAbs_Face ) return;
8726 // find indices of 2 link nodes and of the rest nodes
8727 int iNode = 0, il1, il2, i3, i4;
8728 il1 = il2 = i3 = i4 = -1;
8729 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8730 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8732 if(theFace->IsQuadratic()) {
8733 const SMDS_VtkFace* F =
8734 dynamic_cast<const SMDS_VtkFace*>(theFace);
8735 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8736 // use special nodes iterator
8737 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8738 while( anIter->more() ) {
8739 const SMDS_MeshNode* n = cast2Node(anIter->next());
8740 if ( n == theBetweenNode1 )
8742 else if ( n == theBetweenNode2 )
8748 nodes[ iNode++ ] = n;
8752 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8753 while ( nodeIt->more() ) {
8754 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8755 if ( n == theBetweenNode1 )
8757 else if ( n == theBetweenNode2 )
8763 nodes[ iNode++ ] = n;
8766 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8769 // arrange link nodes to go one after another regarding the face orientation
8770 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8771 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8776 aNodesToInsert.reverse();
8778 // check that not link nodes of a quadrangles are in good order
8779 int nbFaceNodes = theFace->NbNodes();
8780 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8786 if (toCreatePoly || theFace->IsPoly()) {
8789 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8791 // add nodes of face up to first node of link
8794 if(theFace->IsQuadratic()) {
8795 const SMDS_VtkFace* F =
8796 dynamic_cast<const SMDS_VtkFace*>(theFace);
8797 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8798 // use special nodes iterator
8799 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8800 while( anIter->more() && !isFLN ) {
8801 const SMDS_MeshNode* n = cast2Node(anIter->next());
8802 poly_nodes[iNode++] = n;
8803 if (n == nodes[il1]) {
8807 // add nodes to insert
8808 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8809 for (; nIt != aNodesToInsert.end(); nIt++) {
8810 poly_nodes[iNode++] = *nIt;
8812 // add nodes of face starting from last node of link
8813 while ( anIter->more() ) {
8814 poly_nodes[iNode++] = cast2Node(anIter->next());
8818 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8819 while ( nodeIt->more() && !isFLN ) {
8820 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8821 poly_nodes[iNode++] = n;
8822 if (n == nodes[il1]) {
8826 // add nodes to insert
8827 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8828 for (; nIt != aNodesToInsert.end(); nIt++) {
8829 poly_nodes[iNode++] = *nIt;
8831 // add nodes of face starting from last node of link
8832 while ( nodeIt->more() ) {
8833 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8834 poly_nodes[iNode++] = n;
8838 // edit or replace the face
8839 SMESHDS_Mesh *aMesh = GetMeshDS();
8841 if (theFace->IsPoly()) {
8842 aMesh->ChangePolygonNodes(theFace, poly_nodes);
8845 int aShapeId = FindShape( theFace );
8847 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8848 myLastCreatedElems.Append(newElem);
8849 if ( aShapeId && newElem )
8850 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8852 aMesh->RemoveElement(theFace);
8857 SMESHDS_Mesh *aMesh = GetMeshDS();
8858 if( !theFace->IsQuadratic() ) {
8860 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8861 int nbLinkNodes = 2 + aNodesToInsert.size();
8862 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8863 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8864 linkNodes[ 0 ] = nodes[ il1 ];
8865 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8866 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8867 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8868 linkNodes[ iNode++ ] = *nIt;
8870 // decide how to split a quadrangle: compare possible variants
8871 // and choose which of splits to be a quadrangle
8872 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8873 if ( nbFaceNodes == 3 ) {
8874 iBestQuad = nbSplits;
8877 else if ( nbFaceNodes == 4 ) {
8878 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8879 double aBestRate = DBL_MAX;
8880 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8882 double aBadRate = 0;
8883 // evaluate elements quality
8884 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8885 if ( iSplit == iQuad ) {
8886 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8890 aBadRate += getBadRate( &quad, aCrit );
8893 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8895 nodes[ iSplit < iQuad ? i4 : i3 ]);
8896 aBadRate += getBadRate( &tria, aCrit );
8900 if ( aBadRate < aBestRate ) {
8902 aBestRate = aBadRate;
8907 // create new elements
8908 int aShapeId = FindShape( theFace );
8911 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8912 SMDS_MeshElement* newElem = 0;
8913 if ( iSplit == iBestQuad )
8914 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8919 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8921 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8922 myLastCreatedElems.Append(newElem);
8923 if ( aShapeId && newElem )
8924 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8927 // change nodes of theFace
8928 const SMDS_MeshNode* newNodes[ 4 ];
8929 newNodes[ 0 ] = linkNodes[ i1 ];
8930 newNodes[ 1 ] = linkNodes[ i2 ];
8931 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8932 newNodes[ 3 ] = nodes[ i4 ];
8933 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8934 const SMDS_MeshElement* newElem = 0;
8935 if (iSplit == iBestQuad)
8936 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8938 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8939 myLastCreatedElems.Append(newElem);
8940 if ( aShapeId && newElem )
8941 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8942 } // end if(!theFace->IsQuadratic())
8943 else { // theFace is quadratic
8944 // we have to split theFace on simple triangles and one simple quadrangle
8946 int nbshift = tmp*2;
8947 // shift nodes in nodes[] by nbshift
8949 for(i=0; i<nbshift; i++) {
8950 const SMDS_MeshNode* n = nodes[0];
8951 for(j=0; j<nbFaceNodes-1; j++) {
8952 nodes[j] = nodes[j+1];
8954 nodes[nbFaceNodes-1] = n;
8956 il1 = il1 - nbshift;
8957 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8958 // n0 n1 n2 n0 n1 n2
8959 // +-----+-----+ +-----+-----+
8968 // create new elements
8969 int aShapeId = FindShape( theFace );
8972 if(nbFaceNodes==6) { // quadratic triangle
8973 SMDS_MeshElement* newElem =
8974 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8975 myLastCreatedElems.Append(newElem);
8976 if ( aShapeId && newElem )
8977 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8978 if(theFace->IsMediumNode(nodes[il1])) {
8979 // create quadrangle
8980 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8981 myLastCreatedElems.Append(newElem);
8982 if ( aShapeId && newElem )
8983 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8989 // create quadrangle
8990 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8991 myLastCreatedElems.Append(newElem);
8992 if ( aShapeId && newElem )
8993 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8999 else { // nbFaceNodes==8 - quadratic quadrangle
9000 SMDS_MeshElement* newElem =
9001 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
9002 myLastCreatedElems.Append(newElem);
9003 if ( aShapeId && newElem )
9004 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9005 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
9006 myLastCreatedElems.Append(newElem);
9007 if ( aShapeId && newElem )
9008 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9009 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
9010 myLastCreatedElems.Append(newElem);
9011 if ( aShapeId && newElem )
9012 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9013 if(theFace->IsMediumNode(nodes[il1])) {
9014 // create quadrangle
9015 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
9016 myLastCreatedElems.Append(newElem);
9017 if ( aShapeId && newElem )
9018 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9024 // create quadrangle
9025 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
9026 myLastCreatedElems.Append(newElem);
9027 if ( aShapeId && newElem )
9028 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9034 // create needed triangles using n1,n2,n3 and inserted nodes
9035 int nbn = 2 + aNodesToInsert.size();
9036 //const SMDS_MeshNode* aNodes[nbn];
9037 vector<const SMDS_MeshNode*> aNodes(nbn);
9038 aNodes[0] = nodes[n1];
9039 aNodes[nbn-1] = nodes[n2];
9040 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9041 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9042 aNodes[iNode++] = *nIt;
9044 for(i=1; i<nbn; i++) {
9045 SMDS_MeshElement* newElem =
9046 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
9047 myLastCreatedElems.Append(newElem);
9048 if ( aShapeId && newElem )
9049 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9053 aMesh->RemoveElement(theFace);
9056 //=======================================================================
9057 //function : UpdateVolumes
9059 //=======================================================================
9060 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9061 const SMDS_MeshNode* theBetweenNode2,
9062 list<const SMDS_MeshNode*>& theNodesToInsert)
9064 myLastCreatedElems.Clear();
9065 myLastCreatedNodes.Clear();
9067 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9068 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9069 const SMDS_MeshElement* elem = invElemIt->next();
9071 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9072 SMDS_VolumeTool aVolume (elem);
9073 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9076 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9077 int iface, nbFaces = aVolume.NbFaces();
9078 vector<const SMDS_MeshNode *> poly_nodes;
9079 vector<int> quantities (nbFaces);
9081 for (iface = 0; iface < nbFaces; iface++) {
9082 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9083 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9084 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9086 for (int inode = 0; inode < nbFaceNodes; inode++) {
9087 poly_nodes.push_back(faceNodes[inode]);
9089 if (nbInserted == 0) {
9090 if (faceNodes[inode] == theBetweenNode1) {
9091 if (faceNodes[inode + 1] == theBetweenNode2) {
9092 nbInserted = theNodesToInsert.size();
9094 // add nodes to insert
9095 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9096 for (; nIt != theNodesToInsert.end(); nIt++) {
9097 poly_nodes.push_back(*nIt);
9101 else if (faceNodes[inode] == theBetweenNode2) {
9102 if (faceNodes[inode + 1] == theBetweenNode1) {
9103 nbInserted = theNodesToInsert.size();
9105 // add nodes to insert in reversed order
9106 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9108 for (; nIt != theNodesToInsert.begin(); nIt--) {
9109 poly_nodes.push_back(*nIt);
9111 poly_nodes.push_back(*nIt);
9118 quantities[iface] = nbFaceNodes + nbInserted;
9121 // Replace or update the volume
9122 SMESHDS_Mesh *aMesh = GetMeshDS();
9124 if (elem->IsPoly()) {
9125 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
9129 int aShapeId = FindShape( elem );
9131 SMDS_MeshElement* newElem =
9132 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
9133 myLastCreatedElems.Append(newElem);
9134 if (aShapeId && newElem)
9135 aMesh->SetMeshElementOnShape(newElem, aShapeId);
9137 aMesh->RemoveElement(elem);
9144 //================================================================================
9146 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9148 //================================================================================
9150 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9151 vector<const SMDS_MeshNode *> & nodes,
9152 vector<int> & nbNodeInFaces )
9155 nbNodeInFaces.clear();
9156 SMDS_VolumeTool vTool ( elem );
9157 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9159 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9160 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9161 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9166 //=======================================================================
9168 * \brief Convert elements contained in a sub-mesh to quadratic
9169 * \return int - nb of checked elements
9171 //=======================================================================
9173 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9174 SMESH_MesherHelper& theHelper,
9175 const bool theForce3d)
9178 if( !theSm ) return nbElem;
9180 vector<int> nbNodeInFaces;
9181 vector<const SMDS_MeshNode *> nodes;
9182 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9183 while(ElemItr->more())
9186 const SMDS_MeshElement* elem = ElemItr->next();
9187 if( !elem ) continue;
9189 // analyse a necessity of conversion
9190 const SMDSAbs_ElementType aType = elem->GetType();
9191 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9193 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9194 bool hasCentralNodes = false;
9195 if ( elem->IsQuadratic() )
9198 switch ( aGeomType ) {
9199 case SMDSEntity_Quad_Triangle:
9200 case SMDSEntity_Quad_Quadrangle:
9201 case SMDSEntity_Quad_Hexa:
9202 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9204 case SMDSEntity_BiQuad_Triangle:
9205 case SMDSEntity_BiQuad_Quadrangle:
9206 case SMDSEntity_TriQuad_Hexa:
9207 alreadyOK = theHelper.GetIsBiQuadratic();
9208 hasCentralNodes = true;
9213 // take into account already present modium nodes
9215 case SMDSAbs_Volume:
9216 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9218 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9220 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9226 // get elem data needed to re-create it
9228 const int id = elem->GetID();
9229 const int nbNodes = elem->NbCornerNodes();
9230 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9231 if ( aGeomType == SMDSEntity_Polyhedra )
9232 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9233 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9234 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9236 // remove a linear element
9237 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9239 // remove central nodes of biquadratic elements (biquad->quad convertion)
9240 if ( hasCentralNodes )
9241 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9242 if ( nodes[i]->NbInverseElements() == 0 )
9243 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9245 const SMDS_MeshElement* NewElem = 0;
9251 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9259 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9262 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9265 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9269 case SMDSAbs_Volume :
9273 case SMDSEntity_Tetra:
9274 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9276 case SMDSEntity_Pyramid:
9277 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9279 case SMDSEntity_Penta:
9280 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9282 case SMDSEntity_Hexa:
9283 case SMDSEntity_Quad_Hexa:
9284 case SMDSEntity_TriQuad_Hexa:
9285 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9286 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9288 case SMDSEntity_Hexagonal_Prism:
9290 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9297 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9298 if( NewElem && NewElem->getshapeId() < 1 )
9299 theSm->AddElement( NewElem );
9303 //=======================================================================
9304 //function : ConvertToQuadratic
9306 //=======================================================================
9308 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9310 SMESHDS_Mesh* meshDS = GetMeshDS();
9312 SMESH_MesherHelper aHelper(*myMesh);
9314 aHelper.SetIsQuadratic( true );
9315 aHelper.SetIsBiQuadratic( theToBiQuad );
9316 aHelper.SetElementsOnShape(true);
9317 aHelper.ToFixNodeParameters( true );
9319 // convert elements assigned to sub-meshes
9320 int nbCheckedElems = 0;
9321 if ( myMesh->HasShapeToMesh() )
9323 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9325 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9326 while ( smIt->more() ) {
9327 SMESH_subMesh* sm = smIt->next();
9328 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9329 aHelper.SetSubShape( sm->GetSubShape() );
9330 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9336 // convert elements NOT assigned to sub-meshes
9337 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9338 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9340 aHelper.SetElementsOnShape(false);
9341 SMESHDS_SubMesh *smDS = 0;
9344 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9345 while( aEdgeItr->more() )
9347 const SMDS_MeshEdge* edge = aEdgeItr->next();
9348 if ( !edge->IsQuadratic() )
9350 int id = edge->GetID();
9351 const SMDS_MeshNode* n1 = edge->GetNode(0);
9352 const SMDS_MeshNode* n2 = edge->GetNode(1);
9354 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9356 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9357 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9361 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9366 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9367 while( aFaceItr->more() )
9369 const SMDS_MeshFace* face = aFaceItr->next();
9370 if ( !face ) continue;
9372 const SMDSAbs_EntityType type = face->GetEntityType();
9376 case SMDSEntity_Quad_Triangle:
9377 case SMDSEntity_Quad_Quadrangle:
9378 alreadyOK = !theToBiQuad;
9379 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9381 case SMDSEntity_BiQuad_Triangle:
9382 case SMDSEntity_BiQuad_Quadrangle:
9383 alreadyOK = theToBiQuad;
9384 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9386 default: alreadyOK = false;
9391 const int id = face->GetID();
9392 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9394 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9396 SMDS_MeshFace * NewFace = 0;
9399 case SMDSEntity_Triangle:
9400 case SMDSEntity_Quad_Triangle:
9401 case SMDSEntity_BiQuad_Triangle:
9402 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9403 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9404 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9407 case SMDSEntity_Quadrangle:
9408 case SMDSEntity_Quad_Quadrangle:
9409 case SMDSEntity_BiQuad_Quadrangle:
9410 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9411 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9412 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9416 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9418 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9422 vector<int> nbNodeInFaces;
9423 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9424 while(aVolumeItr->more())
9426 const SMDS_MeshVolume* volume = aVolumeItr->next();
9427 if ( !volume ) continue;
9429 const SMDSAbs_EntityType type = volume->GetEntityType();
9430 if ( volume->IsQuadratic() )
9435 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9436 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9437 default: alreadyOK = true;
9441 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9445 const int id = volume->GetID();
9446 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9447 if ( type == SMDSEntity_Polyhedra )
9448 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9449 else if ( type == SMDSEntity_Hexagonal_Prism )
9450 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9452 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9454 SMDS_MeshVolume * NewVolume = 0;
9457 case SMDSEntity_Tetra:
9458 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9460 case SMDSEntity_Hexa:
9461 case SMDSEntity_Quad_Hexa:
9462 case SMDSEntity_TriQuad_Hexa:
9463 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9464 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9465 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9466 if ( nodes[i]->NbInverseElements() == 0 )
9467 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9469 case SMDSEntity_Pyramid:
9470 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9471 nodes[3], nodes[4], id, theForce3d);
9473 case SMDSEntity_Penta:
9474 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9475 nodes[3], nodes[4], nodes[5], id, theForce3d);
9477 case SMDSEntity_Hexagonal_Prism:
9479 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9481 ReplaceElemInGroups(volume, NewVolume, meshDS);
9486 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9487 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9488 // aHelper.FixQuadraticElements(myError);
9489 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9493 //================================================================================
9495 * \brief Makes given elements quadratic
9496 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9497 * \param theElements - elements to make quadratic
9499 //================================================================================
9501 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9502 TIDSortedElemSet& theElements,
9503 const bool theToBiQuad)
9505 if ( theElements.empty() ) return;
9507 // we believe that all theElements are of the same type
9508 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9510 // get all nodes shared by theElements
9511 TIDSortedNodeSet allNodes;
9512 TIDSortedElemSet::iterator eIt = theElements.begin();
9513 for ( ; eIt != theElements.end(); ++eIt )
9514 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9516 // complete theElements with elements of lower dim whose all nodes are in allNodes
9518 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9519 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9520 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9521 for ( ; nIt != allNodes.end(); ++nIt )
9523 const SMDS_MeshNode* n = *nIt;
9524 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9525 while ( invIt->more() )
9527 const SMDS_MeshElement* e = invIt->next();
9528 const SMDSAbs_ElementType type = e->GetType();
9529 if ( e->IsQuadratic() )
9531 quadAdjacentElems[ type ].insert( e );
9534 switch ( e->GetEntityType() ) {
9535 case SMDSEntity_Quad_Triangle:
9536 case SMDSEntity_Quad_Quadrangle:
9537 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9538 case SMDSEntity_BiQuad_Triangle:
9539 case SMDSEntity_BiQuad_Quadrangle:
9540 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9541 default: alreadyOK = true;
9546 if ( type >= elemType )
9547 continue; // same type or more complex linear element
9549 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9550 continue; // e is already checked
9554 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9555 while ( nodeIt->more() && allIn )
9556 allIn = allNodes.count( nodeIt->next() );
9558 theElements.insert(e );
9562 SMESH_MesherHelper helper(*myMesh);
9563 helper.SetIsQuadratic( true );
9564 helper.SetIsBiQuadratic( theToBiQuad );
9566 // add links of quadratic adjacent elements to the helper
9568 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9569 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9570 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9572 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9574 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9575 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9576 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9578 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9580 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9581 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9582 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9584 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9587 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9589 SMESHDS_Mesh* meshDS = GetMeshDS();
9590 SMESHDS_SubMesh* smDS = 0;
9591 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9593 const SMDS_MeshElement* elem = *eIt;
9596 int nbCentralNodes = 0;
9597 switch ( elem->GetEntityType() ) {
9598 // linear convertible
9599 case SMDSEntity_Edge:
9600 case SMDSEntity_Triangle:
9601 case SMDSEntity_Quadrangle:
9602 case SMDSEntity_Tetra:
9603 case SMDSEntity_Pyramid:
9604 case SMDSEntity_Hexa:
9605 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9606 // quadratic that can become bi-quadratic
9607 case SMDSEntity_Quad_Triangle:
9608 case SMDSEntity_Quad_Quadrangle:
9609 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9611 case SMDSEntity_BiQuad_Triangle:
9612 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9613 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9615 default: alreadyOK = true;
9617 if ( alreadyOK ) continue;
9619 const SMDSAbs_ElementType type = elem->GetType();
9620 const int id = elem->GetID();
9621 const int nbNodes = elem->NbCornerNodes();
9622 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9624 helper.SetSubShape( elem->getshapeId() );
9626 if ( !smDS || !smDS->Contains( elem ))
9627 smDS = meshDS->MeshElements( elem->getshapeId() );
9628 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9630 SMDS_MeshElement * newElem = 0;
9633 case 4: // cases for most frequently used element types go first (for optimization)
9634 if ( type == SMDSAbs_Volume )
9635 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9637 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9640 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9641 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9644 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9647 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9650 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9651 nodes[4], id, theForce3d);
9654 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9655 nodes[4], nodes[5], id, theForce3d);
9659 ReplaceElemInGroups( elem, newElem, meshDS);
9660 if( newElem && smDS )
9661 smDS->AddElement( newElem );
9663 // remove central nodes
9664 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9665 if ( nodes[i]->NbInverseElements() == 0 )
9666 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9668 } // loop on theElements
9671 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9672 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9673 // helper.FixQuadraticElements( myError );
9674 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9678 //=======================================================================
9680 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9681 * \return int - nb of checked elements
9683 //=======================================================================
9685 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9686 SMDS_ElemIteratorPtr theItr,
9687 const int theShapeID)
9690 SMESHDS_Mesh* meshDS = GetMeshDS();
9691 ElemFeatures elemType;
9692 vector<const SMDS_MeshNode *> nodes;
9694 while( theItr->more() )
9696 const SMDS_MeshElement* elem = theItr->next();
9698 if( elem && elem->IsQuadratic())
9701 int nbCornerNodes = elem->NbCornerNodes();
9702 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9704 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9706 //remove a quadratic element
9707 if ( !theSm || !theSm->Contains( elem ))
9708 theSm = meshDS->MeshElements( elem->getshapeId() );
9709 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9711 // remove medium nodes
9712 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9713 if ( nodes[i]->NbInverseElements() == 0 )
9714 meshDS->RemoveFreeNode( nodes[i], theSm );
9716 // add a linear element
9717 nodes.resize( nbCornerNodes );
9718 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9719 ReplaceElemInGroups(elem, newElem, meshDS);
9720 if( theSm && newElem )
9721 theSm->AddElement( newElem );
9727 //=======================================================================
9728 //function : ConvertFromQuadratic
9730 //=======================================================================
9732 bool SMESH_MeshEditor::ConvertFromQuadratic()
9734 int nbCheckedElems = 0;
9735 if ( myMesh->HasShapeToMesh() )
9737 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9739 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9740 while ( smIt->more() ) {
9741 SMESH_subMesh* sm = smIt->next();
9742 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9743 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9749 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9750 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9752 SMESHDS_SubMesh *aSM = 0;
9753 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9761 //================================================================================
9763 * \brief Return true if all medium nodes of the element are in the node set
9765 //================================================================================
9767 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9769 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9770 if ( !nodeSet.count( elem->GetNode(i) ))
9776 //================================================================================
9778 * \brief Makes given elements linear
9780 //================================================================================
9782 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9784 if ( theElements.empty() ) return;
9786 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9787 set<int> mediumNodeIDs;
9788 TIDSortedElemSet::iterator eIt = theElements.begin();
9789 for ( ; eIt != theElements.end(); ++eIt )
9791 const SMDS_MeshElement* e = *eIt;
9792 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9793 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9796 // replace given elements by linear ones
9797 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9798 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9800 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9801 // except those elements sharing medium nodes of quadratic element whose medium nodes
9802 // are not all in mediumNodeIDs
9804 // get remaining medium nodes
9805 TIDSortedNodeSet mediumNodes;
9806 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9807 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9808 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9809 mediumNodes.insert( mediumNodes.end(), n );
9811 // find more quadratic elements to convert
9812 TIDSortedElemSet moreElemsToConvert;
9813 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9814 for ( ; nIt != mediumNodes.end(); ++nIt )
9816 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9817 while ( invIt->more() )
9819 const SMDS_MeshElement* e = invIt->next();
9820 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9822 // find a more complex element including e and
9823 // whose medium nodes are not in mediumNodes
9824 bool complexFound = false;
9825 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9827 SMDS_ElemIteratorPtr invIt2 =
9828 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9829 while ( invIt2->more() )
9831 const SMDS_MeshElement* eComplex = invIt2->next();
9832 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9834 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9835 if ( nbCommonNodes == e->NbNodes())
9837 complexFound = true;
9838 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9844 if ( !complexFound )
9845 moreElemsToConvert.insert( e );
9849 elemIt = elemSetIterator( moreElemsToConvert );
9850 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9853 //=======================================================================
9854 //function : SewSideElements
9856 //=======================================================================
9858 SMESH_MeshEditor::Sew_Error
9859 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9860 TIDSortedElemSet& theSide2,
9861 const SMDS_MeshNode* theFirstNode1,
9862 const SMDS_MeshNode* theFirstNode2,
9863 const SMDS_MeshNode* theSecondNode1,
9864 const SMDS_MeshNode* theSecondNode2)
9866 myLastCreatedElems.Clear();
9867 myLastCreatedNodes.Clear();
9869 MESSAGE ("::::SewSideElements()");
9870 if ( theSide1.size() != theSide2.size() )
9871 return SEW_DIFF_NB_OF_ELEMENTS;
9873 Sew_Error aResult = SEW_OK;
9875 // 1. Build set of faces representing each side
9876 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9877 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9879 // =======================================================================
9880 // 1. Build set of faces representing each side:
9881 // =======================================================================
9882 // a. build set of nodes belonging to faces
9883 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9884 // c. create temporary faces representing side of volumes if correspondent
9885 // face does not exist
9887 SMESHDS_Mesh* aMesh = GetMeshDS();
9888 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9889 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9890 TIDSortedElemSet faceSet1, faceSet2;
9891 set<const SMDS_MeshElement*> volSet1, volSet2;
9892 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9893 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9894 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9895 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9896 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9897 int iSide, iFace, iNode;
9899 list<const SMDS_MeshElement* > tempFaceList;
9900 for ( iSide = 0; iSide < 2; iSide++ ) {
9901 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9902 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9903 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9904 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9905 set<const SMDS_MeshElement*>::iterator vIt;
9906 TIDSortedElemSet::iterator eIt;
9907 set<const SMDS_MeshNode*>::iterator nIt;
9909 // check that given nodes belong to given elements
9910 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9911 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9912 int firstIndex = -1, secondIndex = -1;
9913 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9914 const SMDS_MeshElement* elem = *eIt;
9915 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9916 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9917 if ( firstIndex > -1 && secondIndex > -1 ) break;
9919 if ( firstIndex < 0 || secondIndex < 0 ) {
9920 // we can simply return until temporary faces created
9921 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9924 // -----------------------------------------------------------
9925 // 1a. Collect nodes of existing faces
9926 // and build set of face nodes in order to detect missing
9927 // faces corresponding to sides of volumes
9928 // -----------------------------------------------------------
9930 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9932 // loop on the given element of a side
9933 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9934 //const SMDS_MeshElement* elem = *eIt;
9935 const SMDS_MeshElement* elem = *eIt;
9936 if ( elem->GetType() == SMDSAbs_Face ) {
9937 faceSet->insert( elem );
9938 set <const SMDS_MeshNode*> faceNodeSet;
9939 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9940 while ( nodeIt->more() ) {
9941 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9942 nodeSet->insert( n );
9943 faceNodeSet.insert( n );
9945 setOfFaceNodeSet.insert( faceNodeSet );
9947 else if ( elem->GetType() == SMDSAbs_Volume )
9948 volSet->insert( elem );
9950 // ------------------------------------------------------------------------------
9951 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9952 // ------------------------------------------------------------------------------
9954 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9955 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9956 while ( fIt->more() ) { // loop on faces sharing a node
9957 const SMDS_MeshElement* f = fIt->next();
9958 if ( faceSet->find( f ) == faceSet->end() ) {
9959 // check if all nodes are in nodeSet and
9960 // complete setOfFaceNodeSet if they are
9961 set <const SMDS_MeshNode*> faceNodeSet;
9962 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9963 bool allInSet = true;
9964 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9965 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9966 if ( nodeSet->find( n ) == nodeSet->end() )
9969 faceNodeSet.insert( n );
9972 faceSet->insert( f );
9973 setOfFaceNodeSet.insert( faceNodeSet );
9979 // -------------------------------------------------------------------------
9980 // 1c. Create temporary faces representing sides of volumes if correspondent
9981 // face does not exist
9982 // -------------------------------------------------------------------------
9984 if ( !volSet->empty() ) {
9985 //int nodeSetSize = nodeSet->size();
9987 // loop on given volumes
9988 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9989 SMDS_VolumeTool vol (*vIt);
9990 // loop on volume faces: find free faces
9991 // --------------------------------------
9992 list<const SMDS_MeshElement* > freeFaceList;
9993 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9994 if ( !vol.IsFreeFace( iFace ))
9996 // check if there is already a face with same nodes in a face set
9997 const SMDS_MeshElement* aFreeFace = 0;
9998 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9999 int nbNodes = vol.NbFaceNodes( iFace );
10000 set <const SMDS_MeshNode*> faceNodeSet;
10001 vol.GetFaceNodes( iFace, faceNodeSet );
10002 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10004 // no such a face is given but it still can exist, check it
10005 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10006 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10008 if ( !aFreeFace ) {
10009 // create a temporary face
10010 if ( nbNodes == 3 ) {
10011 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10012 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10014 else if ( nbNodes == 4 ) {
10015 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10016 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10019 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10020 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10021 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10024 tempFaceList.push_back( aFreeFace );
10028 freeFaceList.push_back( aFreeFace );
10030 } // loop on faces of a volume
10032 // choose one of several free faces of a volume
10033 // --------------------------------------------
10034 if ( freeFaceList.size() > 1 ) {
10035 // choose a face having max nb of nodes shared by other elems of a side
10036 int maxNbNodes = -1;
10037 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10038 while ( fIt != freeFaceList.end() ) { // loop on free faces
10039 int nbSharedNodes = 0;
10040 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10041 while ( nodeIt->more() ) { // loop on free face nodes
10042 const SMDS_MeshNode* n =
10043 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10044 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10045 while ( invElemIt->more() ) {
10046 const SMDS_MeshElement* e = invElemIt->next();
10047 nbSharedNodes += faceSet->count( e );
10048 nbSharedNodes += elemSet->count( e );
10051 if ( nbSharedNodes > maxNbNodes ) {
10052 maxNbNodes = nbSharedNodes;
10053 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10055 else if ( nbSharedNodes == maxNbNodes ) {
10059 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10062 if ( freeFaceList.size() > 1 )
10064 // could not choose one face, use another way
10065 // choose a face most close to the bary center of the opposite side
10066 gp_XYZ aBC( 0., 0., 0. );
10067 set <const SMDS_MeshNode*> addedNodes;
10068 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10069 eIt = elemSet2->begin();
10070 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10071 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10072 while ( nodeIt->more() ) { // loop on free face nodes
10073 const SMDS_MeshNode* n =
10074 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10075 if ( addedNodes.insert( n ).second )
10076 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10079 aBC /= addedNodes.size();
10080 double minDist = DBL_MAX;
10081 fIt = freeFaceList.begin();
10082 while ( fIt != freeFaceList.end() ) { // loop on free faces
10084 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10085 while ( nodeIt->more() ) { // loop on free face nodes
10086 const SMDS_MeshNode* n =
10087 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10088 gp_XYZ p( n->X(),n->Y(),n->Z() );
10089 dist += ( aBC - p ).SquareModulus();
10091 if ( dist < minDist ) {
10093 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10096 fIt = freeFaceList.erase( fIt++ );
10099 } // choose one of several free faces of a volume
10101 if ( freeFaceList.size() == 1 ) {
10102 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10103 faceSet->insert( aFreeFace );
10104 // complete a node set with nodes of a found free face
10105 // for ( iNode = 0; iNode < ; iNode++ )
10106 // nodeSet->insert( fNodes[ iNode ] );
10109 } // loop on volumes of a side
10111 // // complete a set of faces if new nodes in a nodeSet appeared
10112 // // ----------------------------------------------------------
10113 // if ( nodeSetSize != nodeSet->size() ) {
10114 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10115 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10116 // while ( fIt->more() ) { // loop on faces sharing a node
10117 // const SMDS_MeshElement* f = fIt->next();
10118 // if ( faceSet->find( f ) == faceSet->end() ) {
10119 // // check if all nodes are in nodeSet and
10120 // // complete setOfFaceNodeSet if they are
10121 // set <const SMDS_MeshNode*> faceNodeSet;
10122 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10123 // bool allInSet = true;
10124 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10125 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10126 // if ( nodeSet->find( n ) == nodeSet->end() )
10127 // allInSet = false;
10129 // faceNodeSet.insert( n );
10131 // if ( allInSet ) {
10132 // faceSet->insert( f );
10133 // setOfFaceNodeSet.insert( faceNodeSet );
10139 } // Create temporary faces, if there are volumes given
10142 if ( faceSet1.size() != faceSet2.size() ) {
10143 // delete temporary faces: they are in reverseElements of actual nodes
10144 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10145 // while ( tmpFaceIt->more() )
10146 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10147 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10148 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10149 // aMesh->RemoveElement(*tmpFaceIt);
10150 MESSAGE("Diff nb of faces");
10151 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10154 // ============================================================
10155 // 2. Find nodes to merge:
10156 // bind a node to remove to a node to put instead
10157 // ============================================================
10159 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10160 if ( theFirstNode1 != theFirstNode2 )
10161 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10162 if ( theSecondNode1 != theSecondNode2 )
10163 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10165 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10166 set< long > linkIdSet; // links to process
10167 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10169 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10170 list< NLink > linkList[2];
10171 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10172 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10173 // loop on links in linkList; find faces by links and append links
10174 // of the found faces to linkList
10175 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10176 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10178 NLink link[] = { *linkIt[0], *linkIt[1] };
10179 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10180 if ( !linkIdSet.count( linkID ) )
10183 // by links, find faces in the face sets,
10184 // and find indices of link nodes in the found faces;
10185 // in a face set, there is only one or no face sharing a link
10186 // ---------------------------------------------------------------
10188 const SMDS_MeshElement* face[] = { 0, 0 };
10189 vector<const SMDS_MeshNode*> fnodes[2];
10190 int iLinkNode[2][2];
10191 TIDSortedElemSet avoidSet;
10192 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10193 const SMDS_MeshNode* n1 = link[iSide].first;
10194 const SMDS_MeshNode* n2 = link[iSide].second;
10195 //cout << "Side " << iSide << " ";
10196 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10197 // find a face by two link nodes
10198 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10199 *faceSetPtr[ iSide ], avoidSet,
10200 &iLinkNode[iSide][0],
10201 &iLinkNode[iSide][1] );
10202 if ( face[ iSide ])
10204 //cout << " F " << face[ iSide]->GetID() <<endl;
10205 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10206 // put face nodes to fnodes
10207 if ( face[ iSide ]->IsQuadratic() )
10209 // use interlaced nodes iterator
10210 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10211 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10212 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10213 while ( nIter->more() )
10214 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10218 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10219 face[ iSide ]->end_nodes() );
10221 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10225 // check similarity of elements of the sides
10226 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10227 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10228 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10229 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10232 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10234 break; // do not return because it's necessary to remove tmp faces
10237 // set nodes to merge
10238 // -------------------
10240 if ( face[0] && face[1] ) {
10241 const int nbNodes = face[0]->NbNodes();
10242 if ( nbNodes != face[1]->NbNodes() ) {
10243 MESSAGE("Diff nb of face nodes");
10244 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10245 break; // do not return because it s necessary to remove tmp faces
10247 bool reverse[] = { false, false }; // order of nodes in the link
10248 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10249 // analyse link orientation in faces
10250 int i1 = iLinkNode[ iSide ][ 0 ];
10251 int i2 = iLinkNode[ iSide ][ 1 ];
10252 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10254 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10255 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10256 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10258 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10259 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10262 // add other links of the faces to linkList
10263 // -----------------------------------------
10265 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10266 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10267 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10268 if ( !iter_isnew.second ) { // already in a set: no need to process
10269 linkIdSet.erase( iter_isnew.first );
10271 else // new in set == encountered for the first time: add
10273 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10274 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10275 linkList[0].push_back ( NLink( n1, n2 ));
10276 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10281 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10284 } // loop on link lists
10286 if ( aResult == SEW_OK &&
10287 ( //linkIt[0] != linkList[0].end() ||
10288 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10289 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10290 " " << (faceSetPtr[1]->empty()));
10291 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10294 // ====================================================================
10295 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10296 // ====================================================================
10298 // delete temporary faces
10299 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10300 // while ( tmpFaceIt->more() )
10301 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10302 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10303 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10304 aMesh->RemoveElement(*tmpFaceIt);
10306 if ( aResult != SEW_OK)
10309 list< int > nodeIDsToRemove;
10310 vector< const SMDS_MeshNode*> nodes;
10311 ElemFeatures elemType;
10313 // loop on nodes replacement map
10314 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10315 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10316 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10318 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10319 nodeIDsToRemove.push_back( nToRemove->GetID() );
10320 // loop on elements sharing nToRemove
10321 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10322 while ( invElemIt->more() ) {
10323 const SMDS_MeshElement* e = invElemIt->next();
10324 // get a new suite of nodes: make replacement
10325 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10326 nodes.resize( nbNodes );
10327 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10328 while ( nIt->more() ) {
10329 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10330 nnIt = nReplaceMap.find( n );
10331 if ( nnIt != nReplaceMap.end() ) {
10333 n = (*nnIt).second;
10337 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10338 // elemIDsToRemove.push_back( e->GetID() );
10342 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10343 aMesh->RemoveElement( e );
10345 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10347 AddToSameGroups( newElem, e, aMesh );
10348 if ( int aShapeId = e->getshapeId() )
10349 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10355 Remove( nodeIDsToRemove, true );
10360 //================================================================================
10362 * \brief Find corresponding nodes in two sets of faces
10363 * \param theSide1 - first face set
10364 * \param theSide2 - second first face
10365 * \param theFirstNode1 - a boundary node of set 1
10366 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10367 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10368 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10369 * \param nReplaceMap - output map of corresponding nodes
10370 * \return bool - is a success or not
10372 //================================================================================
10375 //#define DEBUG_MATCHING_NODES
10378 SMESH_MeshEditor::Sew_Error
10379 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10380 set<const SMDS_MeshElement*>& theSide2,
10381 const SMDS_MeshNode* theFirstNode1,
10382 const SMDS_MeshNode* theFirstNode2,
10383 const SMDS_MeshNode* theSecondNode1,
10384 const SMDS_MeshNode* theSecondNode2,
10385 TNodeNodeMap & nReplaceMap)
10387 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10389 nReplaceMap.clear();
10390 if ( theFirstNode1 != theFirstNode2 )
10391 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10392 if ( theSecondNode1 != theSecondNode2 )
10393 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10395 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10396 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10398 list< NLink > linkList[2];
10399 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10400 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10402 // loop on links in linkList; find faces by links and append links
10403 // of the found faces to linkList
10404 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10405 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10406 NLink link[] = { *linkIt[0], *linkIt[1] };
10407 if ( linkSet.find( link[0] ) == linkSet.end() )
10410 // by links, find faces in the face sets,
10411 // and find indices of link nodes in the found faces;
10412 // in a face set, there is only one or no face sharing a link
10413 // ---------------------------------------------------------------
10415 const SMDS_MeshElement* face[] = { 0, 0 };
10416 list<const SMDS_MeshNode*> notLinkNodes[2];
10417 //bool reverse[] = { false, false }; // order of notLinkNodes
10419 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10421 const SMDS_MeshNode* n1 = link[iSide].first;
10422 const SMDS_MeshNode* n2 = link[iSide].second;
10423 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10424 set< const SMDS_MeshElement* > facesOfNode1;
10425 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10427 // during a loop of the first node, we find all faces around n1,
10428 // during a loop of the second node, we find one face sharing both n1 and n2
10429 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10430 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10431 while ( fIt->more() ) { // loop on faces sharing a node
10432 const SMDS_MeshElement* f = fIt->next();
10433 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10434 ! facesOfNode1.insert( f ).second ) // f encounters twice
10436 if ( face[ iSide ] ) {
10437 MESSAGE( "2 faces per link " );
10438 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10441 faceSet->erase( f );
10443 // get not link nodes
10444 int nbN = f->NbNodes();
10445 if ( f->IsQuadratic() )
10447 nbNodes[ iSide ] = nbN;
10448 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10449 int i1 = f->GetNodeIndex( n1 );
10450 int i2 = f->GetNodeIndex( n2 );
10451 int iEnd = nbN, iBeg = -1, iDelta = 1;
10452 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10454 std::swap( iEnd, iBeg ); iDelta = -1;
10459 if ( i == iEnd ) i = iBeg + iDelta;
10460 if ( i == i1 ) break;
10461 nodes.push_back ( f->GetNode( i ) );
10467 // check similarity of elements of the sides
10468 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10469 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10470 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10471 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10474 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10478 // set nodes to merge
10479 // -------------------
10481 if ( face[0] && face[1] ) {
10482 if ( nbNodes[0] != nbNodes[1] ) {
10483 MESSAGE("Diff nb of face nodes");
10484 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10486 #ifdef DEBUG_MATCHING_NODES
10487 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10488 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10489 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10491 int nbN = nbNodes[0];
10493 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10494 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10495 for ( int i = 0 ; i < nbN - 2; ++i ) {
10496 #ifdef DEBUG_MATCHING_NODES
10497 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10499 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10503 // add other links of the face 1 to linkList
10504 // -----------------------------------------
10506 const SMDS_MeshElement* f0 = face[0];
10507 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10508 for ( int i = 0; i < nbN; i++ )
10510 const SMDS_MeshNode* n2 = f0->GetNode( i );
10511 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10512 linkSet.insert( SMESH_TLink( n1, n2 ));
10513 if ( !iter_isnew.second ) { // already in a set: no need to process
10514 linkSet.erase( iter_isnew.first );
10516 else // new in set == encountered for the first time: add
10518 #ifdef DEBUG_MATCHING_NODES
10519 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10520 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10522 linkList[0].push_back ( NLink( n1, n2 ));
10523 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10528 } // loop on link lists
10533 //================================================================================
10535 * \brief Create elements equal (on same nodes) to given ones
10536 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10537 * elements of the uppest dimension are duplicated.
10539 //================================================================================
10541 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10543 ClearLastCreated();
10544 SMESHDS_Mesh* mesh = GetMeshDS();
10546 // get an element type and an iterator over elements
10548 SMDSAbs_ElementType type;
10549 SMDS_ElemIteratorPtr elemIt;
10550 vector< const SMDS_MeshElement* > allElems;
10551 if ( theElements.empty() )
10553 if ( mesh->NbNodes() == 0 )
10555 // get most complex type
10556 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10557 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10558 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10560 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10561 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10566 // put all elements in the vector <allElems>
10567 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10568 elemIt = mesh->elementsIterator( type );
10569 while ( elemIt->more() )
10570 allElems.push_back( elemIt->next());
10571 elemIt = elemSetIterator( allElems );
10575 type = (*theElements.begin())->GetType();
10576 elemIt = elemSetIterator( theElements );
10579 // duplicate elements
10581 ElemFeatures elemType;
10583 vector< const SMDS_MeshNode* > nodes;
10584 while ( elemIt->more() )
10586 const SMDS_MeshElement* elem = elemIt->next();
10587 if ( elem->GetType() != type )
10590 elemType.Init( elem, /*basicOnly=*/false );
10591 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10593 AddElement( nodes, elemType );
10597 //================================================================================
10599 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10600 \param theElems - the list of elements (edges or faces) to be replicated
10601 The nodes for duplication could be found from these elements
10602 \param theNodesNot - list of nodes to NOT replicate
10603 \param theAffectedElems - the list of elements (cells and edges) to which the
10604 replicated nodes should be associated to.
10605 \return TRUE if operation has been completed successfully, FALSE otherwise
10607 //================================================================================
10609 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10610 const TIDSortedElemSet& theNodesNot,
10611 const TIDSortedElemSet& theAffectedElems )
10613 myLastCreatedElems.Clear();
10614 myLastCreatedNodes.Clear();
10616 if ( theElems.size() == 0 )
10619 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10624 TNodeNodeMap anOldNodeToNewNode;
10625 // duplicate elements and nodes
10626 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10627 // replce nodes by duplications
10628 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10632 //================================================================================
10634 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10635 \param theMeshDS - mesh instance
10636 \param theElems - the elements replicated or modified (nodes should be changed)
10637 \param theNodesNot - nodes to NOT replicate
10638 \param theNodeNodeMap - relation of old node to new created node
10639 \param theIsDoubleElem - flag os to replicate element or modify
10640 \return TRUE if operation has been completed successfully, FALSE otherwise
10642 //================================================================================
10644 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10645 const TIDSortedElemSet& theElems,
10646 const TIDSortedElemSet& theNodesNot,
10647 TNodeNodeMap& theNodeNodeMap,
10648 const bool theIsDoubleElem )
10650 MESSAGE("doubleNodes");
10651 // iterate through element and duplicate them (by nodes duplication)
10653 std::vector<const SMDS_MeshNode*> newNodes;
10654 ElemFeatures elemType;
10656 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10657 for ( ; elemItr != theElems.end(); ++elemItr )
10659 const SMDS_MeshElement* anElem = *elemItr;
10663 // duplicate nodes to duplicate element
10664 bool isDuplicate = false;
10665 newNodes.resize( anElem->NbNodes() );
10666 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10668 while ( anIter->more() )
10670 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10671 const SMDS_MeshNode* aNewNode = aCurrNode;
10672 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10673 if ( n2n != theNodeNodeMap.end() )
10675 aNewNode = n2n->second;
10677 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10680 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10681 copyPosition( aCurrNode, aNewNode );
10682 theNodeNodeMap[ aCurrNode ] = aNewNode;
10683 myLastCreatedNodes.Append( aNewNode );
10685 isDuplicate |= (aCurrNode != aNewNode);
10686 newNodes[ ind++ ] = aNewNode;
10688 if ( !isDuplicate )
10691 if ( theIsDoubleElem )
10692 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10694 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10701 //================================================================================
10703 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10704 \param theNodes - identifiers of nodes to be doubled
10705 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10706 nodes. If list of element identifiers is empty then nodes are doubled but
10707 they not assigned to elements
10708 \return TRUE if operation has been completed successfully, FALSE otherwise
10710 //================================================================================
10712 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10713 const std::list< int >& theListOfModifiedElems )
10715 MESSAGE("DoubleNodes");
10716 myLastCreatedElems.Clear();
10717 myLastCreatedNodes.Clear();
10719 if ( theListOfNodes.size() == 0 )
10722 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10726 // iterate through nodes and duplicate them
10728 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10730 std::list< int >::const_iterator aNodeIter;
10731 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10733 int aCurr = *aNodeIter;
10734 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10740 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10743 copyPosition( aNode, aNewNode );
10744 anOldNodeToNewNode[ aNode ] = aNewNode;
10745 myLastCreatedNodes.Append( aNewNode );
10749 // Create map of new nodes for modified elements
10751 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10753 std::list< int >::const_iterator anElemIter;
10754 for ( anElemIter = theListOfModifiedElems.begin();
10755 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10757 int aCurr = *anElemIter;
10758 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10762 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10764 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10766 while ( anIter->more() )
10768 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10769 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10771 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10772 aNodeArr[ ind++ ] = aNewNode;
10775 aNodeArr[ ind++ ] = aCurrNode;
10777 anElemToNodes[ anElem ] = aNodeArr;
10780 // Change nodes of elements
10782 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10783 anElemToNodesIter = anElemToNodes.begin();
10784 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10786 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10787 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10790 MESSAGE("ChangeElementNodes");
10791 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10800 //================================================================================
10802 \brief Check if element located inside shape
10803 \return TRUE if IN or ON shape, FALSE otherwise
10805 //================================================================================
10807 template<class Classifier>
10808 bool isInside(const SMDS_MeshElement* theElem,
10809 Classifier& theClassifier,
10810 const double theTol)
10812 gp_XYZ centerXYZ (0, 0, 0);
10813 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10814 while (aNodeItr->more())
10815 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10817 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10818 theClassifier.Perform(aPnt, theTol);
10819 TopAbs_State aState = theClassifier.State();
10820 return (aState == TopAbs_IN || aState == TopAbs_ON );
10823 //================================================================================
10825 * \brief Classifier of the 3D point on the TopoDS_Face
10826 * with interaface suitable for isInside()
10828 //================================================================================
10830 struct _FaceClassifier
10832 Extrema_ExtPS _extremum;
10833 BRepAdaptor_Surface _surface;
10834 TopAbs_State _state;
10836 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10838 _extremum.Initialize( _surface,
10839 _surface.FirstUParameter(), _surface.LastUParameter(),
10840 _surface.FirstVParameter(), _surface.LastVParameter(),
10841 _surface.Tolerance(), _surface.Tolerance() );
10843 void Perform(const gp_Pnt& aPnt, double theTol)
10846 _state = TopAbs_OUT;
10847 _extremum.Perform(aPnt);
10848 if ( _extremum.IsDone() )
10849 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10850 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10852 TopAbs_State State() const
10859 //================================================================================
10861 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10862 This method is the first step of DoubleNodeElemGroupsInRegion.
10863 \param theElems - list of groups of elements (edges or faces) to be replicated
10864 \param theNodesNot - list of groups of nodes not to replicated
10865 \param theShape - shape to detect affected elements (element which geometric center
10866 located on or inside shape). If the shape is null, detection is done on faces orientations
10867 (select elements with a gravity center on the side given by faces normals).
10868 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10869 The replicated nodes should be associated to affected elements.
10870 \return groups of affected elements
10871 \sa DoubleNodeElemGroupsInRegion()
10873 //================================================================================
10875 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10876 const TIDSortedElemSet& theNodesNot,
10877 const TopoDS_Shape& theShape,
10878 TIDSortedElemSet& theAffectedElems)
10880 if ( theShape.IsNull() )
10882 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10883 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10884 std::set<const SMDS_MeshElement*> edgesToCheck;
10885 alreadyCheckedNodes.clear();
10886 alreadyCheckedElems.clear();
10887 edgesToCheck.clear();
10889 // --- iterates on elements to be replicated and get elements by back references from their nodes
10891 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10893 for ( ielem=1; elemItr != theElems.end(); ++elemItr )
10895 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10896 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10899 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10900 MESSAGE("element " << ielem++ << " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10901 std::set<const SMDS_MeshNode*> nodesElem;
10903 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10904 while ( nodeItr->more() )
10906 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10907 nodesElem.insert(aNode);
10909 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10910 for (; nodit != nodesElem.end(); nodit++)
10912 MESSAGE(" noeud ");
10913 const SMDS_MeshNode* aNode = *nodit;
10914 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10916 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10918 alreadyCheckedNodes.insert(aNode);
10919 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10920 while ( backElemItr->more() )
10922 MESSAGE(" backelem ");
10923 const SMDS_MeshElement* curElem = backElemItr->next();
10924 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10926 if (theElems.find(curElem) != theElems.end())
10928 alreadyCheckedElems.insert(curElem);
10929 double x=0, y=0, z=0;
10931 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10932 while ( nodeItr2->more() )
10934 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10935 x += anotherNode->X();
10936 y += anotherNode->Y();
10937 z += anotherNode->Z();
10941 p.SetCoord( x/nb -aNode->X(),
10943 z/nb -aNode->Z() );
10944 MESSAGE(" check " << p.X() << " " << p.Y() << " " << p.Z());
10947 MESSAGE(" --- inserted")
10948 theAffectedElems.insert( curElem );
10950 else if (curElem->GetType() == SMDSAbs_Edge)
10951 edgesToCheck.insert(curElem);
10955 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10956 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10957 for( ; eit != edgesToCheck.end(); eit++)
10959 bool onside = true;
10960 const SMDS_MeshElement* anEdge = *eit;
10961 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10962 while ( nodeItr->more() )
10964 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10965 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10973 MESSAGE(" --- edge onside inserted")
10974 theAffectedElems.insert(anEdge);
10980 const double aTol = Precision::Confusion();
10981 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10982 auto_ptr<_FaceClassifier> aFaceClassifier;
10983 if ( theShape.ShapeType() == TopAbs_SOLID )
10985 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10986 bsc3d->PerformInfinitePoint(aTol);
10988 else if (theShape.ShapeType() == TopAbs_FACE )
10990 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10993 // iterates on indicated elements and get elements by back references from their nodes
10994 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10996 for ( ielem = 1; elemItr != theElems.end(); ++elemItr )
10998 MESSAGE("element " << ielem++);
10999 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11002 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11003 while ( nodeItr->more() )
11005 MESSAGE(" noeud ");
11006 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11007 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11009 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11010 while ( backElemItr->more() )
11012 MESSAGE(" backelem ");
11013 const SMDS_MeshElement* curElem = backElemItr->next();
11014 if ( curElem && theElems.find(curElem) == theElems.end() &&
11016 isInside( curElem, *bsc3d, aTol ) :
11017 isInside( curElem, *aFaceClassifier, aTol )))
11018 theAffectedElems.insert( curElem );
11026 //================================================================================
11028 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11029 \param theElems - group of of elements (edges or faces) to be replicated
11030 \param theNodesNot - group of nodes not to replicate
11031 \param theShape - shape to detect affected elements (element which geometric center
11032 located on or inside shape).
11033 The replicated nodes should be associated to affected elements.
11034 \return TRUE if operation has been completed successfully, FALSE otherwise
11036 //================================================================================
11038 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11039 const TIDSortedElemSet& theNodesNot,
11040 const TopoDS_Shape& theShape )
11042 if ( theShape.IsNull() )
11045 const double aTol = Precision::Confusion();
11046 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11047 auto_ptr<_FaceClassifier> aFaceClassifier;
11048 if ( theShape.ShapeType() == TopAbs_SOLID )
11050 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11051 bsc3d->PerformInfinitePoint(aTol);
11053 else if (theShape.ShapeType() == TopAbs_FACE )
11055 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11058 // iterates on indicated elements and get elements by back references from their nodes
11059 TIDSortedElemSet anAffected;
11060 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11061 for ( ; elemItr != theElems.end(); ++elemItr )
11063 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11067 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11068 while ( nodeItr->more() )
11070 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11071 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11073 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11074 while ( backElemItr->more() )
11076 const SMDS_MeshElement* curElem = backElemItr->next();
11077 if ( curElem && theElems.find(curElem) == theElems.end() &&
11079 isInside( curElem, *bsc3d, aTol ) :
11080 isInside( curElem, *aFaceClassifier, aTol )))
11081 anAffected.insert( curElem );
11085 return DoubleNodes( theElems, theNodesNot, anAffected );
11089 * \brief compute an oriented angle between two planes defined by four points.
11090 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11091 * @param p0 base of the rotation axe
11092 * @param p1 extremity of the rotation axe
11093 * @param g1 belongs to the first plane
11094 * @param g2 belongs to the second plane
11096 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11098 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
11099 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
11100 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
11101 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
11102 gp_Vec vref(p0, p1);
11105 gp_Vec n1 = vref.Crossed(v1);
11106 gp_Vec n2 = vref.Crossed(v2);
11108 return n2.AngleWithRef(n1, vref);
11110 catch ( Standard_Failure ) {
11112 return Max( v1.Magnitude(), v2.Magnitude() );
11116 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11117 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11118 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11119 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11120 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11121 * 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.
11122 * 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.
11123 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11124 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11125 * \param theElems - list of groups of volumes, where a group of volume is a set of
11126 * SMDS_MeshElements sorted by Id.
11127 * \param createJointElems - if TRUE, create the elements
11128 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11129 * the boundary between \a theDomains and the rest mesh
11130 * \return TRUE if operation has been completed successfully, FALSE otherwise
11132 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11133 bool createJointElems,
11134 bool onAllBoundaries)
11136 MESSAGE("----------------------------------------------");
11137 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11138 MESSAGE("----------------------------------------------");
11140 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11141 meshDS->BuildDownWardConnectivity(true);
11143 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11145 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11146 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11147 // build the list of nodes shared by 2 or more domains, with their domain indexes
11149 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11150 std::map<int,int>celldom; // cell vtkId --> domain
11151 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11152 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11153 faceDomains.clear();
11155 cellDomains.clear();
11156 nodeDomains.clear();
11157 std::map<int,int> emptyMap;
11158 std::set<int> emptySet;
11161 MESSAGE(".. Number of domains :"<<theElems.size());
11163 TIDSortedElemSet theRestDomElems;
11164 const int iRestDom = -1;
11165 const int idom0 = onAllBoundaries ? iRestDom : 0;
11166 const int nbDomains = theElems.size();
11168 // Check if the domains do not share an element
11169 for (int idom = 0; idom < nbDomains-1; idom++)
11171 // MESSAGE("... Check of domain #" << idom);
11172 const TIDSortedElemSet& domain = theElems[idom];
11173 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11174 for (; elemItr != domain.end(); ++elemItr)
11176 const SMDS_MeshElement* anElem = *elemItr;
11177 int idombisdeb = idom + 1 ;
11178 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
11180 const TIDSortedElemSet& domainbis = theElems[idombis];
11181 if ( domainbis.count(anElem) )
11183 MESSAGE(".... Domain #" << idom);
11184 MESSAGE(".... Domain #" << idombis);
11185 throw SALOME_Exception("The domains are not disjoint.");
11192 for (int idom = 0; idom < nbDomains; idom++)
11195 // --- build a map (face to duplicate --> volume to modify)
11196 // with all the faces shared by 2 domains (group of elements)
11197 // and corresponding volume of this domain, for each shared face.
11198 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11200 MESSAGE("... Neighbors of domain #" << idom);
11201 const TIDSortedElemSet& domain = theElems[idom];
11202 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11203 for (; elemItr != domain.end(); ++elemItr)
11205 const SMDS_MeshElement* anElem = *elemItr;
11208 int vtkId = anElem->getVtkId();
11209 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11210 int neighborsVtkIds[NBMAXNEIGHBORS];
11211 int downIds[NBMAXNEIGHBORS];
11212 unsigned char downTypes[NBMAXNEIGHBORS];
11213 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11214 for (int n = 0; n < nbNeighbors; n++)
11216 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11217 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11218 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11221 for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11223 // MESSAGE("Domain " << idombis);
11224 const TIDSortedElemSet& domainbis = theElems[idombis];
11225 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11227 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11229 DownIdType face(downIds[n], downTypes[n]);
11230 if (!faceDomains[face].count(idom))
11232 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11233 celldom[vtkId] = idom;
11234 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11238 theRestDomElems.insert( elem );
11239 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11240 celldom[neighborsVtkIds[n]] = iRestDom;
11248 //MESSAGE("Number of shared faces " << faceDomains.size());
11249 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11251 // --- explore the shared faces domain by domain,
11252 // explore the nodes of the face and see if they belong to a cell in the domain,
11253 // which has only a node or an edge on the border (not a shared face)
11255 for (int idomain = idom0; idomain < nbDomains; idomain++)
11257 //MESSAGE("Domain " << idomain);
11258 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11259 itface = faceDomains.begin();
11260 for (; itface != faceDomains.end(); ++itface)
11262 const std::map<int, int>& domvol = itface->second;
11263 if (!domvol.count(idomain))
11265 DownIdType face = itface->first;
11266 //MESSAGE(" --- face " << face.cellId);
11267 std::set<int> oldNodes;
11269 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11270 std::set<int>::iterator itn = oldNodes.begin();
11271 for (; itn != oldNodes.end(); ++itn)
11274 //MESSAGE(" node " << oldId);
11275 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11276 for (int i=0; i<l.ncells; i++)
11278 int vtkId = l.cells[i];
11279 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11280 if (!domain.count(anElem))
11282 int vtkType = grid->GetCellType(vtkId);
11283 int downId = grid->CellIdToDownId(vtkId);
11286 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11287 continue; // not OK at this stage of the algorithm:
11288 //no cells created after BuildDownWardConnectivity
11290 DownIdType aCell(downId, vtkType);
11291 cellDomains[aCell][idomain] = vtkId;
11292 celldom[vtkId] = idomain;
11293 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11299 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11300 // for each shared face, get the nodes
11301 // for each node, for each domain of the face, create a clone of the node
11303 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11304 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11305 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11307 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11308 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11309 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11311 MESSAGE(".. Duplication of the nodes");
11312 for (int idomain = idom0; idomain < nbDomains; idomain++)
11314 itface = faceDomains.begin();
11315 for (; itface != faceDomains.end(); ++itface)
11317 const std::map<int, int>& domvol = itface->second;
11318 if (!domvol.count(idomain))
11320 DownIdType face = itface->first;
11321 //MESSAGE(" --- face " << face.cellId);
11322 std::set<int> oldNodes;
11324 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11325 std::set<int>::iterator itn = oldNodes.begin();
11326 for (; itn != oldNodes.end(); ++itn)
11329 if (nodeDomains[oldId].empty())
11331 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11332 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11334 std::map<int, int>::const_iterator itdom = domvol.begin();
11335 for (; itdom != domvol.end(); ++itdom)
11337 int idom = itdom->first;
11338 //MESSAGE(" domain " << idom);
11339 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11341 if (nodeDomains[oldId].size() >= 2) // a multiple node
11343 vector<int> orderedDoms;
11344 //MESSAGE("multiple node " << oldId);
11345 if (mutipleNodes.count(oldId))
11346 orderedDoms = mutipleNodes[oldId];
11349 map<int,int>::iterator it = nodeDomains[oldId].begin();
11350 for (; it != nodeDomains[oldId].end(); ++it)
11351 orderedDoms.push_back(it->first);
11353 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11354 //stringstream txt;
11355 //for (int i=0; i<orderedDoms.size(); i++)
11356 // txt << orderedDoms[i] << " ";
11357 //MESSAGE("orderedDoms " << txt.str());
11358 mutipleNodes[oldId] = orderedDoms;
11360 double *coords = grid->GetPoint(oldId);
11361 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11362 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11363 int newId = newNode->getVtkId();
11364 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11365 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11372 MESSAGE(".. Creation of elements");
11373 for (int idomain = idom0; idomain < nbDomains; idomain++)
11375 itface = faceDomains.begin();
11376 for (; itface != faceDomains.end(); ++itface)
11378 std::map<int, int> domvol = itface->second;
11379 if (!domvol.count(idomain))
11381 DownIdType face = itface->first;
11382 //MESSAGE(" --- face " << face.cellId);
11383 std::set<int> oldNodes;
11385 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11386 int nbMultipleNodes = 0;
11387 std::set<int>::iterator itn = oldNodes.begin();
11388 for (; itn != oldNodes.end(); ++itn)
11391 if (mutipleNodes.count(oldId))
11394 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11396 //MESSAGE("multiple Nodes detected on a shared face");
11397 int downId = itface->first.cellId;
11398 unsigned char cellType = itface->first.cellType;
11399 // --- shared edge or shared face ?
11400 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11403 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11404 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11405 if (mutipleNodes.count(nodes[i]))
11406 if (!mutipleNodesToFace.count(nodes[i]))
11407 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11409 else // shared face (between two volumes)
11411 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11412 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11413 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11414 for (int ie =0; ie < nbEdges; ie++)
11417 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11418 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11420 vector<int> vn0 = mutipleNodes[nodes[0]];
11421 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11423 for (int i0 = 0; i0 < vn0.size(); i0++)
11424 for (int i1 = 0; i1 < vn1.size(); i1++)
11425 if (vn0[i0] == vn1[i1])
11426 doms.push_back(vn0[i0]);
11427 if (doms.size() >2)
11429 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11430 double *coords = grid->GetPoint(nodes[0]);
11431 gp_Pnt p0(coords[0], coords[1], coords[2]);
11432 coords = grid->GetPoint(nodes[nbNodes - 1]);
11433 gp_Pnt p1(coords[0], coords[1], coords[2]);
11435 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11436 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11437 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11438 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11439 for (int id=0; id < doms.size(); id++)
11441 int idom = doms[id];
11442 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11443 for (int ivol=0; ivol<nbvol; ivol++)
11445 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11446 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11447 if (domain.count(elem))
11449 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11450 domvol[idom] = svol;
11451 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11453 vtkIdType npts = 0;
11454 vtkIdType* pts = 0;
11455 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11456 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11459 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11460 angleDom[idom] = 0;
11464 gp_Pnt g(values[0], values[1], values[2]);
11465 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11466 //MESSAGE(" angle=" << angleDom[idom]);
11472 map<double, int> sortedDom; // sort domains by angle
11473 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11474 sortedDom[ia->second] = ia->first;
11475 vector<int> vnodes;
11477 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11479 vdom.push_back(ib->second);
11480 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11482 for (int ino = 0; ino < nbNodes; ino++)
11483 vnodes.push_back(nodes[ino]);
11484 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11493 // --- iterate on shared faces (volumes to modify, face to extrude)
11494 // get node id's of the face (id SMDS = id VTK)
11495 // create flat element with old and new nodes if requested
11497 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11498 // (domain1 X domain2) = domain1 + MAXINT*domain2
11500 std::map<int, std::map<long,int> > nodeQuadDomains;
11501 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11503 MESSAGE(".. Creation of elements: simple junction");
11504 if (createJointElems)
11507 string joints2DName = "joints2D";
11508 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11509 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11510 string joints3DName = "joints3D";
11511 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11512 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11514 itface = faceDomains.begin();
11515 for (; itface != faceDomains.end(); ++itface)
11517 DownIdType face = itface->first;
11518 std::set<int> oldNodes;
11519 std::set<int>::iterator itn;
11521 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11523 std::map<int, int> domvol = itface->second;
11524 std::map<int, int>::iterator itdom = domvol.begin();
11525 int dom1 = itdom->first;
11526 int vtkVolId = itdom->second;
11528 int dom2 = itdom->first;
11529 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11531 stringstream grpname;
11534 grpname << dom1 << "_" << dom2;
11536 grpname << dom2 << "_" << dom1;
11537 string namegrp = grpname.str();
11538 if (!mapOfJunctionGroups.count(namegrp))
11539 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11540 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11542 sgrp->Add(vol->GetID());
11543 if (vol->GetType() == SMDSAbs_Volume)
11544 joints3DGrp->Add(vol->GetID());
11545 else if (vol->GetType() == SMDSAbs_Face)
11546 joints2DGrp->Add(vol->GetID());
11550 // --- create volumes on multiple domain intersection if requested
11551 // iterate on mutipleNodesToFace
11552 // iterate on edgesMultiDomains
11554 MESSAGE(".. Creation of elements: multiple junction");
11555 if (createJointElems)
11557 // --- iterate on mutipleNodesToFace
11559 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11560 for (; itn != mutipleNodesToFace.end(); ++itn)
11562 int node = itn->first;
11563 vector<int> orderDom = itn->second;
11564 vector<vtkIdType> orderedNodes;
11565 for (int idom = 0; idom <orderDom.size(); idom++)
11566 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11567 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11569 stringstream grpname;
11571 grpname << 0 << "_" << 0;
11573 string namegrp = grpname.str();
11574 if (!mapOfJunctionGroups.count(namegrp))
11575 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11576 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11578 sgrp->Add(face->GetID());
11581 // --- iterate on edgesMultiDomains
11583 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11584 for (; ite != edgesMultiDomains.end(); ++ite)
11586 vector<int> nodes = ite->first;
11587 vector<int> orderDom = ite->second;
11588 vector<vtkIdType> orderedNodes;
11589 if (nodes.size() == 2)
11591 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11592 for (int ino=0; ino < nodes.size(); ino++)
11593 if (orderDom.size() == 3)
11594 for (int idom = 0; idom <orderDom.size(); idom++)
11595 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11597 for (int idom = orderDom.size()-1; idom >=0; idom--)
11598 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11599 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11602 string namegrp = "jointsMultiples";
11603 if (!mapOfJunctionGroups.count(namegrp))
11604 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11605 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11607 sgrp->Add(vol->GetID());
11611 INFOS("Quadratic multiple joints not implemented");
11612 // TODO quadratic nodes
11617 // --- list the explicit faces and edges of the mesh that need to be modified,
11618 // i.e. faces and edges built with one or more duplicated nodes.
11619 // associate these faces or edges to their corresponding domain.
11620 // only the first domain found is kept when a face or edge is shared
11622 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11623 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11624 faceOrEdgeDom.clear();
11627 MESSAGE(".. Modification of elements");
11628 for (int idomain = idom0; idomain < nbDomains; idomain++)
11630 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11631 for (; itnod != nodeDomains.end(); ++itnod)
11633 int oldId = itnod->first;
11634 //MESSAGE(" node " << oldId);
11635 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11636 for (int i = 0; i < l.ncells; i++)
11638 int vtkId = l.cells[i];
11639 int vtkType = grid->GetCellType(vtkId);
11640 int downId = grid->CellIdToDownId(vtkId);
11642 continue; // new cells: not to be modified
11643 DownIdType aCell(downId, vtkType);
11644 int volParents[1000];
11645 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11646 for (int j = 0; j < nbvol; j++)
11647 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11648 if (!feDom.count(vtkId))
11650 feDom[vtkId] = idomain;
11651 faceOrEdgeDom[aCell] = emptyMap;
11652 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11653 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11654 // << " type " << vtkType << " downId " << downId);
11660 // --- iterate on shared faces (volumes to modify, face to extrude)
11661 // get node id's of the face
11662 // replace old nodes by new nodes in volumes, and update inverse connectivity
11664 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11665 for (int m=0; m<3; m++)
11667 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11668 itface = (*amap).begin();
11669 for (; itface != (*amap).end(); ++itface)
11671 DownIdType face = itface->first;
11672 std::set<int> oldNodes;
11673 std::set<int>::iterator itn;
11675 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11676 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11677 std::map<int, int> localClonedNodeIds;
11679 std::map<int, int> domvol = itface->second;
11680 std::map<int, int>::iterator itdom = domvol.begin();
11681 for (; itdom != domvol.end(); ++itdom)
11683 int idom = itdom->first;
11684 int vtkVolId = itdom->second;
11685 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11686 localClonedNodeIds.clear();
11687 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11690 if (nodeDomains[oldId].count(idom))
11692 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11693 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11696 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11701 // Remove empty groups (issue 0022812)
11702 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11703 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11705 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11706 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11709 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11710 grid->BuildLinks();
11718 * \brief Double nodes on some external faces and create flat elements.
11719 * Flat elements are mainly used by some types of mechanic calculations.
11721 * Each group of the list must be constituted of faces.
11722 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11723 * @param theElems - list of groups of faces, where a group of faces is a set of
11724 * SMDS_MeshElements sorted by Id.
11725 * @return TRUE if operation has been completed successfully, FALSE otherwise
11727 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11729 MESSAGE("-------------------------------------------------");
11730 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11731 MESSAGE("-------------------------------------------------");
11733 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11735 // --- For each group of faces
11736 // duplicate the nodes, create a flat element based on the face
11737 // replace the nodes of the faces by their clones
11739 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11740 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11741 clonedNodes.clear();
11742 intermediateNodes.clear();
11743 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11744 mapOfJunctionGroups.clear();
11746 for (int idom = 0; idom < theElems.size(); idom++)
11748 const TIDSortedElemSet& domain = theElems[idom];
11749 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11750 for (; elemItr != domain.end(); ++elemItr)
11752 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11753 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11756 // MESSAGE("aFace=" << aFace->GetID());
11757 bool isQuad = aFace->IsQuadratic();
11758 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11760 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11762 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11763 while (nodeIt->more())
11765 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11766 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11768 ln2.push_back(node);
11770 ln0.push_back(node);
11772 const SMDS_MeshNode* clone = 0;
11773 if (!clonedNodes.count(node))
11775 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11776 copyPosition( node, clone );
11777 clonedNodes[node] = clone;
11780 clone = clonedNodes[node];
11783 ln3.push_back(clone);
11785 ln1.push_back(clone);
11787 const SMDS_MeshNode* inter = 0;
11788 if (isQuad && (!isMedium))
11790 if (!intermediateNodes.count(node))
11792 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11793 copyPosition( node, inter );
11794 intermediateNodes[node] = inter;
11797 inter = intermediateNodes[node];
11798 ln4.push_back(inter);
11802 // --- extrude the face
11804 vector<const SMDS_MeshNode*> ln;
11805 SMDS_MeshVolume* vol = 0;
11806 vtkIdType aType = aFace->GetVtkType();
11810 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11811 // MESSAGE("vol prism " << vol->GetID());
11812 ln.push_back(ln1[0]);
11813 ln.push_back(ln1[1]);
11814 ln.push_back(ln1[2]);
11817 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11818 // MESSAGE("vol hexa " << vol->GetID());
11819 ln.push_back(ln1[0]);
11820 ln.push_back(ln1[1]);
11821 ln.push_back(ln1[2]);
11822 ln.push_back(ln1[3]);
11824 case VTK_QUADRATIC_TRIANGLE:
11825 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11826 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11827 // MESSAGE("vol quad prism " << vol->GetID());
11828 ln.push_back(ln1[0]);
11829 ln.push_back(ln1[1]);
11830 ln.push_back(ln1[2]);
11831 ln.push_back(ln3[0]);
11832 ln.push_back(ln3[1]);
11833 ln.push_back(ln3[2]);
11835 case VTK_QUADRATIC_QUAD:
11836 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11837 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11838 // ln4[0], ln4[1], ln4[2], ln4[3]);
11839 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11840 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11841 ln4[0], ln4[1], ln4[2], ln4[3]);
11842 // MESSAGE("vol quad hexa " << vol->GetID());
11843 ln.push_back(ln1[0]);
11844 ln.push_back(ln1[1]);
11845 ln.push_back(ln1[2]);
11846 ln.push_back(ln1[3]);
11847 ln.push_back(ln3[0]);
11848 ln.push_back(ln3[1]);
11849 ln.push_back(ln3[2]);
11850 ln.push_back(ln3[3]);
11860 stringstream grpname;
11864 string namegrp = grpname.str();
11865 if (!mapOfJunctionGroups.count(namegrp))
11866 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11867 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11869 sgrp->Add(vol->GetID());
11872 // --- modify the face
11874 aFace->ChangeNodes(&ln[0], ln.size());
11881 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11882 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11883 * groups of faces to remove inside the object, (idem edges).
11884 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11886 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11887 const TopoDS_Shape& theShape,
11888 SMESH_NodeSearcher* theNodeSearcher,
11889 const char* groupName,
11890 std::vector<double>& nodesCoords,
11891 std::vector<std::vector<int> >& listOfListOfNodes)
11893 MESSAGE("--------------------------------");
11894 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11895 MESSAGE("--------------------------------");
11897 // --- zone of volumes to remove is given :
11898 // 1 either by a geom shape (one or more vertices) and a radius,
11899 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11900 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11901 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11902 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11903 // defined by it's name.
11905 SMESHDS_GroupBase* groupDS = 0;
11906 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11907 while ( groupIt->more() )
11910 SMESH_Group * group = groupIt->next();
11911 if ( !group ) continue;
11912 groupDS = group->GetGroupDS();
11913 if ( !groupDS || groupDS->IsEmpty() ) continue;
11914 std::string grpName = group->GetName();
11915 //MESSAGE("grpName=" << grpName);
11916 if (grpName == groupName)
11922 bool isNodeGroup = false;
11923 bool isNodeCoords = false;
11926 if (groupDS->GetType() != SMDSAbs_Node)
11928 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11931 if (nodesCoords.size() > 0)
11932 isNodeCoords = true; // a list o nodes given by their coordinates
11933 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11935 // --- define groups to build
11937 int idg; // --- group of SMDS volumes
11938 string grpvName = groupName;
11939 grpvName += "_vol";
11940 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11943 MESSAGE("group not created " << grpvName);
11946 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11948 int idgs; // --- group of SMDS faces on the skin
11949 string grpsName = groupName;
11950 grpsName += "_skin";
11951 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11954 MESSAGE("group not created " << grpsName);
11957 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11959 int idgi; // --- group of SMDS faces internal (several shapes)
11960 string grpiName = groupName;
11961 grpiName += "_internalFaces";
11962 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11965 MESSAGE("group not created " << grpiName);
11968 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11970 int idgei; // --- group of SMDS faces internal (several shapes)
11971 string grpeiName = groupName;
11972 grpeiName += "_internalEdges";
11973 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11976 MESSAGE("group not created " << grpeiName);
11979 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11981 // --- build downward connectivity
11983 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11984 meshDS->BuildDownWardConnectivity(true);
11985 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11987 // --- set of volumes detected inside
11989 std::set<int> setOfInsideVol;
11990 std::set<int> setOfVolToCheck;
11992 std::vector<gp_Pnt> gpnts;
11995 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11997 MESSAGE("group of nodes provided");
11998 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11999 while ( elemIt->more() )
12001 const SMDS_MeshElement* elem = elemIt->next();
12004 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12007 SMDS_MeshElement* vol = 0;
12008 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12009 while (volItr->more())
12011 vol = (SMDS_MeshElement*)volItr->next();
12012 setOfInsideVol.insert(vol->getVtkId());
12013 sgrp->Add(vol->GetID());
12017 else if (isNodeCoords)
12019 MESSAGE("list of nodes coordinates provided");
12022 while (i < nodesCoords.size()-2)
12024 double x = nodesCoords[i++];
12025 double y = nodesCoords[i++];
12026 double z = nodesCoords[i++];
12027 gp_Pnt p = gp_Pnt(x, y ,z);
12028 gpnts.push_back(p);
12029 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12033 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12035 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12036 TopTools_IndexedMapOfShape vertexMap;
12037 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12038 gp_Pnt p = gp_Pnt(0,0,0);
12039 if (vertexMap.Extent() < 1)
12042 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12044 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12045 p = BRep_Tool::Pnt(vertex);
12046 gpnts.push_back(p);
12047 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12051 if (gpnts.size() > 0)
12054 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12056 nodeId = startNode->GetID();
12057 MESSAGE("nodeId " << nodeId);
12059 double radius2 = radius*radius;
12060 MESSAGE("radius2 " << radius2);
12062 // --- volumes on start node
12064 setOfVolToCheck.clear();
12065 SMDS_MeshElement* startVol = 0;
12066 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12067 while (volItr->more())
12069 startVol = (SMDS_MeshElement*)volItr->next();
12070 setOfVolToCheck.insert(startVol->getVtkId());
12072 if (setOfVolToCheck.empty())
12074 MESSAGE("No volumes found");
12078 // --- starting with central volumes then their neighbors, check if they are inside
12079 // or outside the domain, until no more new neighbor volume is inside.
12080 // Fill the group of inside volumes
12082 std::map<int, double> mapOfNodeDistance2;
12083 mapOfNodeDistance2.clear();
12084 std::set<int> setOfOutsideVol;
12085 while (!setOfVolToCheck.empty())
12087 std::set<int>::iterator it = setOfVolToCheck.begin();
12089 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12090 bool volInside = false;
12091 vtkIdType npts = 0;
12092 vtkIdType* pts = 0;
12093 grid->GetCellPoints(vtkId, npts, pts);
12094 for (int i=0; i<npts; i++)
12096 double distance2 = 0;
12097 if (mapOfNodeDistance2.count(pts[i]))
12099 distance2 = mapOfNodeDistance2[pts[i]];
12100 MESSAGE("point " << pts[i] << " distance2 " << distance2);
12104 double *coords = grid->GetPoint(pts[i]);
12105 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12107 for (int j=0; j<gpnts.size(); j++)
12109 double d2 = aPoint.SquareDistance(gpnts[j]);
12110 if (d2 < distance2)
12113 if (distance2 < radius2)
12117 mapOfNodeDistance2[pts[i]] = distance2;
12118 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12120 if (distance2 < radius2)
12122 volInside = true; // one or more nodes inside the domain
12123 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12129 setOfInsideVol.insert(vtkId);
12130 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12131 int neighborsVtkIds[NBMAXNEIGHBORS];
12132 int downIds[NBMAXNEIGHBORS];
12133 unsigned char downTypes[NBMAXNEIGHBORS];
12134 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12135 for (int n = 0; n < nbNeighbors; n++)
12136 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12137 setOfVolToCheck.insert(neighborsVtkIds[n]);
12141 setOfOutsideVol.insert(vtkId);
12142 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12144 setOfVolToCheck.erase(vtkId);
12148 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12149 // If yes, add the volume to the inside set
12151 bool addedInside = true;
12152 std::set<int> setOfVolToReCheck;
12153 while (addedInside)
12155 MESSAGE(" --------------------------- re check");
12156 addedInside = false;
12157 std::set<int>::iterator itv = setOfInsideVol.begin();
12158 for (; itv != setOfInsideVol.end(); ++itv)
12161 int neighborsVtkIds[NBMAXNEIGHBORS];
12162 int downIds[NBMAXNEIGHBORS];
12163 unsigned char downTypes[NBMAXNEIGHBORS];
12164 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12165 for (int n = 0; n < nbNeighbors; n++)
12166 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12167 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12169 setOfVolToCheck = setOfVolToReCheck;
12170 setOfVolToReCheck.clear();
12171 while (!setOfVolToCheck.empty())
12173 std::set<int>::iterator it = setOfVolToCheck.begin();
12175 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12177 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12178 int countInside = 0;
12179 int neighborsVtkIds[NBMAXNEIGHBORS];
12180 int downIds[NBMAXNEIGHBORS];
12181 unsigned char downTypes[NBMAXNEIGHBORS];
12182 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12183 for (int n = 0; n < nbNeighbors; n++)
12184 if (setOfInsideVol.count(neighborsVtkIds[n]))
12186 MESSAGE("countInside " << countInside);
12187 if (countInside > 1)
12189 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12190 setOfInsideVol.insert(vtkId);
12191 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12192 addedInside = true;
12195 setOfVolToReCheck.insert(vtkId);
12197 setOfVolToCheck.erase(vtkId);
12201 // --- map of Downward faces at the boundary, inside the global volume
12202 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12203 // fill group of SMDS faces inside the volume (when several volume shapes)
12204 // fill group of SMDS faces on the skin of the global volume (if skin)
12206 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12207 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12208 std::set<int>::iterator it = setOfInsideVol.begin();
12209 for (; it != setOfInsideVol.end(); ++it)
12212 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12213 int neighborsVtkIds[NBMAXNEIGHBORS];
12214 int downIds[NBMAXNEIGHBORS];
12215 unsigned char downTypes[NBMAXNEIGHBORS];
12216 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12217 for (int n = 0; n < nbNeighbors; n++)
12219 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12220 if (neighborDim == 3)
12222 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12224 DownIdType face(downIds[n], downTypes[n]);
12225 boundaryFaces[face] = vtkId;
12227 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12228 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12229 if (vtkFaceId >= 0)
12231 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12232 // find also the smds edges on this face
12233 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12234 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12235 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12236 for (int i = 0; i < nbEdges; i++)
12238 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12239 if (vtkEdgeId >= 0)
12240 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12244 else if (neighborDim == 2) // skin of the volume
12246 DownIdType face(downIds[n], downTypes[n]);
12247 skinFaces[face] = vtkId;
12248 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12249 if (vtkFaceId >= 0)
12250 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12255 // --- identify the edges constituting the wire of each subshape on the skin
12256 // define polylines with the nodes of edges, equivalent to wires
12257 // project polylines on subshapes, and partition, to get geom faces
12259 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12260 std::set<int> emptySet;
12262 std::set<int> shapeIds;
12264 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12265 while (itelem->more())
12267 const SMDS_MeshElement *elem = itelem->next();
12268 int shapeId = elem->getshapeId();
12269 int vtkId = elem->getVtkId();
12270 if (!shapeIdToVtkIdSet.count(shapeId))
12272 shapeIdToVtkIdSet[shapeId] = emptySet;
12273 shapeIds.insert(shapeId);
12275 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12278 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12279 std::set<DownIdType, DownIdCompare> emptyEdges;
12280 emptyEdges.clear();
12282 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12283 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12285 int shapeId = itShape->first;
12286 MESSAGE(" --- Shape ID --- "<< shapeId);
12287 shapeIdToEdges[shapeId] = emptyEdges;
12289 std::vector<int> nodesEdges;
12291 std::set<int>::iterator its = itShape->second.begin();
12292 for (; its != itShape->second.end(); ++its)
12295 MESSAGE(" " << vtkId);
12296 int neighborsVtkIds[NBMAXNEIGHBORS];
12297 int downIds[NBMAXNEIGHBORS];
12298 unsigned char downTypes[NBMAXNEIGHBORS];
12299 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12300 for (int n = 0; n < nbNeighbors; n++)
12302 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12304 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12305 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12306 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12308 DownIdType edge(downIds[n], downTypes[n]);
12309 if (!shapeIdToEdges[shapeId].count(edge))
12311 shapeIdToEdges[shapeId].insert(edge);
12313 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12314 nodesEdges.push_back(vtkNodeId[0]);
12315 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12316 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12322 std::list<int> order;
12324 if (nodesEdges.size() > 0)
12326 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12327 nodesEdges[0] = -1;
12328 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12329 nodesEdges[1] = -1; // do not reuse this edge
12333 int nodeTofind = order.back(); // try first to push back
12335 for (i = 0; i<nodesEdges.size(); i++)
12336 if (nodesEdges[i] == nodeTofind)
12338 if (i == nodesEdges.size())
12339 found = false; // no follower found on back
12342 if (i%2) // odd ==> use the previous one
12343 if (nodesEdges[i-1] < 0)
12347 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12348 nodesEdges[i-1] = -1;
12350 else // even ==> use the next one
12351 if (nodesEdges[i+1] < 0)
12355 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12356 nodesEdges[i+1] = -1;
12361 // try to push front
12363 nodeTofind = order.front(); // try to push front
12364 for (i = 0; i<nodesEdges.size(); i++)
12365 if (nodesEdges[i] == nodeTofind)
12367 if (i == nodesEdges.size())
12369 found = false; // no predecessor found on front
12372 if (i%2) // odd ==> use the previous one
12373 if (nodesEdges[i-1] < 0)
12377 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12378 nodesEdges[i-1] = -1;
12380 else // even ==> use the next one
12381 if (nodesEdges[i+1] < 0)
12385 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12386 nodesEdges[i+1] = -1;
12392 std::vector<int> nodes;
12393 nodes.push_back(shapeId);
12394 std::list<int>::iterator itl = order.begin();
12395 for (; itl != order.end(); itl++)
12397 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12398 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12400 listOfListOfNodes.push_back(nodes);
12403 // partition geom faces with blocFissure
12404 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12405 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12411 //================================================================================
12413 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12414 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12415 * \return TRUE if operation has been completed successfully, FALSE otherwise
12417 //================================================================================
12419 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12421 // iterates on volume elements and detect all free faces on them
12422 SMESHDS_Mesh* aMesh = GetMeshDS();
12426 ElemFeatures faceType( SMDSAbs_Face );
12427 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12428 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12431 const SMDS_MeshVolume* volume = vIt->next();
12432 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12433 vTool.SetExternalNormal();
12434 const int iQuad = volume->IsQuadratic();
12435 faceType.SetQuad( iQuad );
12436 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12438 if (!vTool.IsFreeFace(iface))
12441 vector<const SMDS_MeshNode *> nodes;
12442 int nbFaceNodes = vTool.NbFaceNodes(iface);
12443 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12445 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12446 nodes.push_back(faceNodes[inode]);
12448 if (iQuad) // add medium nodes
12450 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12451 nodes.push_back(faceNodes[inode]);
12452 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12453 nodes.push_back(faceNodes[8]);
12455 // add new face based on volume nodes
12456 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12458 nbExisted++; // face already exsist
12462 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12467 return ( nbFree == ( nbExisted + nbCreated ));
12472 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12474 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12476 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12479 //================================================================================
12481 * \brief Creates missing boundary elements
12482 * \param elements - elements whose boundary is to be checked
12483 * \param dimension - defines type of boundary elements to create
12484 * \param group - a group to store created boundary elements in
12485 * \param targetMesh - a mesh to store created boundary elements in
12486 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12487 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12488 * boundary elements will be copied into the targetMesh
12489 * \param toAddExistingBondary - if true, not only new but also pre-existing
12490 * boundary elements will be added into the new group
12491 * \param aroundElements - if true, elements will be created on boundary of given
12492 * elements else, on boundary of the whole mesh.
12493 * \return nb of added boundary elements
12495 //================================================================================
12497 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12498 Bnd_Dimension dimension,
12499 SMESH_Group* group/*=0*/,
12500 SMESH_Mesh* targetMesh/*=0*/,
12501 bool toCopyElements/*=false*/,
12502 bool toCopyExistingBoundary/*=false*/,
12503 bool toAddExistingBondary/*= false*/,
12504 bool aroundElements/*= false*/)
12506 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12507 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12508 // hope that all elements are of the same type, do not check them all
12509 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12510 throw SALOME_Exception(LOCALIZED("wrong element type"));
12513 toCopyElements = toCopyExistingBoundary = false;
12515 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12516 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12517 int nbAddedBnd = 0;
12519 // editor adding present bnd elements and optionally holding elements to add to the group
12520 SMESH_MeshEditor* presentEditor;
12521 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12522 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12524 SMESH_MesherHelper helper( *myMesh );
12525 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12526 SMDS_VolumeTool vTool;
12527 TIDSortedElemSet avoidSet;
12528 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12531 typedef vector<const SMDS_MeshNode*> TConnectivity;
12532 TConnectivity tgtNodes;
12533 ElemFeatures elemKind( missType ), elemToCopy;
12535 SMDS_ElemIteratorPtr eIt;
12536 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12537 else eIt = elemSetIterator( elements );
12539 while (eIt->more())
12541 const SMDS_MeshElement* elem = eIt->next();
12542 const int iQuad = elem->IsQuadratic();
12543 elemKind.SetQuad( iQuad );
12545 // ------------------------------------------------------------------------------------
12546 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12547 // ------------------------------------------------------------------------------------
12548 vector<const SMDS_MeshElement*> presentBndElems;
12549 vector<TConnectivity> missingBndElems;
12550 TConnectivity nodes, elemNodes;
12551 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12553 vTool.SetExternalNormal();
12554 const SMDS_MeshElement* otherVol = 0;
12555 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12557 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12558 ( !aroundElements || elements.count( otherVol )))
12560 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12561 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12562 if ( missType == SMDSAbs_Edge ) // boundary edges
12564 nodes.resize( 2+iQuad );
12565 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12567 for ( int j = 0; j < nodes.size(); ++j )
12568 nodes[j] = nn[ i+j ];
12569 if ( const SMDS_MeshElement* edge =
12570 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12571 presentBndElems.push_back( edge );
12573 missingBndElems.push_back( nodes );
12576 else // boundary face
12579 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12580 nodes.push_back( nn[inode] ); // add corner nodes
12582 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12583 nodes.push_back( nn[inode] ); // add medium nodes
12584 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12586 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12588 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12589 SMDSAbs_Face, /*noMedium=*/false ))
12590 presentBndElems.push_back( f );
12592 missingBndElems.push_back( nodes );
12594 if ( targetMesh != myMesh )
12596 // add 1D elements on face boundary to be added to a new mesh
12597 const SMDS_MeshElement* edge;
12598 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12601 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12603 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12604 if ( edge && avoidSet.insert( edge ).second )
12605 presentBndElems.push_back( edge );
12611 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12613 avoidSet.clear(), avoidSet.insert( elem );
12614 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12615 SMDS_MeshElement::iterator() );
12616 elemNodes.push_back( elemNodes[0] );
12617 nodes.resize( 2 + iQuad );
12618 const int nbLinks = elem->NbCornerNodes();
12619 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12621 nodes[0] = elemNodes[iN];
12622 nodes[1] = elemNodes[iN+1+iQuad];
12623 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12624 continue; // not free link
12626 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12627 if ( const SMDS_MeshElement* edge =
12628 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12629 presentBndElems.push_back( edge );
12631 missingBndElems.push_back( nodes );
12635 // ---------------------------------
12636 // 2. Add missing boundary elements
12637 // ---------------------------------
12638 if ( targetMesh != myMesh )
12639 // instead of making a map of nodes in this mesh and targetMesh,
12640 // we create nodes with same IDs.
12641 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12643 TConnectivity& srcNodes = missingBndElems[i];
12644 tgtNodes.resize( srcNodes.size() );
12645 for ( inode = 0; inode < srcNodes.size(); ++inode )
12646 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12647 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12649 /*noMedium=*/false))
12651 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12655 for ( int i = 0; i < missingBndElems.size(); ++i )
12657 TConnectivity& nodes = missingBndElems[i];
12658 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12660 /*noMedium=*/false))
12662 SMDS_MeshElement* newElem =
12663 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12664 nbAddedBnd += bool( newElem );
12666 // try to set a new element to a shape
12667 if ( myMesh->HasShapeToMesh() )
12670 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12671 const size_t nbN = nodes.size() / (iQuad+1 );
12672 for ( inode = 0; inode < nbN && ok; ++inode )
12674 pair<int, TopAbs_ShapeEnum> i_stype =
12675 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12676 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12677 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12679 if ( ok && mediumShapes.size() > 1 )
12681 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12682 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12683 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12685 if (( ok = ( stype_i->first != stype_i_0.first )))
12686 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12687 aMesh->IndexToShape( stype_i_0.second ));
12690 if ( ok && mediumShapes.begin()->first == missShapeType )
12691 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12695 // ----------------------------------
12696 // 3. Copy present boundary elements
12697 // ----------------------------------
12698 if ( toCopyExistingBoundary )
12699 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12701 const SMDS_MeshElement* e = presentBndElems[i];
12702 tgtNodes.resize( e->NbNodes() );
12703 for ( inode = 0; inode < nodes.size(); ++inode )
12704 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12705 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12707 else // store present elements to add them to a group
12708 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12710 presentEditor->myLastCreatedElems.Append( presentBndElems[i] );
12713 } // loop on given elements
12715 // ---------------------------------------------
12716 // 4. Fill group with boundary elements
12717 // ---------------------------------------------
12720 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12721 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12722 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12724 tgtEditor.myLastCreatedElems.Clear();
12725 tgtEditor2.myLastCreatedElems.Clear();
12727 // -----------------------
12728 // 5. Copy given elements
12729 // -----------------------
12730 if ( toCopyElements && targetMesh != myMesh )
12732 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12733 else eIt = elemSetIterator( elements );
12734 while (eIt->more())
12736 const SMDS_MeshElement* elem = eIt->next();
12737 tgtNodes.resize( elem->NbNodes() );
12738 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12739 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12740 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12742 tgtEditor.myLastCreatedElems.Clear();
12748 //================================================================================
12750 * \brief Copy node position and set \a to node on the same geometry
12752 //================================================================================
12754 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12755 const SMDS_MeshNode* to )
12757 if ( !from || !to ) return;
12759 SMDS_PositionPtr pos = from->GetPosition();
12760 if ( !pos || from->getshapeId() < 1 ) return;
12762 switch ( pos->GetTypeOfPosition() )
12764 case SMDS_TOP_3DSPACE: break;
12766 case SMDS_TOP_FACE:
12768 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12769 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12770 fPos->GetUParameter(), fPos->GetVParameter() );
12773 case SMDS_TOP_EDGE:
12775 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12776 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12777 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12780 case SMDS_TOP_VERTEX:
12782 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12785 case SMDS_TOP_UNSPEC: